#include #include #include #include #include #include #include /* 115200 baud */ #define DIVISOR 1 #define RSBASE 0x3F8 #define RSDATA RSBASE #define RSINTEN (RSBASE + 1) #define RSFIFOC (RSBASE + 2) #define RSLINEC (RSBASE + 3) #define RSMODEMC (RSBASE + 4) #define RSLINES (RSBASE + 5) #define RSMODEMS (RSBASE + 6) #define RSSCRATCH (RSBASE + 7) #define rswait()\ while((inb(RSLINES) & 0x20) == 0); extern void rs_isr(void); static char present; static char rxbuf[256]; static unsigned rxread, rxwrite; static struct task_struct *waiting_task; int rsputs(char *s) { if(!present) return -ENXIO; while(*s) { rswait(); if(*s == '\n' || *s == '\r') { outb(RSDATA, '\n'); rswait(); outb(RSDATA, '\r'); s++; } else { outb(RSDATA, *s++); } } return 0; } void serial_init(void) { present = 0; outb(RSSCRATCH, 0x00); outb(RSSCRATCH, 0xAA); if(inb(RSSCRATCH) != 0xAA) { printk("[rs] COM0 not detected!\n"); return; } /* enable access to the divisor */ outb(RSLINEC, inb(RSLINEC) | 0x80); /* write the divisor */ outb(RSBASE, DIVISOR); outb(RSBASE + 1, DIVISOR >> 8); /* restore access to the divisor */ outb(RSLINEC, inb(RSLINEC) & 0x7F); /* configure port parameters */ outb(RSFIFOC, 0x06); outb(RSLINEC, 0x03); outb(RSMODEMC, 0x0B); outb(RSINTEN, 0x01); inb(RSDATA); /* register the interrupt handler */ register_isr(0x24, 0, &rs_isr); irq_enable(0x10); rsputs("[kernel] Serial monitor initialized!\n\r"); printk("[rs] Serial port COM0 initialized!\n"); rxread = 0; rxwrite = 1; waiting_task = NULL; present = 1; } ssize_t rsread(void *buf, size_t len) { ssize_t n = len; char *b = buf; if(!present) return -ENXIO; if(len == 0) return 0; while(n--) { if(rxread == rxwrite) { /* put the current task to sleep */ waiting_task = ctask; ctask->state = TSTATE_INTERRUPTIBLE; reschedule(); } /* check again to see if anything was read, or we were interrupted */ if(rxread == rxwrite) { waiting_task = NULL; return -EINTR; } *b++ = rxbuf[rxread]; rxread = rxread + 1 % sizeof(rxbuf); } return len; } void rs_handler(void) { if((inb(RSLINES) & 1) == 0) return; if(rxwrite == (rxread - 1 % sizeof(rxbuf))) return; rxbuf[rxwrite] = inb(RSDATA); rxwrite = rxwrite + 1 % sizeof(rxbuf); if(waiting_task != NULL) { waiting_task->state = TSTATE_RUNNING; waiting_task = NULL; } }