Subversion Repositories shark

Compare Revisions

Ignore whitespace Rev 713 → Rev 714

/shark/trunk/drivers/serial/scom.c
18,25 → 18,6
* http://shark.sssup.it
*/
 
/**
------------
CVS : $Id: scom.c,v 1.1 2004-03-29 18:31:42 mauro Exp $
 
File: $File$
Revision: $Revision: 1.1 $
Last update: $Date: 2004-03-29 18:31:42 $
------------
 
Author: Massimiliano Giorgi
 
Author: Gerardo Lamastra
Date: 9/5/96
 
File: SCOM.C
Revision: 1.0g
 
**/
 
/*
* Copyright (C) 2000 Paolo Gai
*
70,42 → 51,15
* mouse better (see below)
*/
 
//#include <string.h>
//#include <stdlib.h>
//#include <cons.h>
 
#include <kernel/kern.h>
//#include "exc.h"
 
#include <modules/sem.h>
#include <drivers/scom.h>
 
 
/* #define __DEBUG_SERIAL__ */
/* #define __STUB__ */
 
/* Base address for each standard COM link */
static unsigned com_base[] = {0x03F8,0x02F8,0x03E8,0x02E8};
/* Irq linked to each serial channel */
static unsigned com_irq[] = {COM1_IRQ,COM2_IRQ,COM3_IRQ,COM4_IRQ};
/* COM port which shares interrupt with indexed one */
/* I assume standard AT assignement where each irq line can */
/* hold up to 2 different COM port */
static const int com_share[] = {COM3,COM4,COM1,COM2};
 
/* Used for decoding the IIR status */
const int IIRbits[] = {MS_CHANGED,TX_EMPTY,RX_FULL,LS_CHANGED};
 
/* The descriptor of a serial link */
/* Each array entry is associated to a COM port */
/* The control field is used to specify which kind of interrupts */
/* are going to be served; the status field tracks if the entry */
/* is curretly used & if the shared fast handler is linked */
/* The semaphores are opened if you use an asyncronous server */
/* with the link */
 
struct COM_LINK com_link[COM_LINKS];
 
/* Register level access functions */
 
unsigned com_read(unsigned port,unsigned reg)
136,96 → 90,15
return(com_read(port,RBR));
}
 
/* Fast routines for cascaded irq */
 
/* By massy */
/* com_fast must be called if there isn't a server
* and an interrupt is detected (so sermouse.c module can
* activate mouse task only if a packet not a byte is received).
*/
static void dummy(int x) {}
void (*com_fast)(int)=dummy;
 
#ifdef __DEBUG_SERIAL__
int fast_times1 = 0,fast_times3 = 0;
#endif
 
static void com_1_3_fast(int n)
{
unsigned iir1,iir3;
iir1 = DECODE(com_read(COM1,IIR));
iir3 = DECODE(com_read(COM3,IIR));
if ((iir1 & com_link[COM1].control)) {
#ifdef __DEBUG_SERIAL__
fast_times1++;
#endif
com_write(COM1,IER,0);
com_link[COM1].request = iir1;
if (com_link[COM1].server==NIL)
com_fast(COM1);
else
task_activate(com_link[COM1].server);
}
else if ((iir3 & com_link[COM3].control)) {
#ifdef __DEBUG_SERIAL__
fast_times3++;
#endif
com_write(COM3,IER,0);
com_link[COM3].request = iir3;
if (com_link[COM3].server==NIL)
com_fast(COM3);
else
task_activate(com_link[COM3].server);
}
}
 
#ifdef __DEBUG_SERIAL__
int fast_times2 = 0,fast_times4 = 0;
#endif
 
static void com_2_4_fast(int n)
{
unsigned iir2,iir4;
iir2 = DECODE(com_read(COM2,IIR));
iir4 = DECODE(com_read(COM4,IIR));
if ((iir2 & com_link[COM2].control)) {
#ifdef __DEBUG_SERIAL__
fast_times2++;
#endif
com_write(COM2,IER,0);
com_link[COM2].request = iir2;
if (com_link[COM2].server==NIL)
com_fast(COM2);
else
task_activate(com_link[COM2].server);
}
else if ((iir4 & com_link[COM4].control)) {
#ifdef __DEBUG_SERIAL__
fast_times4++;
#endif
com_write(COM4,IER,0);
com_link[COM4].request = iir4;
if (com_link[COM4].server==NIL)
com_fast(COM4);
else
task_activate(com_link[COM4].server);
}
}
 
/* Initialize a serial channel */
 
int com_open(unsigned port,DWORD speed,BYTE parity,BYTE len,BYTE stop)
{
unsigned long div,b_mask;
/* Check if link is already open */
cli();
if (com_link[port].status & LINK_BUSY) {
sti();
return(-1);
} else com_link[port].status |= LINK_BUSY;
sti();
SYS_FLAGS f;
 
f = kern_fsave();
 
/* Now set up the serial link */
b_mask = (parity & 3) * 8 + (stop & 1) * 4 + ((len - 5) & 3);
div = 115200L / speed;
243,42 → 116,11
com_write(port,LCR,b_mask);
/* Attiva OUT1 & OUT2 */
com_write(port,MCR,0x0C);
return(1);
}
 
/* Link a particular server to a serial channel */
/* The semaphores are opened to syncronize the server and the */
/* application task which use serial communication */
kern_frestore(f);
 
int com_server(unsigned port,unsigned control,PID server)
{
unsigned hndl = com_irq[port];
unsigned shared = com_share[port];
void (*com_fast)(int n);
/* Select appropriate fast routine */
if (port == COM1 || port == COM3) com_fast = com_1_3_fast;
else if (port == COM2 || port == COM4) com_fast = com_2_4_fast;
else return(-1);
if ((control & (RX_FULL|TX_EMPTY)) == 0) return(-1);
/* If the fast routine is not already installed, install it! */
cli();
if (!(com_link[port].status & FAST_INSTALLED)) {
bit_on(com_link[port].status,FAST_INSTALLED);
bit_on(com_link[shared].status,FAST_INSTALLED);
handler_set(hndl,com_fast,NIL, TRUE);
#ifdef __DEBUG_SERIAL__
cputs("Handler OK\n");
#endif
}
sti();
/* Set com link tasks & flags */
com_link[port].control = control;
com_link[port].server = server;
com_link[port].msk = 0;
if (control & RX_FULL) sem_init(&com_link[port].rx_sem,0,0);
if (control & TX_EMPTY) sem_init(&com_link[port].tx_sem,0,0);
sem_init(&com_link[port].mutex,0,1);
return(1);
return(0);
 
}
 
/* Close port channel & release the server */
285,208 → 127,16
 
int com_close(unsigned port)
{
unsigned hndl = com_irq[port];
unsigned shared = com_share[port];
SYS_FLAGS f;
 
/* Check if fast is already installed */
cli();
if (!(com_link[port].status & LINK_BUSY)) {
sti();
return(-1);
} else {
if (com_link[port].control & RX_FULL) sem_destroy(&com_link[port].rx_sem);
if (com_link[port].control & TX_EMPTY) sem_destroy(&com_link[port].tx_sem);
com_write(port,IER,0);
com_read(port,IIR);
com_read(port,RBR);
com_link[port].status = 0;
com_link[port].control = 0;
com_link[port].msk = 0;
sti();
sem_destroy(&com_link[port].mutex);
if (com_link[port].server != NIL) {
task_kill(com_link[port].server);
com_link[port].server = NIL;
}
}
/* If the fast routine is no more necessary, remove it */
if (!(com_link[shared].status & FAST_INSTALLED))
handler_remove(hndl);
/* If the other link still uses it, we must remember this */
else com_link[port].status = FAST_INSTALLED;
return(1);
}
f = kern_fsave();
 
#ifdef __DEBUG_SERIAL__
int rx_time = 0;
int tx_time = 0;
#endif
 
/* This is the full duplex server; used for bidirectional data */
/* transmission. */
/* As intr is masked, the server can only be activated once */
/* and operates in mutex with the fast handler */
/* This server operates in conjunction with the com_Async... */
/* procedures! */
 
TASK duplexServer(int port)
{
char data;
for(;;) {
if (com_link[port].request & RX_FULL) {
#ifdef __DEBUG_SERIAL__
putc_xy(78,0,RED,'R');
rx_time++;
#endif
data = com_read(port,RBR);
*(com_link[port].rx_buf + com_link[port].rx_cnt) = data;
com_link[port].rx_cnt++;
if (com_link[port].rx_cnt == com_link[port].rx_len) {
bit_off(com_link[port].msk,RX_FULL);
sem_post(&com_link[port].rx_sem);
}
}
if (com_link[port].request & TX_EMPTY) {
#ifdef __DEBUG_SERIAL__
putc_xy(79,0,GREEN,'T');
tx_time++;
#endif
data = *(com_link[port].tx_buf + com_link[port].tx_cnt);
com_link[port].tx_cnt++;
com_write(port,THR,data);
if (com_link[port].tx_cnt == com_link[port].tx_len) {
bit_off(com_link[port].msk,TX_EMPTY);
sem_post(&com_link[port].tx_sem);
}
}
cli();
com_write(port,IER,com_link[port].msk);
task_endcycle();
sti();
}
}
 
/* This routines provides asyncronous decoupling between the server */
/* and the tasks which produce/consume serial data */
 
void com_AsyncSend(int port,void *buf,unsigned len)
{
sem_wait(&com_link[port].mutex);
com_link[port].tx_buf = buf;
com_link[port].tx_cnt = 0;
com_link[port].tx_len = len;
bit_on(com_link[port].msk,TX_EMPTY);
sem_post(&com_link[port].mutex);
com_write(port,IER,com_link[port].msk);
sem_wait(&com_link[port].tx_sem);
}
 
void com_AsyncReceive(int port,void *buf,unsigned len)
{
sem_wait(&com_link[port].mutex);
com_link[port].rx_buf = buf;
com_link[port].rx_cnt = 0;
com_link[port].rx_len = len;
bit_on(com_link[port].msk,RX_FULL);
sem_post(&com_link[port].mutex);
com_write(port,IER,com_link[port].msk);
sem_wait(&com_link[port].rx_sem);
}
 
/* Receive Only Server */
/* This server is used for passive devices which cannot receive */
/* data, but only transmit. I assume that only one byte is pro- */
/* cessed each time and the byte is got to the control process */
 
TASK rxServer(int port)
{
static char data;
com_link[port].rx_buf = &data;
for(;;) {
#ifdef __DEBUG_SERIAL__
putc_xy(76,0,YELLOW,'R');
#endif
data = com_read(port,RBR);
sem_post(&com_link[port].rx_sem);
cli();
com_write(port,IER,com_link[port].msk);
task_endcycle();
sti();
}
}
 
/* Debug Stub */
 
#ifdef __STUB__
 
#include "keyb.h"
 
TASK Visualize(void)
{
char str[40];
char buf[80];
unsigned long times = 0;
 
for (;;) {
com_AsyncReceive(COM2,str,10);
sprintf(buf,"Str : %s (Times : %lu)",str,times++);
puts_xy(0,1,WHITE,buf);
}
}
 
TASK Sender(void)
{
char buf[80];
unsigned long times = 0;
cputs("Sender has started...\n");
for (;;) {
buf[0] = keyb_getchar();
if (buf[0] == 'x') task_activate(MAIN_INDEX);
if ((times % 10) == 9) buf[0] = 0;
com_AsyncSend(COM2,buf,1);
sprintf(buf,"Sender (Times : %lu)",times++);
puts_xy(0,3,WHITE,buf);
}
} 0
 
void main()
{
PID p1,p2,p3;
BYTE m1 = TX_EMPTY|RX_FULL;
BYTE m2 = RX_FULL;
BYTE m3 = TX_EMPTY;
MODEL m = BASE_MODEL;
MODEL mm = BASE_MODEL;
com_write(port,IER,0);
com_read(port,IIR);
com_read(port,RBR);
sys_init(1000,uSec,0);
keyb_init(HARD,50);
clear();
/* This is the sequence of operations needed to setup */
/* a serial asyncronous bidirectional link */
task_def_arg(m,COM2);
task_def_wcet(m,500);
p1 = task_create("ComDuplex",duplexServer,HARD,APERIODIC,100,&m);
if (p1 == -1) {
ll_printf("Error creating comduplex task\n");
sys_end();
exit(-1);
}
com_open(COM2,9600,NONE,8,1);
com_server(COM2,m1,p1);
kern_frestore(f);
 
/* Well, that is some other stuff ... */
task_def_wcet(mm,500);
p2 = task_create("Visor",Visualize,NRT,APERIODIC,11,&mm);
p3 = task_create("Sender",Sender,NRT,APERIODIC,11,&mm);
task_activate(p2);
task_activate(p3);
task_endcycle();
sys_end();
#ifdef __DEBUG_SERIAL__
cprintf("RxServer was activated %d times\n",rx_time);
cprintf("TxServer was activated %d times\n",tx_time);
cprintf("Fast : %d\n",fast_times1);
#endif
return(0);
}
 
#endif