diff --git a/kernel/serial.c b/kernel/serial.c index feec21e..021eb94 100644 --- a/kernel/serial.c +++ b/kernel/serial.c @@ -16,6 +16,8 @@ See the file LICENSE for details. #include "interrupt.h" #include "string.h" #include "device.h" +#include "event.h" +#include "event_queue.h" #define COM1 0x3f8 #define COM2 0x2F8 @@ -24,7 +26,8 @@ See the file LICENSE for details. #define SERIAL_DATA 0 // If DLAB disabled in LCR -#define SERIAL_IRQ_ENABLE 1 // If DLAB disabled in LCR +#define SERIAL_IER 1 + #define SERIAL_IRQ_DATA_AVAILABILE (0x01 << 0) #define SERIAL_IRQ_TRASMITTER_EMPTY (0x01 << 1) #define SERIAL_IRQ_ERROR (0x01 << 2) @@ -64,21 +67,32 @@ See the file LICENSE for details. #define SERIAL_SCRATCH 7 // units 0,1,2,3 -static const int serial_ports[4] = { COM1, COM2, COM3, COM4 }; -static const int serial_ports_irq[4] = { 4, 3, 4, 3 }; +static const int serial_ports[2] = { COM1, COM2 }; +static const int serial_ports_irq[2] = { 4, 3 }; +static struct event_queue *queue[2]; -static void serial_interrupt(int i, int intr_code) +static void serial_interrupt(int intr, int intr_code) { - printf("serial_interrupt %d %d\n",i,intr_code); + intr=intr-32; // reduce by IRQ table offset; IRQ is not unique! We may only support 2 COMs! + int port_no=intr==4?0:1, + port = serial_ports[port_no]; + while(inb(port + SERIAL_LSR) & SERIAL_DATA_AVAILABLE) { + char ch=inb(port); + struct event e; + e.type = EVENT_DATA; + e.code = ch; + event_queue_post(queue[port_no],&e); + } } - - -static void serial_init_port(int unit) +static int serial_init_port(int unit) { int port = serial_ports[unit]; + + // probe for hardware + if(inb(port+SERIAL_LSR)==0xff) return 0; //Disable interrupts - outb(0x00, port + SERIAL_IRQ_ENABLE); + outb(0x00, port + SERIAL_IER); //Enable DLAB(set baud rate divisor) outb(SERIAL_DLAB_ENABLE, port + SERIAL_LCR); //Set divisor to 3(lo byte) 38400 baud @@ -87,18 +101,24 @@ static void serial_init_port(int unit) outb(0x00, port + SERIAL_DIVISOR_HI); //8 bits, no parity, one stop bit outb(SERIAL_CHARLEN_START * 3, port + SERIAL_LCR); +#ifdef SERIALFIFO //Enable FIFO, clear them, with 14 - byte threshold outb(SERIAL_FIFO_ENABLE | SERIAL_FIFO_CLEAR_RECEIVER | SERIAL_FIFO_CLEAR_TRANSMITTER | SERIAL_TRIGGER_LEVEL0 , port + SERIAL_FCR); +#else + outb(0 , port + SERIAL_FCR); +#endif //IRQs enabled, RTS / DSR set outb(SERIAL_DATA_TERMINAL_READY | SERIAL_REQUEST_TO_SEND | SERIAL_AUX_OUT2, port + SERIAL_MCR); - // setup interrupt support - interrupt_register(serial_ports_irq[unit], serial_interrupt); - interrupt_enable(serial_ports_irq[unit]); + // setup interrupt support (need to add 32 to IRQ number!!!!) + interrupt_register(32+serial_ports_irq[unit], serial_interrupt); + interrupt_enable(32+serial_ports_irq[unit]); // + received data available - outb(0x01, port + SERIAL_IRQ_ENABLE); + outb(SERIAL_IRQ_DATA_AVAILABILE, port + SERIAL_IER); - printf("[COM%d] serial_init_port %x: ready.\n",unit,port); + queue[unit]=event_queue_create(); + printf("[COM%d] serial_init_port %x: ready [IRQ=%d QUE=%x].\n",unit,port,serial_ports_irq[unit],queue[unit]); + return 1; } @@ -117,6 +137,9 @@ static int is_valid_port(uint8_t port_no) return port_no < sizeof(serial_ports) / sizeof(int); } +/* + Polling read +*/ int serial_read(uint8_t port_no) { if(!is_valid_port(port_no)) @@ -124,7 +147,21 @@ int serial_read(uint8_t port_no) while(serial_received(serial_ports[port_no]) == 0); printf("[COM%d]\n",port_no); - return inb(serial_ports[port_no]); + return inb(serial_ports[port_no]); +} + +/* + Interrupt driven event queue (blocks process if no dat ais available) +*/ +int serial_getchar(uint8_t port_no) { + struct event e; + if(!is_valid_port(port_no)) + return -1; + + event_queue_read(queue[port_no],&e,sizeof(e)); + + if(e.type==EVENT_DATA) return (int)e.code; + return -1; } int serial_read_nonblock(uint8_t port_no) @@ -152,7 +189,8 @@ int serial_device_probe( int unit, int *blocksize, int *nblocks, char *info ) if(unit<0 || unit>3) return 0; // TODO probing for real hardware // reinit, second time (first time in serial_init, but needed to register the device) - serial_init_port(unit); + int status=serial_init_port(unit); + if (!status) return -1; *blocksize = 1; *nblocks = 0; strcpy(info,"serial"); @@ -206,7 +244,7 @@ void serial_init() { int i; for(i = 0; i < sizeof(serial_ports) / sizeof(int); i++) { - serial_init_port(i); + int status = serial_init_port(i); } device_driver_register(&serial_driver); }