Rev 714 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* Project: S.Ha.R.K.
*
* Coordinators:
* Giorgio Buttazzo <giorgio@sssup.it>
* Paolo Gai <pj@gandalf.sssup.it>
*
* Authors :
* Paolo Gai <pj@gandalf.sssup.it>
* Massimiliano Giorgi <massy@gandalf.sssup.it>
* Luca Abeni <luca@gandalf.sssup.it>
* (see the web pages for full authors list)
*
* ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy)
*
* http://www.sssup.it
* http://retis.sssup.it
* 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
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/* Serial communication device */
/* This implementation is capable of handling 4 distinct COM ports */
/* The COM port settings are the standard PC settings: */
/* PORT ADDRESS IRQ */
/* 1 0x3F8 4 */
/* 2 0x2F8 3 */
/* 3 0x3E8 4 */
/* 4 0x2E8 2 */
/* By Massy
* I have modified the fast handler routines to support serial
* 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
)
{
unsigned b
;
if (port
> 3 || reg
> 7) return(0);
b
= ll_in
(com_base
[port
]+reg
);
return(b
);
}
void com_write
(unsigned port
,unsigned reg
,unsigned value
)
{
if (port
> 3 || reg
> 7) return;
ll_out
(com_base
[port
]+reg
,value
);
}
/* Polled send/receive */
void com_send
(unsigned port
,BYTE b
)
{
while ((com_read
(port
,LSR
) & 32) == 0);
com_write
(port
,THR
,b
);
}
unsigned com_receive
(unsigned port
)
{
while ((com_read
(port
,LSR
) & 1) == 0);
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
();
/* Now set up the serial link */
b_mask
= (parity
& 3) * 8 + (stop
& 1) * 4 + ((len
- 5) & 3);
div = 115200L / speed
;
/* Clear serial interrupt enable register */
com_write
(port
,IER
,0);
/* Empty input buffer */
com_read
(port
,RBR
);
/* Activate DLAB bit for speed setting */
com_write
(port
,LCR
,0x80);
/* Load baud divisor */
com_write
(port
,0,div & 0x00FF);
div >>= 8;
com_write
(port
,1,div & 0x00FF);
/* Load control word (parity,stop bit,bit len) */
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 */
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);
}
/* Close port channel & release the server */
int com_close
(unsigned port
)
{
unsigned hndl
= com_irq
[port
];
unsigned shared
= com_share
[port
];
/* 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);
}
#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
;
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
);
/* 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
}
#endif