1 /****************************************************************************
2 serial.c file for SERIAL DRIVER
3 Each serial terminal is represented by a stty struct consisting of
4 INPUT section = a circular buffer with incount chars
5 OUTPUT section = a circular buffer with outcount chars
6 Process and interrupt handler synchronize by sleep()/wakeup()
7 ***************************************************************************/
9 /**************** CONSTANTS ***********************/
11 #define NSTTY 2 /* number of serial ports */
13 /* offset from serial ports base */
14 #define DATA 0 /* Data reg for Rx, Tx */
15 #define DIVL 0 /* When used as divisor */
16 #define DIVH 1 /* to generate baud rate */
17 #define IER 1 /* Interrupt Enable reg */
18 #define IIR 2 /* Interrupt ID rer */
19 #define LCR 3 /* Line Control reg */
20 #define MCR 4 /* Modem Control reg */
21 #define LSR 5 /* Line Status reg */
22 #define MSR 6 /* Modem Status reg */
24 /**** The serial terminal data structure ****/
27 /* I/O port base address */
32 int inhead
, intail
, incount
;
36 int outhead
, outtail
, outcount
;
38 // coontrol section: (erase, kill, interrupt keys, etc)
43 STTY stty
[NSTTY
]; // actual stty data structure; one per terminal
45 /************ serial ports initialization ***************/
47 char *message
[2] = { "Serial Port 0 Ready\n\r\007", "Serial Port 1 Ready\n\r\007"};
49 int sinit() // init() function
54 /* initialize stty[] and serial port control registers */
56 for (i
= 0; i
< NSTTY
; i
++){
59 /* initialize data structures and pointers */
61 t
->port
= 0x3F8; /* COM1 base address */
63 t
->port
= 0x2F8; /* COM2 base address */
65 printf("sinit : port %x\n", t
->port
);
66 t
->inhead
= t
->intail
= t
->incount
= 0;
67 t
->outhead
= t
->outtail
= t
->outcount
= 0;
69 lock(); // CLI; no interrupts
70 out_byte(t
->port
+IER
, 0x00); /* disable serial port interrupts */
72 out_byte(t
->port
+LCR
, 0x80); /* ready to use 3f9,3f8 as divisor */
73 out_byte(t
->port
+DIVH
, 0x00);
74 out_byte(t
->port
+DIVL
, 12); /* divisor = 12 ===> 9600 bauds */
76 /******** term 9600 /dev/ttyS0: 8 bits/char, no parity *************/
77 out_byte(t
->port
+LCR
, 0x03);
79 /*******************************************************************
80 Writing to 3fc ModemControl tells modem : DTR, then RTS ==>
81 let modem respond as a DCE. Here we must let the (crossed)
82 cable tell the TVI terminal that the "DCE" has DSR and CTS.
83 So we turn the port's DTR and RTS on.
84 ********************************************************************/
86 out_byte(t
->port
+MCR
, 0x0B); /* 1011 ==> INTERRUPT, RTS, DTR on */
87 out_byte(t
->port
+IER
, 0x01); /* Enable Rx interrupt, Tx off */
91 enable_irq(4-i
); // COM1: IRQ4; COM2: IRQ3
93 /* show greeting message */
94 sputline(i
, message
[i
]);
98 //==================== LOWER HALF ROUTINES (Interrupt handlers) =======================
109 int shandler(port
) int port
;
112 int IntID
, LineStatus
, ModemStatus
, intType
, c
;
114 t
= &stty
[port
]; /* IRQ 4 interrupt : COM1 = stty[0] */
116 IntID
= in_byte(t
->port
+IIR
); /* read InterruptID Reg */
117 LineStatus
= in_byte(t
->port
+LSR
); /* read LineStatus Reg */
118 ModemStatus
=in_byte(t
->port
+MSR
); /* read ModemStatus Reg */
120 intType
= IntID
& 7; /* mask out all except the lowest 3 bits */
122 case 6 : do_errors(t
); break; /* 110 = errors */
123 case 4 : do_rx(t
); break; /* 100 = rx interrupt */
124 case 2 : do_tx(t
); break; /* 010 = tx interrupt */
125 case 0 : do_modem(t
); break; /* 000 = modem interrupt */
127 out_byte(0x20, 0x20); /* reenable the 8259 controller */
130 int do_errors() { printf("under construction\n"); }
132 int do_modem() { printf("don't have a modem\n"); }
134 int do_rx(t
) STTY
*t
; /* rx interrupt */
138 c
= in_byte(t
->port
) & 0x7F; /* read the ASCII char from port */
140 printf("\n%x rx_interrupt : c=%c", t
->port
, c
);
142 if (t
->incount
>= BUFLEN
){ // if input buf is full, discard
143 out_byte(t
->port
, 007); // but sound warning beep
147 t
->inbuf
[t
->inhead
++] = c
; /* put char into circular inbuf[] */
148 t
->inhead
%= BUFLEN
; /* advance inhead */
151 wakeup(&t
->incount
); // wakeup process that may be sleeping
154 int do_tx(t
) STTY
*t
; // tx_interrupt
158 if (t
->outcount
==0){ // nothing to do
159 out_byte(t
->port
+IER
, 0x01);
162 c
= t
->outbuf
[t
->outtail
++];
163 t
->outtail
%= BUFLEN
;
166 out_byte(t
->port
, c
);
168 wakeup(&t
->outcount
);
171 /**************** Upper half routines CALLED BY process **************:
172 sgetc()/sputs() which interact with Interrupt handlers
173 **********************************************************************/
175 int sgetc(port
) int port
;
182 while (t
->incount
==0)
185 lock(); /* disable interrupts */
186 c
= t
->inbuf
[t
->intail
++];
193 int sputc(port
, c
) int port
, c
;
195 STTY
*t
= &stty
[port
];
197 while(t
->outcount
==BUFLEN
)
202 out_byte(t
->port
, c
);
203 out_byte(t
->port
+IER
, 0x03);
206 t
->outbuf
[t
->outhead
++] = c
;
207 t
->outhead
%= BUFLEN
;
212 /********************* END OF SERIAL DRIVER *************************************/
215 /***** line mode functions sgetline()/sputline() based on sgetc()/sputc() ************/
217 int sgetline(port
, line
) int port
; char *line
;
219 while ( (*line
= sgetc(port
)) != '\r'){
222 *(line
) = 0; /* change \r to null char */
226 int sputline(port
, line
) int port
; char *line
;
234 /******* User Interface fucntions to serial ports ************/
240 printf("enter port # (0|1):");
242 printf("port=%d\n", port
);
244 sputline(port
, "enter a line from serial terminal : ");
245 sgetline(port
, line
);
246 printf("\nline=%s\n", line
);
247 sputline(port
, line
);
248 sputline(port
,"\n\r");
256 printf("enter port # (0|1):");
259 printf("enter a line : ");
261 strcat(line
, "\n\r");
262 printf("to serial port %d: line=%s\n", port
, line
);
264 sputline(port
, line
);