xref: /minix3/minix/drivers/hid/pckbd/pckbd.c (revision cfd712b4245f67a5631cc14e950ce43b18455602)
1433d6423SLionel Sambuc /* Keyboard driver for PCs and ATs. */
2433d6423SLionel Sambuc #include <minix/drivers.h>
3433d6423SLionel Sambuc #include <minix/input.h>
4433d6423SLionel Sambuc #include <minix/inputdriver.h>
5433d6423SLionel Sambuc 
6433d6423SLionel Sambuc #include "pckbd.h"
7433d6423SLionel Sambuc 
8433d6423SLionel Sambuc /*
9433d6423SLionel Sambuc  * Data that is to be sent to the keyboard. Each byte is ACKed by the keyboard.
10433d6423SLionel Sambuc  * This is currently somewhat overpowered for its only purpose: setting LEDs.
11433d6423SLionel Sambuc  */
12433d6423SLionel Sambuc static struct kbdout {
13433d6423SLionel Sambuc 	unsigned char buf[KBD_OUT_BUFSZ];
14433d6423SLionel Sambuc 	int offset;
15433d6423SLionel Sambuc 	int avail;
16433d6423SLionel Sambuc 	int expect_ack;
17433d6423SLionel Sambuc } kbdout;
18433d6423SLionel Sambuc 
19433d6423SLionel Sambuc static int kbd_watchdog_set = 0;
20433d6423SLionel Sambuc static int kbd_alive = 1;
21433d6423SLionel Sambuc static minix_timer_t tmr_kbd_wd;
22433d6423SLionel Sambuc 
23433d6423SLionel Sambuc static int irq_hook_id = -1;
24433d6423SLionel Sambuc static int aux_irq_hook_id = -1;
25433d6423SLionel Sambuc 
26433d6423SLionel Sambuc static int kbd_state = 0;
27433d6423SLionel Sambuc 
28433d6423SLionel Sambuc static unsigned char aux_bytes[3];
29433d6423SLionel Sambuc static unsigned char aux_state = 0;
30433d6423SLionel Sambuc static int aux_counter = 0;
313d310546SLionel Sambuc static int aux_available = 0;
32433d6423SLionel Sambuc 
33433d6423SLionel Sambuc static void pckbd_leds(unsigned int);
34433d6423SLionel Sambuc static void pckbd_intr(unsigned int);
35433d6423SLionel Sambuc static void pckbd_alarm(clock_t);
36433d6423SLionel Sambuc 
37433d6423SLionel Sambuc static struct inputdriver pckbd_tab = {
38433d6423SLionel Sambuc 	.idr_leds	= pckbd_leds,
39433d6423SLionel Sambuc 	.idr_intr	= pckbd_intr,
40433d6423SLionel Sambuc 	.idr_alarm	= pckbd_alarm
41433d6423SLionel Sambuc };
42433d6423SLionel Sambuc 
43433d6423SLionel Sambuc /*
44433d6423SLionel Sambuc  * The watchdog timer function, implementing all but the actual reset.
45433d6423SLionel Sambuc  */
46433d6423SLionel Sambuc static void
kbd_watchdog(int arg __unused)47*cfd712b4SDavid van Moolenbroek kbd_watchdog(int arg __unused)
48433d6423SLionel Sambuc {
49433d6423SLionel Sambuc 	kbd_watchdog_set = 0;
50433d6423SLionel Sambuc 	if (!kbdout.avail)
51433d6423SLionel Sambuc 		return;	/* Watchdog is no longer needed */
52433d6423SLionel Sambuc 
53433d6423SLionel Sambuc 	if (!kbd_alive)
54433d6423SLionel Sambuc 		printf("PCKBD: watchdog should reset keyboard\n");
55433d6423SLionel Sambuc 	kbd_alive = 0;
56433d6423SLionel Sambuc 
57433d6423SLionel Sambuc 	set_timer(&tmr_kbd_wd, sys_hz(), kbd_watchdog, 0);
58433d6423SLionel Sambuc 
59433d6423SLionel Sambuc 	kbd_watchdog_set = 1;
60433d6423SLionel Sambuc }
61433d6423SLionel Sambuc 
62433d6423SLionel Sambuc /*
63433d6423SLionel Sambuc  * Send queued data to the keyboard.
64433d6423SLionel Sambuc  */
65433d6423SLionel Sambuc static void
kbd_send(void)66433d6423SLionel Sambuc kbd_send(void)
67433d6423SLionel Sambuc {
68433d6423SLionel Sambuc 	u32_t sb;
69433d6423SLionel Sambuc 	int r;
70433d6423SLionel Sambuc 
71433d6423SLionel Sambuc 	if (!kbdout.avail)
72433d6423SLionel Sambuc 		return;
73433d6423SLionel Sambuc 	if (kbdout.expect_ack)
74433d6423SLionel Sambuc 		return;
75433d6423SLionel Sambuc 
76433d6423SLionel Sambuc 	if ((r = sys_inb(KB_STATUS, &sb)) != OK)
77433d6423SLionel Sambuc 		printf("PCKBD: send sys_inb() failed (1): %d\n", r);
78433d6423SLionel Sambuc 
79433d6423SLionel Sambuc 	if (sb & (KB_OUT_FULL | KB_IN_FULL)) {
80433d6423SLionel Sambuc 		printf("PCKBD: not sending (1): sb = 0x%x\n", sb);
81433d6423SLionel Sambuc 		return;
82433d6423SLionel Sambuc 	}
83433d6423SLionel Sambuc 	micro_delay(KBC_IN_DELAY);
84433d6423SLionel Sambuc 	if ((r = sys_inb(KB_STATUS, &sb)) != OK)
85433d6423SLionel Sambuc 		printf("PCKBD: send sys_inb() failed (2): %d\n", r);
86433d6423SLionel Sambuc 	if (sb & (KB_OUT_FULL | KB_IN_FULL)) {
87433d6423SLionel Sambuc 		printf("PCKBD: not sending (2): sb = 0x%x\n", sb);
88433d6423SLionel Sambuc 		return;
89433d6423SLionel Sambuc 	}
90433d6423SLionel Sambuc 
91433d6423SLionel Sambuc 	/* Okay, buffer is really empty */
92433d6423SLionel Sambuc 	if ((r = sys_outb(KEYBD, kbdout.buf[kbdout.offset])) != OK)
93433d6423SLionel Sambuc 		printf("PCKBD: send sys_outb() failed: %d\n", r);
94433d6423SLionel Sambuc 	kbdout.offset++;
95433d6423SLionel Sambuc 	kbdout.avail--;
96433d6423SLionel Sambuc 	kbdout.expect_ack = 1;
97433d6423SLionel Sambuc 
98433d6423SLionel Sambuc 	kbd_alive = 1;
99433d6423SLionel Sambuc 	if (kbd_watchdog_set) {
100433d6423SLionel Sambuc 		/* Set a watchdog timer for one second. */
101433d6423SLionel Sambuc 		set_timer(&tmr_kbd_wd, sys_hz(), kbd_watchdog, 0);
102433d6423SLionel Sambuc 
103433d6423SLionel Sambuc 		kbd_watchdog_set = 1;
104433d6423SLionel Sambuc 	}
105433d6423SLionel Sambuc }
106433d6423SLionel Sambuc 
107433d6423SLionel Sambuc /*
108433d6423SLionel Sambuc  * Try to obtain input from the keyboard.
109433d6423SLionel Sambuc  */
110433d6423SLionel Sambuc static int
scan_keyboard(unsigned char * bp,int * isauxp)111433d6423SLionel Sambuc scan_keyboard(unsigned char *bp, int *isauxp)
112433d6423SLionel Sambuc {
113433d6423SLionel Sambuc 	u32_t b, sb;
114433d6423SLionel Sambuc 	int r;
115433d6423SLionel Sambuc 
116433d6423SLionel Sambuc 	if ((r = sys_inb(KB_STATUS, &sb)) != OK) {
117433d6423SLionel Sambuc 		printf("PCKBD: scan sys_inb() failed (1): %d\n", r);
118433d6423SLionel Sambuc 		return FALSE;
119433d6423SLionel Sambuc 	}
120433d6423SLionel Sambuc 	if (!(sb & KB_OUT_FULL)) {
121433d6423SLionel Sambuc 		if (kbdout.avail && !kbdout.expect_ack)
122433d6423SLionel Sambuc 			kbd_send();
123433d6423SLionel Sambuc 		return FALSE;
124433d6423SLionel Sambuc 	}
125433d6423SLionel Sambuc 	if ((r = sys_inb(KEYBD, &b)) != OK) {
126433d6423SLionel Sambuc 		printf("PCKBD: scan sys_inb() failed (2): %d\n", r);
127433d6423SLionel Sambuc 		return FALSE;
128433d6423SLionel Sambuc 	}
1293d310546SLionel Sambuc 	if (!(sb & 0x40) && b == KB_ACK && kbdout.expect_ack) {
130433d6423SLionel Sambuc 		kbdout.expect_ack = 0;
131433d6423SLionel Sambuc 		micro_delay(KBC_IN_DELAY);
132433d6423SLionel Sambuc 		kbd_send();
133433d6423SLionel Sambuc 		return FALSE;
134433d6423SLionel Sambuc 	}
135433d6423SLionel Sambuc 	if (bp)
136433d6423SLionel Sambuc 		*bp = b;
137433d6423SLionel Sambuc 	if (isauxp)
138433d6423SLionel Sambuc 		*isauxp = !!(sb & KB_AUX_BYTE);
139433d6423SLionel Sambuc 	if (kbdout.avail && !kbdout.expect_ack) {
140433d6423SLionel Sambuc 		micro_delay(KBC_IN_DELAY);
141433d6423SLionel Sambuc 		kbd_send();
142433d6423SLionel Sambuc 	}
143433d6423SLionel Sambuc 	return TRUE;
144433d6423SLionel Sambuc }
145433d6423SLionel Sambuc 
146433d6423SLionel Sambuc /*
147433d6423SLionel Sambuc  * Wait until the controller is ready.  Return TRUE on success, FALSE on
148433d6423SLionel Sambuc  * timeout.  Since this may discard input, only use during initialization.
149433d6423SLionel Sambuc  */
150433d6423SLionel Sambuc static int
kb_wait(void)151433d6423SLionel Sambuc kb_wait(void)
152433d6423SLionel Sambuc {
153433d6423SLionel Sambuc 	spin_t spin;
154433d6423SLionel Sambuc 	u32_t status;
155433d6423SLionel Sambuc 	int r, isaux;
156433d6423SLionel Sambuc 	unsigned char byte;
157433d6423SLionel Sambuc 
158433d6423SLionel Sambuc 	SPIN_FOR(&spin, KBC_WAIT_TIME) {
159433d6423SLionel Sambuc 		if ((r = sys_inb(KB_STATUS, &status)) != OK)
160433d6423SLionel Sambuc 			printf("PCKBD: wait sys_inb() failed: %d\n", r);
161433d6423SLionel Sambuc 		if (status & KB_OUT_FULL)
162433d6423SLionel Sambuc 			(void) scan_keyboard(&byte, &isaux);
163433d6423SLionel Sambuc 		if (!(status & (KB_IN_FULL | KB_OUT_FULL)))
164433d6423SLionel Sambuc 			return TRUE;		/* wait until ready */
165433d6423SLionel Sambuc 	}
166433d6423SLionel Sambuc 
167433d6423SLionel Sambuc 	printf("PCKBD: wait timeout\n");
168433d6423SLionel Sambuc 	return FALSE;
169433d6423SLionel Sambuc }
170433d6423SLionel Sambuc 
171433d6423SLionel Sambuc /*
172433d6423SLionel Sambuc  * Set the LEDs on the caps, num, and scroll lock keys.
173433d6423SLionel Sambuc  */
174433d6423SLionel Sambuc static void
set_leds(unsigned char ledmask)175433d6423SLionel Sambuc set_leds(unsigned char ledmask)
176433d6423SLionel Sambuc {
177433d6423SLionel Sambuc 	if (kbdout.avail == 0)
178433d6423SLionel Sambuc 		kbdout.offset = 0;
179433d6423SLionel Sambuc 	if (kbdout.offset + kbdout.avail + 2 > KBD_OUT_BUFSZ) {
180433d6423SLionel Sambuc 		/*
181433d6423SLionel Sambuc 		 * The output buffer is full.  Ignore this command.  Reset the
182433d6423SLionel Sambuc 		 * ACK flag.
183433d6423SLionel Sambuc 		 */
184433d6423SLionel Sambuc 		kbdout.expect_ack = 0;
185433d6423SLionel Sambuc 	} else {
186433d6423SLionel Sambuc 		kbdout.buf[kbdout.offset+kbdout.avail] = LED_CODE;
187433d6423SLionel Sambuc 		kbdout.buf[kbdout.offset+kbdout.avail+1] = ledmask;
188433d6423SLionel Sambuc 		kbdout.avail += 2;
189433d6423SLionel Sambuc 	}
190433d6423SLionel Sambuc 	if (!kbdout.expect_ack)
191433d6423SLionel Sambuc 		kbd_send();
192433d6423SLionel Sambuc }
193433d6423SLionel Sambuc 
194433d6423SLionel Sambuc /*
195433d6423SLionel Sambuc  * Send a command to the keyboard.
196433d6423SLionel Sambuc  */
197433d6423SLionel Sambuc static void
kbc_cmd0(int cmd)198433d6423SLionel Sambuc kbc_cmd0(int cmd)
199433d6423SLionel Sambuc {
200433d6423SLionel Sambuc 	int r;
201433d6423SLionel Sambuc 
202433d6423SLionel Sambuc 	kb_wait();
203433d6423SLionel Sambuc 	if ((r = sys_outb(KB_COMMAND, cmd)) != OK)
204433d6423SLionel Sambuc 		printf("PCKBD: cmd0 sys_outb() failed: %d\n", r);
205433d6423SLionel Sambuc }
206433d6423SLionel Sambuc 
207433d6423SLionel Sambuc /*
208433d6423SLionel Sambuc  * Send a command to the keyboard, including data.
209433d6423SLionel Sambuc  */
210433d6423SLionel Sambuc static void
kbc_cmd1(int cmd,int data)211433d6423SLionel Sambuc kbc_cmd1(int cmd, int data)
212433d6423SLionel Sambuc {
213433d6423SLionel Sambuc 	int r;
214433d6423SLionel Sambuc 
215433d6423SLionel Sambuc 	kb_wait();
216433d6423SLionel Sambuc 	if ((r = sys_outb(KB_COMMAND, cmd)) != OK)
217433d6423SLionel Sambuc 		printf("PCKBD: cmd1 sys_outb() failed (1): %d\n", r);
218433d6423SLionel Sambuc 	kb_wait();
219433d6423SLionel Sambuc 	if ((r = sys_outb(KEYBD, data)) != OK)
220433d6423SLionel Sambuc 		printf("PCKBD: cmd1 sys_outb() failed (2): %d\n", r);
221433d6423SLionel Sambuc }
222433d6423SLionel Sambuc 
223433d6423SLionel Sambuc /*
224433d6423SLionel Sambuc  * Wait at most one second for a byte from the keyboard or the controller.
225433d6423SLionel Sambuc  */
226433d6423SLionel Sambuc static int
kbc_read(void)227433d6423SLionel Sambuc kbc_read(void)
228433d6423SLionel Sambuc {
229433d6423SLionel Sambuc 	u32_t byte, status;
230433d6423SLionel Sambuc 	spin_t spin;
231433d6423SLionel Sambuc 	int r;
232433d6423SLionel Sambuc 
233433d6423SLionel Sambuc 	SPIN_FOR(&spin, KBC_READ_TIME) {
234433d6423SLionel Sambuc 		if ((r = sys_inb(KB_STATUS, &status)) != OK)
235433d6423SLionel Sambuc 			printf("PCKBD: read sys_inb() failed (1): %d\n", r);
236433d6423SLionel Sambuc 		if (status & KB_OUT_FULL) {
237433d6423SLionel Sambuc 			micro_delay(KBC_IN_DELAY);
238433d6423SLionel Sambuc 			if ((r = sys_inb(KEYBD, &byte)) != OK)
239433d6423SLionel Sambuc 				printf("PCKBD: read sys_inb() failed (2): "
240433d6423SLionel Sambuc 				    "%d\n", r);
241433d6423SLionel Sambuc 			if (status & KB_AUX_BYTE)
242433d6423SLionel Sambuc 				printf("PCKBD: read got aux 0x%x\n", byte);
243433d6423SLionel Sambuc 			return byte;
244433d6423SLionel Sambuc 		}
245433d6423SLionel Sambuc 	}
246433d6423SLionel Sambuc 
247433d6423SLionel Sambuc 	panic("kbc_read failed to complete");
248433d6423SLionel Sambuc }
249433d6423SLionel Sambuc 
250433d6423SLionel Sambuc /*
251433d6423SLionel Sambuc  * Initialize the keyboard hardware.
252433d6423SLionel Sambuc  */
253e1e2bc96Srlfnb static int
kb_init(void)254433d6423SLionel Sambuc kb_init(void)
255433d6423SLionel Sambuc {
256433d6423SLionel Sambuc 	int r, ccb;
257433d6423SLionel Sambuc 
2583d310546SLionel Sambuc 	/* Disable the keyboard and AUX. */
2593d310546SLionel Sambuc 	kbc_cmd0(KBC_DI_KBD);
2603d310546SLionel Sambuc 	kbc_cmd0(KBC_DI_AUX);
2613d310546SLionel Sambuc 
262433d6423SLionel Sambuc 	/* Discard leftover keystroke. */
263433d6423SLionel Sambuc 	scan_keyboard(NULL, NULL);
264433d6423SLionel Sambuc 
2653d310546SLionel Sambuc 	/* Get the current configuration byte. */
2663d310546SLionel Sambuc 	kbc_cmd0(KBC_RD_RAM_CCB);
2673d310546SLionel Sambuc 	ccb = kbc_read();
2683d310546SLionel Sambuc 
2693d310546SLionel Sambuc 	/* If bit 5 is clear, it is a single channel controler for sure.. */
2703d310546SLionel Sambuc 	aux_available = (ccb & 0x10);
2713d310546SLionel Sambuc 
2723d310546SLionel Sambuc 	/* Execute Controller Self Test. */
2733d310546SLionel Sambuc 	kbc_cmd0(0xAA);
2743d310546SLionel Sambuc 	r = kbc_read();
275e1e2bc96Srlfnb 	if (r != 0x55){
276e1e2bc96Srlfnb 		printf("PCKBD: Controller self-test failed.\n");
277e1e2bc96Srlfnb 		return EGENERIC;
278e1e2bc96Srlfnb 	}
2793d310546SLionel Sambuc 
280433d6423SLionel Sambuc 	/* Set interrupt handler and enable keyboard IRQ. */
281433d6423SLionel Sambuc 	irq_hook_id = KEYBOARD_IRQ;	/* id to be returned on interrupt */
282433d6423SLionel Sambuc 	r = sys_irqsetpolicy(KEYBOARD_IRQ, IRQ_REENABLE, &irq_hook_id);
283433d6423SLionel Sambuc 	if (r != OK)
284433d6423SLionel Sambuc 		panic("Couldn't set keyboard IRQ policy: %d", r);
285433d6423SLionel Sambuc 	if ((r = sys_irqenable(&irq_hook_id)) != OK)
286433d6423SLionel Sambuc 		panic("Couldn't enable keyboard IRQs: %d", r);
287433d6423SLionel Sambuc 
2883d310546SLionel Sambuc 	/* Activate IRQ bit for the keyboard. */
2893d310546SLionel Sambuc 	ccb |= 0x1;
2903d310546SLionel Sambuc 
2913d310546SLionel Sambuc 	if (aux_available != 0) {
292433d6423SLionel Sambuc 		/* Set AUX interrupt handler and enable AUX IRQ. */
293433d6423SLionel Sambuc 		aux_irq_hook_id = KBD_AUX_IRQ;	/* id to be returned on interrupt */
294433d6423SLionel Sambuc 		r = sys_irqsetpolicy(KBD_AUX_IRQ, IRQ_REENABLE, &aux_irq_hook_id);
295433d6423SLionel Sambuc 		if (r != OK)
296433d6423SLionel Sambuc 			panic("Couldn't set AUX IRQ policy: %d", r);
297433d6423SLionel Sambuc 		if ((r = sys_irqenable(&aux_irq_hook_id)) != OK)
298433d6423SLionel Sambuc 			panic("Couldn't enable AUX IRQs: %d", r);
299433d6423SLionel Sambuc 
3003d310546SLionel Sambuc 		/* Activate IRQ for AUX. */
3013d310546SLionel Sambuc 		ccb |= 0x2;
3023d310546SLionel Sambuc 	}
303433d6423SLionel Sambuc 
3043d310546SLionel Sambuc 	/* Enable interrupt(s). */
3053d310546SLionel Sambuc 	kbc_cmd1(KBC_WR_RAM_CCB, ccb);
306433d6423SLionel Sambuc 
307433d6423SLionel Sambuc 	/* Re-enable the keyboard device. */
308433d6423SLionel Sambuc 	kbc_cmd0(KBC_EN_KBD);
309433d6423SLionel Sambuc 
3103d310546SLionel Sambuc 	if (aux_available != 0) {
311433d6423SLionel Sambuc 		/* Enable the AUX device. */
312433d6423SLionel Sambuc 		kbc_cmd0(KBC_EN_AUX);
3133d310546SLionel Sambuc 		kbc_cmd1(0xD4, 0xF6);
3143d310546SLionel Sambuc 		kbc_cmd1(0xD4, 0xF4);
3153d310546SLionel Sambuc 	}
316433d6423SLionel Sambuc 
317433d6423SLionel Sambuc 	/* Set the initial LED state. */
318433d6423SLionel Sambuc 	kb_wait();
319433d6423SLionel Sambuc 
320433d6423SLionel Sambuc 	set_leds(0);
321e1e2bc96Srlfnb 	return OK;
322433d6423SLionel Sambuc }
323433d6423SLionel Sambuc 
324433d6423SLionel Sambuc /*
325433d6423SLionel Sambuc  * Process a keyboard scancode.
326433d6423SLionel Sambuc  */
327433d6423SLionel Sambuc static void
kbd_process(unsigned char scode)328433d6423SLionel Sambuc kbd_process(unsigned char scode)
329433d6423SLionel Sambuc {
330433d6423SLionel Sambuc 	int press, index, page, code;
331433d6423SLionel Sambuc 
332433d6423SLionel Sambuc 	press = !(scode & SCAN_RELEASE) ? INPUT_PRESS : INPUT_RELEASE;
333433d6423SLionel Sambuc 	index = scode & ~SCAN_RELEASE;
334433d6423SLionel Sambuc 
335433d6423SLionel Sambuc 	switch (kbd_state) {
336433d6423SLionel Sambuc 	case 1:
337433d6423SLionel Sambuc 		page = scanmap_escaped[index].page;
338433d6423SLionel Sambuc 		code = scanmap_escaped[index].code;
339433d6423SLionel Sambuc 		break;
340433d6423SLionel Sambuc 	case 2:
341433d6423SLionel Sambuc 		kbd_state = (index == SCAN_CTRL) ? 3 : 0;
342433d6423SLionel Sambuc 		return;
343433d6423SLionel Sambuc 	case 3:
344433d6423SLionel Sambuc 		if (index == SCAN_NUMLOCK) {
345433d6423SLionel Sambuc 			page = INPUT_PAGE_KEY;
346433d6423SLionel Sambuc 			code = INPUT_KEY_PAUSE;
347433d6423SLionel Sambuc 			break;
348433d6423SLionel Sambuc 		}
349433d6423SLionel Sambuc 		/* FALLTHROUGH */
350433d6423SLionel Sambuc 	default:
351433d6423SLionel Sambuc 		switch (scode) {
352433d6423SLionel Sambuc 		case SCAN_EXT0:
353433d6423SLionel Sambuc 			kbd_state = 1;
354433d6423SLionel Sambuc 			return;
355433d6423SLionel Sambuc 		case SCAN_EXT1:
356433d6423SLionel Sambuc 			kbd_state = 2;
357433d6423SLionel Sambuc 			return;
358433d6423SLionel Sambuc 		}
359433d6423SLionel Sambuc 		page = scanmap_normal[index].page;
360433d6423SLionel Sambuc 		code = scanmap_normal[index].code;
361433d6423SLionel Sambuc 		break;
362433d6423SLionel Sambuc 	}
363433d6423SLionel Sambuc 
364433d6423SLionel Sambuc 	if (page)
365433d6423SLionel Sambuc 		inputdriver_send_event(FALSE /*mouse*/, page, code, press, 0);
366433d6423SLionel Sambuc 
367433d6423SLionel Sambuc 	kbd_state = 0;
368433d6423SLionel Sambuc }
369433d6423SLionel Sambuc 
370433d6423SLionel Sambuc /*
371433d6423SLionel Sambuc  * Process an auxiliary (mouse) scancode.
372433d6423SLionel Sambuc  */
373433d6423SLionel Sambuc static void
kbdaux_process(unsigned char scode)374433d6423SLionel Sambuc kbdaux_process(unsigned char scode)
375433d6423SLionel Sambuc {
376433d6423SLionel Sambuc 	u32_t delta;
377433d6423SLionel Sambuc 	int i;
378433d6423SLionel Sambuc 
379433d6423SLionel Sambuc 	if (aux_counter == 0 && !(scode & 0x08))
380433d6423SLionel Sambuc 		return;	/* resync */
381433d6423SLionel Sambuc 
382433d6423SLionel Sambuc 	aux_bytes[aux_counter++] = scode;
383433d6423SLionel Sambuc 
384433d6423SLionel Sambuc 	if (aux_counter < 3)
385433d6423SLionel Sambuc 		return; /* need more first */
386433d6423SLionel Sambuc 
387433d6423SLionel Sambuc 	aux_counter = 0;
388433d6423SLionel Sambuc 
389433d6423SLionel Sambuc 	/* Send an event for each button state change. */
390433d6423SLionel Sambuc 	for (i = 0; i < 3; i++) {
391433d6423SLionel Sambuc 		if ((aux_state ^ aux_bytes[0]) & (1 << i)) {
392433d6423SLionel Sambuc 			aux_state ^= (1 << i);
393433d6423SLionel Sambuc 
394433d6423SLionel Sambuc 			inputdriver_send_event(TRUE /*mouse*/,
395433d6423SLionel Sambuc 			    INPUT_PAGE_BUTTON, INPUT_BUTTON_1 + i,
3963d310546SLionel Sambuc 			    !!(aux_state & (1 << i)), 0);
397433d6423SLionel Sambuc 		}
398433d6423SLionel Sambuc 	}
399433d6423SLionel Sambuc 
400433d6423SLionel Sambuc 	/* Send an event for each relative mouse movement, X and/or Y. */
401433d6423SLionel Sambuc 	for (i = 0; i < 2; i++) {
402433d6423SLionel Sambuc 		delta = aux_bytes[1 + i];
403433d6423SLionel Sambuc 		if (delta != 0) {
404433d6423SLionel Sambuc 			if (aux_bytes[0] & (0x10 << i))
405433d6423SLionel Sambuc 				delta |= 0xFFFFFF00; /* make signed */
406433d6423SLionel Sambuc 
407433d6423SLionel Sambuc 			inputdriver_send_event(TRUE /*mouse*/, INPUT_PAGE_GD,
408433d6423SLionel Sambuc 			    !i ? INPUT_GD_X : INPUT_GD_Y, delta,
409433d6423SLionel Sambuc 			    INPUT_FLAG_REL);
410433d6423SLionel Sambuc 		}
411433d6423SLionel Sambuc 	}
412433d6423SLionel Sambuc }
413433d6423SLionel Sambuc 
414433d6423SLionel Sambuc /*
415433d6423SLionel Sambuc  * Set keyboard LEDs.
416433d6423SLionel Sambuc  */
417433d6423SLionel Sambuc static void
pckbd_leds(unsigned int leds)418433d6423SLionel Sambuc pckbd_leds(unsigned int leds)
419433d6423SLionel Sambuc {
420433d6423SLionel Sambuc 	unsigned char b;
421433d6423SLionel Sambuc 
422433d6423SLionel Sambuc 	b = 0;
423433d6423SLionel Sambuc 	if (leds & (1 << INPUT_LED_NUMLOCK)) b |= LED_NUM_LOCK;
424433d6423SLionel Sambuc 	if (leds & (1 << INPUT_LED_CAPSLOCK)) b |= LED_CAPS_LOCK;
425433d6423SLionel Sambuc 	if (leds & (1 << INPUT_LED_SCROLLLOCK)) b |= LED_SCROLL_LOCK;
426433d6423SLionel Sambuc 
427433d6423SLionel Sambuc 	set_leds(b);
428433d6423SLionel Sambuc }
429433d6423SLionel Sambuc 
430433d6423SLionel Sambuc /*
431433d6423SLionel Sambuc  * Process a keyboard interrupt.
432433d6423SLionel Sambuc  */
433433d6423SLionel Sambuc static void
pckbd_intr(unsigned int UNUSED (mask))434433d6423SLionel Sambuc pckbd_intr(unsigned int UNUSED(mask))
435433d6423SLionel Sambuc {
436433d6423SLionel Sambuc 	unsigned char scode;
437433d6423SLionel Sambuc 	int isaux;
438433d6423SLionel Sambuc 
439433d6423SLionel Sambuc 	/* Fetch a character from the keyboard hardware and acknowledge it. */
440433d6423SLionel Sambuc 	if (!scan_keyboard(&scode, &isaux))
441433d6423SLionel Sambuc 		return;
442433d6423SLionel Sambuc 
443433d6423SLionel Sambuc 	if (!isaux) {
444433d6423SLionel Sambuc 		/* A keyboard key press or release. */
445433d6423SLionel Sambuc 		kbd_process(scode);
446433d6423SLionel Sambuc 	} else {
447433d6423SLionel Sambuc 		/* A mouse event. */
448433d6423SLionel Sambuc 		kbdaux_process(scode);
449433d6423SLionel Sambuc 	}
450433d6423SLionel Sambuc }
451433d6423SLionel Sambuc 
452433d6423SLionel Sambuc /*
453433d6423SLionel Sambuc  * Process a timer signal.
454433d6423SLionel Sambuc  */
455433d6423SLionel Sambuc static void
pckbd_alarm(clock_t stamp)456433d6423SLionel Sambuc pckbd_alarm(clock_t stamp)
457433d6423SLionel Sambuc {
458433d6423SLionel Sambuc 	expire_timers(stamp);
459433d6423SLionel Sambuc }
460433d6423SLionel Sambuc 
461433d6423SLionel Sambuc /*
462433d6423SLionel Sambuc  * Initialize the driver.
463433d6423SLionel Sambuc  */
464433d6423SLionel Sambuc static int
pckbd_init(int UNUSED (type),sef_init_info_t * UNUSED (info))465433d6423SLionel Sambuc pckbd_init(int UNUSED(type), sef_init_info_t *UNUSED(info))
466433d6423SLionel Sambuc {
4673d310546SLionel Sambuc 	int flags = INPUT_DEV_KBD;
468433d6423SLionel Sambuc 	/* Initialize the watchdog timer. */
469433d6423SLionel Sambuc 	init_timer(&tmr_kbd_wd);
470433d6423SLionel Sambuc 
471433d6423SLionel Sambuc 	/* Initialize the keyboard. */
472e1e2bc96Srlfnb 	int r;
473e1e2bc96Srlfnb 	if((r = kb_init())!=OK){
474e1e2bc96Srlfnb 		return r;
475e1e2bc96Srlfnb 	}
476433d6423SLionel Sambuc 
477433d6423SLionel Sambuc 	/* Announce the driver's presence. */
4783d310546SLionel Sambuc 	if (aux_available != 0)
4793d310546SLionel Sambuc 		flags |= INPUT_DEV_MOUSE;
4803d310546SLionel Sambuc 	inputdriver_announce(flags);
481433d6423SLionel Sambuc 
482433d6423SLionel Sambuc 	return OK;
483433d6423SLionel Sambuc }
484433d6423SLionel Sambuc 
485433d6423SLionel Sambuc /*
486433d6423SLionel Sambuc  * Set callback routines and let SEF initialize.
487433d6423SLionel Sambuc  */
488433d6423SLionel Sambuc static void
pckbd_startup(void)489433d6423SLionel Sambuc pckbd_startup(void)
490433d6423SLionel Sambuc {
491433d6423SLionel Sambuc 	sef_setcb_init_fresh(pckbd_init);
492433d6423SLionel Sambuc 
493433d6423SLionel Sambuc 	sef_startup();
494433d6423SLionel Sambuc }
495433d6423SLionel Sambuc 
496433d6423SLionel Sambuc /*
497433d6423SLionel Sambuc  * PC keyboard/mouse driver task.
498433d6423SLionel Sambuc  */
499433d6423SLionel Sambuc int
main(void)500433d6423SLionel Sambuc main(void)
501433d6423SLionel Sambuc {
502433d6423SLionel Sambuc 	pckbd_startup();
503433d6423SLionel Sambuc 
504433d6423SLionel Sambuc 	inputdriver_task(&pckbd_tab);
505433d6423SLionel Sambuc 
506433d6423SLionel Sambuc 	return 0;
507433d6423SLionel Sambuc }
508