summaryrefslogtreecommitdiff
path: root/kernel/old/drivers/clock.c
blob: 9f3d4be827c80492fec2ff8a7a4ea1212748fdba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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);
}