nuclear@33: #include nuclear@37: #include nuclear@33: #include "intr.h" nuclear@33: #include "asmops.h" nuclear@34: #include "timer.h" nuclear@51: #include "proc.h" nuclear@51: #include "sched.h" nuclear@33: #include "config.h" nuclear@33: nuclear@33: /* frequency of the oscillator driving the 8254 timer */ nuclear@33: #define OSC_FREQ_HZ 1193182 nuclear@33: nuclear@33: /* macro to divide and round to the nearest integer */ nuclear@33: #define DIV_ROUND(a, b) ((a) / (b) + ((a) % (b)) / ((b) / 2)) nuclear@33: nuclear@33: /* I/O ports connected to the 8254 */ nuclear@33: #define PORT_DATA0 0x40 nuclear@33: #define PORT_DATA1 0x41 nuclear@33: #define PORT_DATA2 0x42 nuclear@33: #define PORT_CMD 0x43 nuclear@33: nuclear@33: /* command bits */ nuclear@33: #define CMD_CHAN0 0 nuclear@33: #define CMD_CHAN1 (1 << 6) nuclear@33: #define CMD_CHAN2 (2 << 6) nuclear@33: #define CMD_RDBACK (3 << 6) nuclear@33: nuclear@33: #define CMD_LATCH 0 nuclear@33: #define CMD_ACCESS_LOW (1 << 4) nuclear@33: #define CMD_ACCESS_HIGH (2 << 4) nuclear@33: #define CMD_ACCESS_BOTH (3 << 4) nuclear@33: nuclear@33: #define CMD_OP_INT_TERM 0 nuclear@33: #define CMD_OP_ONESHOT (1 << 1) nuclear@33: #define CMD_OP_RATE (2 << 1) nuclear@33: #define CMD_OP_SQWAVE (3 << 1) nuclear@33: #define CMD_OP_SOFT_STROBE (4 << 1) nuclear@33: #define CMD_OP_HW_STROBE (5 << 1) nuclear@33: nuclear@33: #define CMD_MODE_BIN 0 nuclear@33: #define CMD_MODE_BCD 1 nuclear@33: nuclear@33: nuclear@51: #define MSEC_TO_TICKS(ms) ((ms) * TICK_FREQ_HZ / 1000) nuclear@51: nuclear@51: struct timer_event { nuclear@51: int dt; /* remaining ticks delta from the previous event */ nuclear@51: struct timer_event *next; nuclear@51: }; nuclear@51: nuclear@51: nuclear@63: static void timer_handler(); nuclear@33: nuclear@33: nuclear@51: static struct timer_event *evlist; nuclear@51: nuclear@51: nuclear@33: void init_timer(void) nuclear@33: { nuclear@34: /* calculate the reload count: round(osc / freq) */ nuclear@34: int reload_count = DIV_ROUND(OSC_FREQ_HZ, TICK_FREQ_HZ); nuclear@33: nuclear@33: /* set the mode to square wave for channel 0, both low nuclear@34: * and high reload count bytes will follow... nuclear@33: */ nuclear@33: outb(CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE, PORT_CMD); nuclear@33: nuclear@34: /* write the low and high bytes of the reload count to the nuclear@33: * port for channel 0 nuclear@33: */ nuclear@34: outb(reload_count & 0xff, PORT_DATA0); nuclear@34: outb((reload_count >> 8) & 0xff, PORT_DATA0); nuclear@33: nuclear@33: /* set the timer interrupt handler */ nuclear@63: interrupt(IRQ_TO_INTR(0), timer_handler); nuclear@33: } nuclear@33: nuclear@55: void sleep(unsigned long msec) nuclear@51: { nuclear@51: int ticks, tsum, istate; nuclear@51: struct timer_event *ev, *node; nuclear@51: nuclear@55: printf("sleep(%lu)\n", msec); nuclear@51: nuclear@51: if((ticks = MSEC_TO_TICKS(msec)) <= 0) { nuclear@55: return; nuclear@51: } nuclear@51: nuclear@51: if(!(ev = malloc(sizeof *ev))) { nuclear@55: printf("sleep: failed to allocate timer_event structure\n"); nuclear@55: return; nuclear@51: } nuclear@51: nuclear@51: istate = get_intr_state(); nuclear@51: disable_intr(); nuclear@51: nuclear@51: /* insert at the beginning */ nuclear@51: if(!evlist || ticks <= evlist->dt) { nuclear@51: ev->next = evlist; nuclear@51: evlist = ev; nuclear@51: nuclear@51: ev->dt = ticks; nuclear@51: if(ev->next) { nuclear@51: ev->next->dt -= ticks; nuclear@51: } nuclear@55: } else { nuclear@51: nuclear@55: tsum = evlist->dt; nuclear@55: node = evlist; nuclear@51: nuclear@55: while(node->next && ticks > tsum + node->next->dt) { nuclear@55: tsum += node->next->dt; nuclear@55: node = node->next; nuclear@55: } nuclear@51: nuclear@55: ev->next = node->next; nuclear@55: node->next = ev; nuclear@51: nuclear@55: /* fix the relative times */ nuclear@55: ev->dt = ticks - tsum; nuclear@55: if(ev->next) { nuclear@55: ev->next->dt -= ev->dt; nuclear@55: } nuclear@51: } nuclear@51: nuclear@51: set_intr_state(istate); nuclear@55: nuclear@55: /* wait on the address of this timer event */ nuclear@55: wait(ev); nuclear@51: } nuclear@51: nuclear@33: /* This will be called by the interrupt dispatcher approximately nuclear@33: * every 1/250th of a second, so it must be extremely fast. nuclear@33: * For now, just increasing a tick counter will suffice. nuclear@33: */ nuclear@63: static void timer_handler(int inum) nuclear@33: { nuclear@51: int istate; nuclear@57: struct process *p; nuclear@51: nuclear@33: nticks++; nuclear@51: nuclear@56: /*printf("TICKS: %d\n", nticks);*/ nuclear@51: nuclear@51: istate = get_intr_state(); nuclear@51: disable_intr(); nuclear@51: nuclear@51: /* find out if there are any timers that have to go off */ nuclear@51: if(evlist) { nuclear@51: evlist->dt--; nuclear@51: nuclear@56: while(evlist && evlist->dt <= 0) { nuclear@51: struct timer_event *ev = evlist; nuclear@51: evlist = evlist->next; nuclear@51: nuclear@51: printf("timer going off!!!\n"); nuclear@55: /* wake up all processes waiting on this address */ nuclear@55: wakeup(ev); nuclear@51: free(ev); nuclear@51: } nuclear@51: } nuclear@51: nuclear@57: /* decrement the process' ticks_left and call the scheduler to decide if nuclear@57: * it's time to switch processes nuclear@57: */ nuclear@57: if((p = get_current_proc())) { nuclear@57: p->ticks_left--; nuclear@57: } nuclear@56: schedule(); nuclear@51: nuclear@51: set_intr_state(istate); nuclear@33: }