diff options
Diffstat (limited to 'kernel/old/drivers')
-rw-r--r-- | kernel/old/drivers/clock.c | 152 | ||||
-rw-r--r-- | kernel/old/drivers/serial.c | 629 |
2 files changed, 781 insertions, 0 deletions
diff --git a/kernel/old/drivers/clock.c b/kernel/old/drivers/clock.c new file mode 100644 index 0000000..9f3d4be --- /dev/null +++ b/kernel/old/drivers/clock.c @@ -0,0 +1,152 @@ +#define KERNEL_SRC + +#include <common.h> + +#include <clock.h> +#include <procs.h> + +#include <x86/arch.h> +#include <x86/pic.h> +#include <x86/pit.h> + +/* +** PRIVATE DEFINITIONS +*/ + +/* +** PRIVATE DATA TYPES +*/ + +/* +** PRIVATE GLOBAL VARIABLES +*/ + +// pinwheel control variables +static uint32_t pinwheel; // pinwheel counter +static uint32_t pindex; // index into pinwheel string + +/* +** PUBLIC GLOBAL VARIABLES +*/ + +// current system time +uint32_t system_time; + +/* +** PRIVATE FUNCTIONS +*/ + +/** +** Name: clk_isr +** +** The ISR for the clock +** +** @param vector Vector number for the clock interrupt +** @param code Error code (0 for this interrupt) +*/ +static void clk_isr(int vector, int code) +{ + // spin the pinwheel + + ++pinwheel; + if (pinwheel == (CLOCK_FREQ / 10)) { + pinwheel = 0; + ++pindex; + cio_putchar_at(0, 0, "|/-\\"[pindex & 3]); + } + +#if defined(SYSTEM_STATUS) + // Periodically, dump the queue lengths and the SIO status (along + // with the SIO buffers, if non-empty). + // + // Define the symbol SYSTEM_STATUS with a value equal to the desired + // reporting frequency, in seconds. + + if ((system_time % SEC_TO_TICKS(SYSTEM_STATUS)) == 0) { + cio_printf_at(1, 0, " queues: R[%u] W[%u] S[%u] Z[%u] I[%u] ", + pcb_queue_length(ready), pcb_queue_length(waiting), + pcb_queue_length(sleeping), pcb_queue_length(zombie), + pcb_queue_length(sioread)); + } +#endif + + // time marches on! + ++system_time; + + // wake up any sleeping processes whose time has come + // + // we give them preference over the current process when + // it is scheduled again + + do { + // if there isn't anyone in the sleep queue, we're done + if (pcb_queue_empty(sleeping)) { + break; + } + + // peek at the first member of the queue + pcb_t *tmp = pcb_queue_peek(sleeping); + assert(tmp != NULL); + + // the sleep queue is sorted in ascending order by wakeup + // time, so we know that the retrieved PCB's wakeup time is + // the earliest of any process on the sleep queue; if that + // time hasn't arrived yet, there's nobody left to awaken + + if (tmp->wakeup > system_time) { + break; + } + + // OK, we need to wake this process up + assert(pcb_queue_remove(sleeping, &tmp) == SUCCESS); + schedule(tmp); + } while (1); + + // next, we decrement the current process' remaining time + current->ticks -= 1; + + // has it expired? + if (current->ticks < 1) { + // yes! reschedule it + schedule(current); + current = NULL; + // and pick a new process + dispatch(); + } + + // tell the PIC we're done + outb(PIC1_CMD, PIC_EOI); +} + +/* +** PUBLIC FUNCTIONS +*/ + +/** +** Name: clk_init +** +** Initializes the clock module +** +*/ +void clk_init(void) +{ +#if TRACING_INIT + cio_puts(" Clock"); +#endif + + // start the pinwheel + pinwheel = (CLOCK_FREQ / 10) - 1; + pindex = 0; + + // return to the dawn of time + system_time = 0; + + // configure the clock + uint32_t divisor = PIT_FREQ / CLOCK_FREQ; + outb(PIT_CONTROL_PORT, PIT_0_LOAD | PIT_0_SQUARE); + outb(PIT_0_PORT, divisor & 0xff); // LSB of divisor + outb(PIT_0_PORT, (divisor >> 8) & 0xff); // MSB of divisor + + // register the second-stage ISR + install_isr(VEC_TIMER, clk_isr); +} diff --git a/kernel/old/drivers/serial.c b/kernel/old/drivers/serial.c new file mode 100644 index 0000000..d6572e5 --- /dev/null +++ b/kernel/old/drivers/serial.c @@ -0,0 +1,629 @@ +#define KERNEL_SRC + +// this should do all includes required for this OS +#include <compat.h> + +// all other framework includes are next +#include <x86/uart.h> +#include <x86/arch.h> +#include <x86/pic.h> + +#include <sio.h> +#include <lib.h> + +/* +** PRIVATE DEFINITIONS +*/ + +#define BUF_SIZE 2048 + +/* +** PRIVATE GLOBALS +*/ + +// input character buffer +static char inbuffer[BUF_SIZE]; +static char *inlast; +static char *innext; +static uint32_t incount; + +// output character buffer +static char outbuffer[BUF_SIZE]; +static char *outlast; +static char *outnext; +static uint32_t outcount; + +// output control flag +static int sending; + +// interrupt register status +static uint8_t ier; + +/* +** PUBLIC GLOBAL VARIABLES +*/ + +// queue for read-blocked processes +#ifdef QNAME +extern QTYPE QNAME; +#endif + +/* +** PRIVATE FUNCTIONS +*/ + +/** +** sio_isr(vector,ecode) +** +** Interrupt handler for the SIO module. Handles all pending +** events (as described by the SIO controller). +** +** @param vector The interrupt vector number for this interrupt +** @param ecode The error code associated with this interrupt +*/ +static void sio_isr(int vector, int ecode) +{ + int ch; + +#if TRACING_SIO_ISR + cio_puts("SIO: int:"); +#endif + // + // Must process all pending events; loop until the IRR + // says there's nothing else to do. + // + + for (;;) { + // get the "pending event" indicator + int iir = inb(UA4_IIR) & UA4_IIR_INT_PRI_MASK; + + // process this event + switch (iir) { + case UA4_IIR_LINE_STATUS: + // shouldn't happen, but just in case.... + cio_printf("** SIO int, LSR = %02x\n", inb(UA4_LSR)); + break; + + case UA4_IIR_RX: +#if TRACING_SIO_ISR + cio_puts(" RX"); +#endif + // get the character + ch = inb(UA4_RXD); + if (ch == '\r') { // map CR to LF + ch = '\n'; + } +#if TRACING_SIO_ISR + cio_printf(" ch %02x", ch); +#endif + +#ifdef QNAME + // + // If there is a waiting process, this must be + // the first input character; give it to that + // process and awaken the process. + // + + if (!QEMPTY(QNAME)) { + PCBTYPE *pcb; + + QDEQUE(QNAME, pcb); + // make sure we got a non-NULL result + assert(pcb); + + // return char via arg #2 and count in EAX + char *buf = (char *)ARG(pcb, 2); + *buf = ch & 0xff; + RET(pcb) = 1; + SCHED(pcb); + + } else { +#endif /* QNAME */ + + // + // Nobody waiting - add to the input buffer + // if there is room, otherwise just ignore it. + // + + if (incount < BUF_SIZE) { + *inlast++ = ch; + ++incount; + } + +#ifdef QNAME + } +#endif /* QNAME */ + break; + + case UA5_IIR_RX_FIFO: + // shouldn't happen, but just in case.... + ch = inb(UA4_RXD); + cio_printf("** SIO FIFO timeout, RXD = %02x\n", ch); + break; + + case UA4_IIR_TX: +#if TRACING_SIO_ISR + cio_puts(" TX"); +#endif + // if there is another character, send it + if (sending && outcount > 0) { +#if TRACING_SIO_ISR + cio_printf(" ch %02x", *outnext); +#endif + outb(UA4_TXD, *outnext); + ++outnext; + // wrap around if necessary + if (outnext >= (outbuffer + BUF_SIZE)) { + outnext = outbuffer; + } + --outcount; +#if TRACING_SIO_ISR + cio_printf(" (outcount %d)", outcount); +#endif + } else { +#if TRACING_SIO_ISR + cio_puts(" EOS"); +#endif + // no more data - reset the output vars + outcount = 0; + outlast = outnext = outbuffer; + sending = 0; + // disable TX interrupts + sio_disable(SIO_TX); + } + break; + + case UA4_IIR_NO_INT: +#if TRACING_SIO_ISR + cio_puts(" EOI\n"); +#endif + // nothing to do - tell the PIC we're done + outb(PIC1_CMD, PIC_EOI); + return; + + case UA4_IIR_MODEM_STATUS: + // shouldn't happen, but just in case.... + cio_printf("** SIO int, MSR = %02x\n", inb(UA4_MSR)); + break; + + default: + // uh-oh.... + sprint(b256, "sio isr: IIR %02x\n", ((uint32_t)iir) & 0xff); + PANIC(0, b256); + } + } + + // should never reach this point! + assert(false); +} + +/* +** PUBLIC FUNCTIONS +*/ + +/** +** sio_init() +** +** Initialize the UART chip. +*/ +void sio_init(void) +{ +#if TRACING_INIT + cio_puts(" Sio"); +#endif + + /* + ** Initialize SIO variables. + */ + + memclr((void *)inbuffer, sizeof(inbuffer)); + inlast = innext = inbuffer; + incount = 0; + + memclr((void *)outbuffer, sizeof(outbuffer)); + outlast = outnext = outbuffer; + outcount = 0; + sending = 0; + + // queue of read-blocked processes + QCREATE(QNAME); + + /* + ** Next, initialize the UART. + ** + ** Initialize the FIFOs + ** + ** this is a bizarre little sequence of operations + */ + + outb(UA5_FCR, 0x20); + outb(UA5_FCR, UA5_FCR_FIFO_RESET); // 0x00 + outb(UA5_FCR, UA5_FCR_FIFO_EN); // 0x01 + outb(UA5_FCR, UA5_FCR_FIFO_EN | UA5_FCR_RXSR); // 0x03 + outb(UA5_FCR, UA5_FCR_FIFO_EN | UA5_FCR_RXSR | UA5_FCR_TXSR); // 0x07 + + /* + ** disable interrupts + ** + ** note that we leave them disabled; sio_enable() must be + ** called to switch them back on + */ + + outb(UA4_IER, 0); + ier = 0; + + /* + ** select the divisor latch registers and set the data rate + */ + + outb(UA4_LCR, UA4_LCR_DLAB); + outb(UA4_DLL, BAUD_LOW_BYTE(DL_BAUD_9600)); + outb(UA4_DLM, BAUD_HIGH_BYTE(DL_BAUD_9600)); + + /* + ** deselect the latch registers, by setting the data + ** characteristics in the LCR + */ + + outb(UA4_LCR, UA4_LCR_WLS_8 | UA4_LCR_1_STOP_BIT | UA4_LCR_NO_PARITY); + + /* + ** Set the ISEN bit to enable the interrupt request signal, + ** and the DTR and RTS bits to enable two-way communication. + */ + + outb(UA4_MCR, UA4_MCR_ISEN | UA4_MCR_DTR | UA4_MCR_RTS); + + /* + ** Install our ISR + */ + + install_isr(VEC_COM1, sio_isr); +} + +/** +** sio_enable() +** +** Enable SIO interrupts +** +** usage: uint8_t old = sio_enable( uint8_t which ) +** +** @param which Bit mask indicating which interrupt(s) to enable +** +** @return the prior IER setting +*/ +uint8_t sio_enable(uint8_t which) +{ + uint8_t old; + + // remember the current status + + old = ier; + + // figure out what to enable + + if (which & SIO_TX) { + ier |= UA4_IER_TX_IE; + } + + if (which & SIO_RX) { + ier |= UA4_IER_RX_IE; + } + + // if there was a change, make it + + if (old != ier) { + outb(UA4_IER, ier); + } + + // return the prior settings + + return (old); +} + +/** +** sio_disable() +** +** Disable SIO interrupts +** +** usage: uint8_t old = sio_disable( uint8_t which ) +** +** @param which Bit mask indicating which interrupt(s) to disable +** +** @return the prior IER setting +*/ +uint8_t sio_disable(uint8_t which) +{ + uint8_t old; + + // remember the current status + + old = ier; + + // figure out what to disable + + if (which & SIO_TX) { + ier &= ~UA4_IER_TX_IE; + } + + if (which & SIO_RX) { + ier &= ~UA4_IER_RX_IE; + } + + // if there was a change, make it + + if (old != ier) { + outb(UA4_IER, ier); + } + + // return the prior settings + + return (old); +} + +/** +** sio_inq_length() +** +** Get the input queue length +** +** usage: int num = sio_inq_length() +** +** @return the count of characters still in the input queue +*/ +int sio_inq_length(void) +{ + return (incount); +} + +/** +** sio_readc() +** +** Get the next input character +** +** usage: int ch = sio_readc() +** +** @return the next character, or -1 if no character is available +*/ +int sio_readc(void) +{ + int ch; + + // assume there is no character available + ch = -1; + + // + // If there is a character, return it + // + + if (incount > 0) { + // take it out of the input buffer + ch = ((int)(*innext++)) & 0xff; + --incount; + + // reset the buffer variables if this was the last one + if (incount < 1) { + inlast = innext = inbuffer; + } + } + + return (ch); +} + +/** +** sio_read(buf,length) +** +** Read the entire input buffer into a user buffer of a specified size +** +** usage: int num = sio_read( char *buffer, int length ) +** +** @param buf The destination buffer +** @param length Length of the buffer +** +** @return the number of bytes copied, or 0 if no characters were available +*/ + +int sio_read(char *buf, int length) +{ + char *ptr = buf; + int copied = 0; + + // if there are no characters, just return 0 + + if (incount < 1) { + return (0); + } + + // + // We have characters. Copy as many of them into the user + // buffer as will fit. + // + + while (incount > 0 && copied < length) { + *ptr++ = *innext++ & 0xff; + if (innext > (inbuffer + BUF_SIZE)) { + innext = inbuffer; + } + --incount; + ++copied; + } + + // reset the input buffer if necessary + + if (incount < 1) { + inlast = innext = inbuffer; + } + + // return the copy count + + return (copied); +} + +/** +** sio_writec( ch ) +** +** Write a character to the serial output +** +** usage: sio_writec( int ch ) +** +** @param ch Character to be written (in the low-order 8 bits) +*/ +void sio_writec(int ch) +{ + // + // Must do LF -> CRLF mapping + // + + if (ch == '\n') { + sio_writec('\r'); + } + + // + // If we're currently transmitting, just add this to the buffer + // + + if (sending) { + *outlast++ = ch; + ++outcount; + return; + } + + // + // Not sending - must prime the pump + // + + sending = 1; + outb(UA4_TXD, ch); + + // Also must enable transmitter interrupts + + sio_enable(SIO_TX); +} + +/** +** sio_write( buffer, length ) +** +** Write a buffer of characters to the serial output +** +** usage: int num = sio_write( const char *buffer, int length ) +** +** @param buffer Buffer containing characters to write +** @param length Number of characters to write +** +** @return the number of characters copied into the SIO output buffer +*/ +int sio_write(const char *buffer, int length) +{ + int first = *buffer; + const char *ptr = buffer; + int copied = 0; + + // + // If we are currently sending, we want to append all + // the characters to the output buffer; else, we want + // to append all but the first character, and then use + // sio_writec() to send the first one out. + // + + if (!sending) { + ptr += 1; + copied++; + } + + while (copied < length && outcount < BUF_SIZE) { + *outlast++ = *ptr++; + // wrap around if necessary + if (outlast >= (outbuffer + BUF_SIZE)) { + outlast = outbuffer; + } + ++outcount; + ++copied; + } + + // + // We use sio_writec() to send out the first character, + // as it will correctly set all the other necessary + // variables for us. + // + + if (!sending) { + sio_writec(first); + } + + // Return the transfer count + + return (copied); +} + +/** +** sio_puts( buf ) +** +** Write a NUL-terminated buffer of characters to the serial output +** +** usage: int num = sio_puts( const char *buffer ) +** +** @param buffer The buffer containing a NUL-terminated string +** +** @return the count of bytes transferred +*/ +int sio_puts(const char *buffer) +{ + int n; // must be outside the loop so we can return it + + n = SLENGTH(buffer); + sio_write(buffer, n); + + return (n); +} + +/** +** sio_dump( full ) +** +** dump the contents of the SIO buffers to the console +** +** usage: sio_dump(true) or sio_dump(false) +** +** @param full Boolean indicating whether or not a "full" dump +** is being requested (which includes the contents +** of the queues) +*/ + +void sio_dump(bool_t full) +{ + int n; + char *ptr; + + // dump basic info into the status region + + cio_printf_at(48, 0, "SIO: IER %02x (%c%c%c) in %d ot %d", + ((uint32_t)ier) & 0xff, sending ? '*' : '.', + (ier & UA4_IER_TX_IE) ? 'T' : 't', + (ier & UA4_IER_RX_IE) ? 'R' : 'r', incount, outcount); + + // if we're not doing a full dump, stop now + + if (!full) { + return; + } + + // also want the queue contents, but we'll + // dump them into the scrolling region + + if (incount) { + cio_puts("SIO input queue: \""); + ptr = innext; + for (n = 0; n < incount; ++n) { + put_char_or_code(*ptr++); + } + cio_puts("\"\n"); + } + + if (outcount) { + cio_puts("SIO output queue: \""); + cio_puts(" ot: \""); + ptr = outnext; + for (n = 0; n < outcount; ++n) { + put_char_or_code(*ptr++); + } + cio_puts("\"\n"); + } +} |