kern

annotate src/timer.c @ 51:b1e8c8251884

lalalala
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 01 Aug 2011 06:45:29 +0300
parents f65b348780e3
children 88a6c4e192f9
rev   line source
nuclear@33 1 #include <stdio.h>
nuclear@37 2 #include <time.h>
nuclear@33 3 #include "intr.h"
nuclear@33 4 #include "asmops.h"
nuclear@34 5 #include "timer.h"
nuclear@51 6 #include "proc.h"
nuclear@51 7 #include "sched.h"
nuclear@33 8 #include "config.h"
nuclear@33 9
nuclear@33 10 /* frequency of the oscillator driving the 8254 timer */
nuclear@33 11 #define OSC_FREQ_HZ 1193182
nuclear@33 12
nuclear@33 13 /* macro to divide and round to the nearest integer */
nuclear@33 14 #define DIV_ROUND(a, b) ((a) / (b) + ((a) % (b)) / ((b) / 2))
nuclear@33 15
nuclear@33 16 /* I/O ports connected to the 8254 */
nuclear@33 17 #define PORT_DATA0 0x40
nuclear@33 18 #define PORT_DATA1 0x41
nuclear@33 19 #define PORT_DATA2 0x42
nuclear@33 20 #define PORT_CMD 0x43
nuclear@33 21
nuclear@33 22 /* command bits */
nuclear@33 23 #define CMD_CHAN0 0
nuclear@33 24 #define CMD_CHAN1 (1 << 6)
nuclear@33 25 #define CMD_CHAN2 (2 << 6)
nuclear@33 26 #define CMD_RDBACK (3 << 6)
nuclear@33 27
nuclear@33 28 #define CMD_LATCH 0
nuclear@33 29 #define CMD_ACCESS_LOW (1 << 4)
nuclear@33 30 #define CMD_ACCESS_HIGH (2 << 4)
nuclear@33 31 #define CMD_ACCESS_BOTH (3 << 4)
nuclear@33 32
nuclear@33 33 #define CMD_OP_INT_TERM 0
nuclear@33 34 #define CMD_OP_ONESHOT (1 << 1)
nuclear@33 35 #define CMD_OP_RATE (2 << 1)
nuclear@33 36 #define CMD_OP_SQWAVE (3 << 1)
nuclear@33 37 #define CMD_OP_SOFT_STROBE (4 << 1)
nuclear@33 38 #define CMD_OP_HW_STROBE (5 << 1)
nuclear@33 39
nuclear@33 40 #define CMD_MODE_BIN 0
nuclear@33 41 #define CMD_MODE_BCD 1
nuclear@33 42
nuclear@33 43
nuclear@51 44 #define MSEC_TO_TICKS(ms) ((ms) * TICK_FREQ_HZ / 1000)
nuclear@51 45
nuclear@51 46 struct timer_event {
nuclear@51 47 int dt; /* remaining ticks delta from the previous event */
nuclear@51 48
nuclear@51 49 void (*callback)(void*);
nuclear@51 50 void *cbarg;
nuclear@51 51
nuclear@51 52 struct timer_event *next;
nuclear@51 53 };
nuclear@51 54
nuclear@51 55
nuclear@33 56 static void intr_handler();
nuclear@33 57
nuclear@33 58
nuclear@51 59 static struct timer_event *evlist;
nuclear@51 60
nuclear@51 61
nuclear@33 62 void init_timer(void)
nuclear@33 63 {
nuclear@34 64 /* calculate the reload count: round(osc / freq) */
nuclear@34 65 int reload_count = DIV_ROUND(OSC_FREQ_HZ, TICK_FREQ_HZ);
nuclear@33 66
nuclear@33 67 /* set the mode to square wave for channel 0, both low
nuclear@34 68 * and high reload count bytes will follow...
nuclear@33 69 */
nuclear@33 70 outb(CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE, PORT_CMD);
nuclear@33 71
nuclear@34 72 /* write the low and high bytes of the reload count to the
nuclear@33 73 * port for channel 0
nuclear@33 74 */
nuclear@34 75 outb(reload_count & 0xff, PORT_DATA0);
nuclear@34 76 outb((reload_count >> 8) & 0xff, PORT_DATA0);
nuclear@33 77
nuclear@33 78 /* set the timer interrupt handler */
nuclear@35 79 interrupt(IRQ_TO_INTR(0), intr_handler);
nuclear@33 80 }
nuclear@33 81
nuclear@51 82 int start_timer(unsigned long msec, timer_func_t cbfunc, void *cbarg)
nuclear@51 83 {
nuclear@51 84 int ticks, tsum, istate;
nuclear@51 85 struct timer_event *ev, *node;
nuclear@51 86
nuclear@51 87 printf("start_timer(%lu)\n", msec);
nuclear@51 88
nuclear@51 89 if((ticks = MSEC_TO_TICKS(msec)) <= 0) {
nuclear@51 90 cbfunc(cbarg);
nuclear@51 91 return 0;
nuclear@51 92 }
nuclear@51 93
nuclear@51 94 if(!(ev = malloc(sizeof *ev))) {
nuclear@51 95 printf("start_timer: failed to allocate timer_event structure\n");
nuclear@51 96 return -1;
nuclear@51 97 }
nuclear@51 98 ev->callback = cbfunc;
nuclear@51 99 ev->cbarg = cbarg;
nuclear@51 100
nuclear@51 101 istate = get_intr_state();
nuclear@51 102 disable_intr();
nuclear@51 103
nuclear@51 104 /* insert at the beginning */
nuclear@51 105 if(!evlist || ticks <= evlist->dt) {
nuclear@51 106 ev->next = evlist;
nuclear@51 107 evlist = ev;
nuclear@51 108
nuclear@51 109 ev->dt = ticks;
nuclear@51 110 if(ev->next) {
nuclear@51 111 ev->next->dt -= ticks;
nuclear@51 112 }
nuclear@51 113 set_intr_state(istate);
nuclear@51 114 return 0;
nuclear@51 115 }
nuclear@51 116
nuclear@51 117 tsum = evlist->dt;
nuclear@51 118 node = evlist;
nuclear@51 119
nuclear@51 120 while(node->next && ticks > tsum + node->next->dt) {
nuclear@51 121 tsum += node->next->dt;
nuclear@51 122 node = node->next;
nuclear@51 123 }
nuclear@51 124
nuclear@51 125 ev->next = node->next;
nuclear@51 126 node->next = ev;
nuclear@51 127
nuclear@51 128 /* fix the relative times */
nuclear@51 129 ev->dt = ticks - tsum;
nuclear@51 130 if(ev->next) {
nuclear@51 131 ev->next->dt -= ev->dt;
nuclear@51 132 }
nuclear@51 133
nuclear@51 134 set_intr_state(istate);
nuclear@51 135 return 0;
nuclear@51 136 }
nuclear@51 137
nuclear@33 138 /* This will be called by the interrupt dispatcher approximately
nuclear@33 139 * every 1/250th of a second, so it must be extremely fast.
nuclear@33 140 * For now, just increasing a tick counter will suffice.
nuclear@33 141 */
nuclear@51 142 static void intr_handler(int inum)
nuclear@33 143 {
nuclear@51 144 int istate;
nuclear@51 145 struct process *cur_proc;
nuclear@51 146
nuclear@33 147 nticks++;
nuclear@51 148
nuclear@51 149 printf("TICKS: %d\n", nticks);
nuclear@51 150
nuclear@51 151 istate = get_intr_state();
nuclear@51 152 disable_intr();
nuclear@51 153
nuclear@51 154 /* find out if there are any timers that have to go off */
nuclear@51 155 if(evlist) {
nuclear@51 156 evlist->dt--;
nuclear@51 157
nuclear@51 158 while(evlist->dt <= 0) {
nuclear@51 159 struct timer_event *ev = evlist;
nuclear@51 160 evlist = evlist->next;
nuclear@51 161
nuclear@51 162 printf("timer going off!!!\n");
nuclear@51 163 ev->callback(ev->cbarg);
nuclear@51 164 free(ev);
nuclear@51 165 }
nuclear@51 166 }
nuclear@51 167
nuclear@51 168 if((cur_proc = get_current_proc())) {
nuclear@51 169 if(--cur_proc->ticks_left <= 0) {
nuclear@51 170 /* since schedule will not return, we have to notify
nuclear@51 171 * the PIC that we're done with the IRQ handling
nuclear@51 172 */
nuclear@51 173 end_of_irq(INTR_TO_IRQ(inum));
nuclear@51 174 schedule();
nuclear@51 175 }
nuclear@51 176 }
nuclear@51 177
nuclear@51 178 set_intr_state(istate);
nuclear@33 179 }