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: nuclear@51: void (*callback)(void*); nuclear@51: void *cbarg; nuclear@51: nuclear@51: struct timer_event *next; nuclear@51: }; nuclear@51: nuclear@51: nuclear@33: static void intr_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@35: interrupt(IRQ_TO_INTR(0), intr_handler); nuclear@33: } nuclear@33: nuclear@51: int start_timer(unsigned long msec, timer_func_t cbfunc, void *cbarg) nuclear@51: { nuclear@51: int ticks, tsum, istate; nuclear@51: struct timer_event *ev, *node; nuclear@51: nuclear@51: printf("start_timer(%lu)\n", msec); nuclear@51: nuclear@51: if((ticks = MSEC_TO_TICKS(msec)) <= 0) { nuclear@51: cbfunc(cbarg); nuclear@51: return 0; nuclear@51: } nuclear@51: nuclear@51: if(!(ev = malloc(sizeof *ev))) { nuclear@51: printf("start_timer: failed to allocate timer_event structure\n"); nuclear@51: return -1; nuclear@51: } nuclear@51: ev->callback = cbfunc; nuclear@51: ev->cbarg = cbarg; 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@51: set_intr_state(istate); nuclear@51: return 0; nuclear@51: } nuclear@51: nuclear@51: tsum = evlist->dt; nuclear@51: node = evlist; nuclear@51: nuclear@51: while(node->next && ticks > tsum + node->next->dt) { nuclear@51: tsum += node->next->dt; nuclear@51: node = node->next; nuclear@51: } nuclear@51: nuclear@51: ev->next = node->next; nuclear@51: node->next = ev; nuclear@51: nuclear@51: /* fix the relative times */ nuclear@51: ev->dt = ticks - tsum; nuclear@51: if(ev->next) { nuclear@51: ev->next->dt -= ev->dt; nuclear@51: } nuclear@51: nuclear@51: set_intr_state(istate); nuclear@51: return 0; 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@51: static void intr_handler(int inum) nuclear@33: { nuclear@51: int istate; nuclear@51: struct process *cur_proc; nuclear@51: nuclear@33: nticks++; nuclear@51: nuclear@51: 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@51: while(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@51: ev->callback(ev->cbarg); nuclear@51: free(ev); nuclear@51: } nuclear@51: } nuclear@51: nuclear@51: if((cur_proc = get_current_proc())) { nuclear@51: if(--cur_proc->ticks_left <= 0) { nuclear@51: /* since schedule will not return, we have to notify nuclear@51: * the PIC that we're done with the IRQ handling nuclear@51: */ nuclear@51: end_of_irq(INTR_TO_IRQ(inum)); nuclear@51: schedule(); nuclear@51: } nuclear@51: } nuclear@51: nuclear@51: set_intr_state(istate); nuclear@33: }