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 |