460 Notes on Serial Driver
********************** LAB Assignment #7 *********************************** DUE : NOV 5, 2009 // P,V on semaphores for process synchronization // lock()/restore() are defined in assembly file samples/SERIAL/ts.s struct semaphore{ int value; PROC *queue; }; int P(s) struct semaphore *s; { PROC *p; lock(); s->value--; if (s->value < 0){ running->status=BLOCK; ENTER running INTO s->queue; tswitch(); } restore(); } int V(s) struct semaphore *s; { PROC *p; lock(); s->value++; if (s->value <= 0){ p = dequeue FIRST WAITER FROM s-queue; p->status = READY; ENTER p INTO readyQueue; } restore(); } /**************** CONSTANTS ***********************/ #define INT_CTL 0x20 #define ENABLE 0x20 #define NULLCHAR 0 #define BEEP 7 #define BACKSPACE 8 #define ESC 27 #define SPACE 32 #define PLUS 43 #define INBUFLEN 80 #define OUTBUFLEN 80 #define EBUFLEN 10 #define LSIZE 64 #define NR_STTY 2 /* number of serial ports */ /* offset from serial ports base */ #define DATA 0 /* Data reg for Rx, Tx */ #define DIVL 0 /* When used as divisor */ #define DIVH 1 /* to generate baud rate */ #define IER 1 /* Interrupt Enable reg */ #define IIR 2 /* Interrupt ID rer */ #define LCR 3 /* Line Control reg */ #define MCR 4 /* Modem Control reg */ #define LSR 5 /* Line Status reg */ #define MSR 6 /* Modem Status reg */ /**** The serial terminal data structure ****/ struct stty { /* input buffer */ char inbuf[INBUFLEN]; int inhead, intail; struct semaphore inchars; /* output buffer */ char outbuf[OUTBUFLEN]; int outhead, outtail; struct semaphore outspace; char kline[LSIZE]; int tx_on; /* echo buffer */ char ebuf[EBUFLEN]; int ehead, etail, e_count; /* Control section */ char echo; /* echo inputs */ char ison; /* on or off */ char erase, kill, intr, quit, x_on, x_off, eof; /* I/O port base address */ int port; } stty[NR_STTY]; /******** bgetc()/bputc() by polling *********/ int bputc(port, c) int port, c; { while ((in_byte(port+LSR) & 0x20) == 0); out_byte(port+DATA, c); } int bgetc(port) int port; { while ((in_byte(port+LSR) & 0x01) == 0); return (in_byte(port+DATA) & 0x7F); } int enable_irq(irq_nr) unsigned irq_nr; { out_byte(0x21, in_byte(0x21) & ~(1 << irq_nr)); } /************ serial ports initialization ***************/ char *p = "\n\rSerial Port Ready\n\r\007"; int sinit() { int i; struct stty *t; char *q; /* initialize stty[] and serial ports */ for (i = 0; i < NR_STTY; i++){ q = p; prints("sinit:"); printi(i); t = &stty[i]; /* initialize data structures and pointers */ if (i==0) t->port = 0x3F8; /* COM1 base address */ else t->port = 0x2F8; /* COM2 base address */ t->inchars.value = t->inlines.value = 0; t->inlines.queue = t->inchars.queue = 0; t->mutex.value = 1; t->mutex.queue = 0; t->outspace.value = OUTBUFLEN; t->outspace.queue = 0; t->inhead = t->intail = 0; t->ehead = t->etail = t->e_count = 0; t->outhead =t->outtail = t->tx_on = 0; // initialize control chars; NOT used in MTX but show how anyway t->ison = t->echo = 1; /* is on and echoing */ t->erase = '\b'; t->kill = '@'; t->intr = (char)0177; /* del */ t->quit = (char)034; /* control-C */ t->x_on = (char)021; /* control-Q */ t->x_off = (char)023; /* control-S */ t->eof = (char)004; /* control-D */ lock(); // CLI; no interrupts out_byte(t->port+MCR, 0x09); /* IRQ4 on, DTR on */ out_byte(t->port+IER, 0x00); /* disable serial port interrupts */ out_byte(t->port+LCR, 0x80); /* ready to use 3f9,3f8 as divisor */ out_byte(t->port+DIVH, 0x00); out_byte(t->port+DIVL, 12); /* divisor = 12 ===> 9600 bauds */ /******** term 9600 /dev/ttyS0: 8 bits/char, no parity *************/ out_byte(t->port+LCR, 0x03); /******************************************************************* Writing to 3fc ModemControl tells modem : DTR, then RTS ==> let modem respond as a DCE. Here we must let the (crossed) cable tell the TVI terminal that the "DCE" has DSR and CTS. So we turn the port's DTR and RTS on. ********************************************************************/ out_byte(t->port+MCR, 0x0B); /* 1011 ==> IRQ4, RTS, DTR on */ out_byte(t->port+IER, 0x01); /* Enable Rx interrupt, Tx off */ unlock(); enable_irq(4-i); // COM1: IRQ4; COM2: IRQ3 /* show greeting message */ prints(q); while (*q){ bputc(t->port, *q); q++; } } } //======================== LOWER HALF ROUTINES =============================== int shandler(port) int port; { struct stty *t; int IntID, LineStatus, ModemStatus, intType, c; t = &stty[port]; /* IRQ 4 interrupt : COM1 = stty[0] */ IntID = in_byte(t->port+IIR); /* read InterruptID Reg */ LineStatus= in_byte(t->port+LSR); /* read LineStatus Reg */ ModemStatus=in_byte(t->port+MSR); /* read ModemStatus Reg */ intType = IntID & 7; /* mask out all except the lowest 3 bits */ switch(intType){ case 6 : do_errors(t); break; /* 110 = errors */ case 4 : do_rx(t); break; /* 100 = rx interrupt */ case 2 : do_tx(t); break; /* 010 = tx interrupt */ case 0 : do_modem(t); break; /* 000 = modem interrupt */ } out_byte(INT_CTL, ENABLE); /* reenable the 8259 controller */ } int do_errors() { printf("ignore error\n"); } int do_modem() { printf("don't have a modem\n"); } /* The following show how to enable and disable Tx interrupts */ en_tx(t) struct stty *t; { out_byte(t->port+IER, 0x03); /* 0011 ==> both tx and rx on */ t->tx_on = 1; } disable_tx(t) struct stty *t; { out_byte(t->port+IER, 0x01); /* 0001 ==> tx off, rx on */ t->tx_on = 0; } /******** echo char to serial port **********/ int secho(t,c) struct stty *t; int c; { /* insert c into ebuf[]; turn on tx interrupt */ } int do_rx(t) struct stty *t; { int c; c = in_byte(tty->port) & 0x7F; /* read the char from port */ printf("\nrx interrupt c="); putc(c); // COMPLETE with YOUR C code } /*********************************************************************/ int do_tx(t) struct stty *t; { int c; // if ebuf AND outbuf are both empty ==> // turn off tx interrupt; return if (ebuf not empty) { // out a char from ebuf; return; } if (outbuf not empty){ // out a char from outbuf; // WAKEUP anyone blocked on outbuf room } } int sgetc(port) int port; // port = 0 or 1 { // BLOCK if no input char yet lock(); get a char c from inbuf unlock(); return(c); } int sputc(c, port) int c; int port { // BLOCK if outbuf has no room lock(); enter c into outbuf; unlock(); enable tx interrupt if it is off; } int sgetline(line, port) char *line; int port { // listen to lecture } int sputline(line, port) char *line; int port { // listen to lecture } int usgets(y, port) char *y; int port; { // get a line and write line to y in U space } int uputs(y, port) char *y; int port; { write line y in U space to serail port }