includes: AROS_UFIxx -> AROS_INTxx change
[AROS.git] / arch / i386-pc / drivers / mouse.hidd / drv_ps2.c
blob1f3cfc0fe72768f943b01bd638f734bdf27fd1c4
1 /*
2 Copyright © 1995-2010, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: PS/2 mouse driver.
6 Lang: English.
7 */
9 /****************************************************************************************/
11 #include <asm/speaker.h>
12 #include <proto/exec.h>
13 #include <proto/utility.h>
14 #include <proto/oop.h>
15 #include <oop/oop.h>
17 #include <exec/alerts.h>
18 #include <exec/memory.h>
20 #include <hidd/hidd.h>
21 #include <hidd/mouse.h>
22 #include <devices/inputevent.h>
24 #include <SDI/SDI_interrupt.h>
26 #include "mouse.h"
28 #define DEBUG 0
29 #include <aros/debug.h>
31 /****************************************************************************************/
33 #ifdef HiddMouseAB
34 #undef HiddMouseAB
35 #endif
36 #define HiddMouseAB (MSD(cl)->hiddMouseAB)
38 /* defines for buttonstate */
40 #define LEFT_BUTTON 1
41 #define RIGHT_BUTTON 2
42 #define MIDDLE_BUTTON 4
44 /****************************************************************************************/
46 #define TIMER_RPROK 3599597124UL
48 int mouse_wait_for_input(void);
50 /****************************************************************************************/
52 int mouse_ps2reset(struct mouse_data *);
54 /****************************************************************************************/
56 static ULONG usec2tick(ULONG usec)
58 ULONG ret;
59 ULONG prok = TIMER_RPROK;
60 asm volatile("movl $0,%%eax; divl %2":"=a"(ret):"d"(usec),"m"(prok));
61 return ret;
64 static void mouse_usleep(LONG usec)
66 int oldtick, tick;
67 usec = usec2tick(usec);
69 outb(0x80, 0x43);
70 oldtick = inb(0x42);
71 oldtick += inb(0x42) << 8;
73 while (usec > 0)
75 outb(0x80, 0x43);
76 tick = inb(0x42);
77 tick += inb(0x42) << 8;
79 usec -= (oldtick - tick);
80 if (tick > oldtick) usec -= 0x10000;
81 oldtick = tick;
85 unsigned char handle_mouse_event(void)
87 unsigned char status = mouse_read_status();
88 unsigned int work = 10000;
90 while (status & KBD_STATUS_OBF)
92 mouse_read_input();
94 status = mouse_read_status();
95 if(!work--)
97 //printf(KERN_ERR "pc_keyb: controller jammed (0x%02X).\n",status);
98 break;
101 return status;
105 * Wait until we can write to a peripheral again. Any input that comes in
106 * while we're waiting is discarded.
108 void mouse_wait(void)
110 ULONG timeout = 1000; /* 1 sec should be enough */
114 unsigned char status = handle_mouse_event();
115 if (! (status & KBD_STATUS_IBF))
116 return;
118 mouse_usleep(1000);
119 timeout--;
120 } while (timeout);
123 void mouse_write_cmd(int cmd)
125 mouse_wait();
126 mouse_write_command(KBD_CTRLCMD_WRITE_MODE);
127 mouse_wait();
128 mouse_write_output(cmd);
131 void mouse_write_ack(int val)
133 mouse_wait();
134 mouse_write_command(KBD_CTRLCMD_WRITE_MOUSE);
135 mouse_wait();
136 mouse_write_output(val);
137 mouse_wait_for_input();
140 void mouse_write_noack(int val)
142 mouse_wait();
143 mouse_write_command(KBD_CTRLCMD_WRITE_MOUSE);
144 mouse_wait();
145 mouse_write_output(val);
148 void mouse_write_output_w(int data)
150 mouse_wait();
151 mouse_write_output(data);
154 void mouse_write_command_w(int data)
156 mouse_wait();
157 mouse_write_command(data);
160 #define KBD_NO_DATA (-1)
161 #define KBD_BAD_DATA (-2)
163 int mouse_read_data(void)
165 LONG retval = KBD_NO_DATA;
166 UBYTE status;
168 status = mouse_read_status();
169 if (status & KBD_STATUS_OBF)
171 UBYTE data = mouse_read_input();
173 retval = data;
174 if (status & (KBD_STATUS_GTO | KBD_STATUS_PERR))
175 retval = KBD_BAD_DATA;
178 return retval;
181 int mouse_clear_input(void)
183 int maxread = 100, code, lastcode = KBD_NO_DATA;
184 UBYTE status;
188 status = mouse_read_status();
189 if ((code = mouse_read_data()) == KBD_NO_DATA)
190 break;
191 if (!(status & KBD_STATUS_MOUSE_OBF))
192 lastcode = code;
193 } while (--maxread);
195 return lastcode;
198 int mouse_wait_for_input(void)
200 ULONG timeout = 1000;
204 int retval = mouse_read_data();
205 if (retval >= 0)
206 return retval;
207 mouse_usleep(1000);
208 } while(--timeout);
209 return -1;
212 /****************************************************************************************/
214 AROS_INTH1(mouse_ps2int,struct mouse_data *, data)
216 AROS_INTFUNC_INIT
218 struct pHidd_Mouse_Event *e = &data->u.ps2.event;
219 UWORD buttonstate;
220 WORD work = 10000;
221 UBYTE info, mousecode, *mouse_data;
223 info = mouse_read_status();
225 for(; ((info = mouse_read_status()) & KBD_STATUS_OBF) && work; work--)
227 if (!(info & KBD_STATUS_MOUSE_OBF))
230 ** Data from keyboard. Hopefully this gets through to keyboard interrupt
231 ** if we break out of for loop here :-\
233 break;
236 mousecode = mouse_read_input();
238 if (info & (KBD_STATUS_GTO | KBD_STATUS_PERR))
240 /* Ignore errors and messages for keyboard -> eat status/error byte */
241 continue;
244 /* Mouse Packet Byte */
246 mouse_data = data->u.ps2.mouse_data;
248 data->u.ps2.expected_mouse_acks = 0;
249 mouse_data[data->u.ps2.mouse_collected_bytes] = mousecode;
251 /* Packet validity check. Bit 3 of first mouse packet byte must be set */
253 if ((mouse_data[0] & 8) == 0)
255 data->u.ps2.mouse_collected_bytes = 0;
256 continue;
259 data->u.ps2.mouse_collected_bytes++;
261 if (data->u.ps2.mouse_collected_bytes != data->u.ps2.mouse_packetsize)
263 /* Mouse Packet not yet complete */
264 continue;
267 /* We have a complete mouse packet :-) */
269 data->u.ps2.mouse_collected_bytes = 0;
272 * Let's see whether these data can be right...
274 * D7 D6 D5 D4 D3 D2 D1 D0
275 * YV XV YS XS 1 M R L
276 * X7 . . . . . . X1 (X)
277 * Y7 . . . . . . Y1 (Y)
279 * XV,YV : overflow in x/y direction
280 * XS,YS : most significant bit of X and Y: represents sign
281 * X,Y : displacement in x and y direction (8 least significant bits).
283 * X, XS, Y and YS make up two 9-bit two's complement fields.
286 #if 0
287 D(bug("Got the following: 1. byte: 0x%x, dx=%d, dy=%d\n",
288 mouse_data[0],
289 mouse_data[1],
290 mouse_data[2]));
291 #endif
293 e->x = mouse_data[1];
294 e->y = mouse_data[2];
296 if (mouse_data[0] & 0x10) e->x -= 256;
297 if (mouse_data[0] & 0x20) e->y -= 256;
299 /* dy is reversed! */
300 e->y = -(e->y);
302 if (e->x || e->y)
304 e->button = vHidd_Mouse_NoButton;
305 e->type = vHidd_Mouse_Motion;
307 data->mouse_callback(data->callbackdata, e);
310 buttonstate = mouse_data[0] & 0x07;
312 if ((buttonstate & LEFT_BUTTON) != (data->buttonstate & LEFT_BUTTON))
314 e->button = vHidd_Mouse_Button1;
315 e->type = (buttonstate & LEFT_BUTTON) ? vHidd_Mouse_Press : vHidd_Mouse_Release;
317 data->mouse_callback(data->callbackdata, e);
320 if ((buttonstate & RIGHT_BUTTON) != (data->buttonstate & RIGHT_BUTTON))
322 e->button = vHidd_Mouse_Button2;
323 e->type = (buttonstate & RIGHT_BUTTON) ? vHidd_Mouse_Press : vHidd_Mouse_Release;
325 data->mouse_callback(data->callbackdata, e);
328 if ((buttonstate & MIDDLE_BUTTON) != (data->buttonstate & MIDDLE_BUTTON))
330 e->button = vHidd_Mouse_Button3;
331 e->type = (buttonstate & MIDDLE_BUTTON) ? vHidd_Mouse_Press : vHidd_Mouse_Release;
333 data->mouse_callback(data->callbackdata, e);
336 data->buttonstate = buttonstate;
338 #if INTELLIMOUSE_SUPPORT
339 /* mouse wheel */
340 e->y = (mouse_data[3] & 8) ? (mouse_data[3] & 15) - 16 : (mouse_data[3] & 15);
341 if (e->y)
343 e->x = 0;
344 e->type = vHidd_Mouse_WheelMotion;
345 e->button = vHidd_Mouse_NoButton;
347 data->mouse_callback(data->callbackdata, e);
349 #endif
351 } /* for(; ((info = mouse_read_status()) & KBD_STATUS_OBF) && work; work--) */
353 if (!work)
355 D(bug("mouse.hidd: controller jammed (0x%02X).\n", info));
358 return FALSE;
360 AROS_INTFUNC_EXIT
363 /****************************************************************************************/
365 int test_mouse_ps2(OOP_Class *cl, OOP_Object *o)
367 struct mouse_data *data = OOP_INST_DATA(cl, o);
368 struct Library *kbd_hidd;
369 struct Interrupt *irq;
370 int result;
372 /* Test for a PS/2 controller */
373 if ((kbd_hidd = OpenLibrary("kbd.hidd", 0)) == NULL)
374 return 0;
375 CloseLibrary(kbd_hidd);
377 irq = &data->u.ps2.irq;
379 irq->is_Node.ln_Type = NT_INTERRUPT;
380 irq->is_Node.ln_Pri = 127;
381 irq->is_Node.ln_Name = "PS/2 mouse class irq";
382 irq->is_Code = (VOID_FUNC)mouse_ps2int;
383 irq->is_Data = (APTR)data;
385 AddIntServer(INTB_KERNEL + 12, irq);
387 Disable();
388 result = mouse_ps2reset(data);
389 Enable();
391 /* If mouse_ps2reset() returned non-zero value, there is vaild PS/2 mouse */
392 if (result)
394 return 1;
396 /* Either no PS/2 mouse or problems with it */
397 /* Remove mouse interrupt */
398 RemIntServer(INTB_KERNEL + 12, irq);
400 /* Report no PS/2 mouse */
401 return 0;
404 void dispose_mouse_ps2(OOP_Class *cl, OOP_Object *o) {
405 struct mouse_data *data = OOP_INST_DATA(cl, o);
407 RemIntServer(INTB_KERNEL + 12, &data->u.ps2.irq);
410 /****************************************************************************************/
412 void getps2State(OOP_Class *cl, OOP_Object *o, struct pHidd_Mouse_Event *event)
414 #if 0
415 struct mouse_data *data = OOP_INST_DATA(cl, o);
416 UBYTE ack;
418 /* The following doesn't seem to do anything useful */
419 mouse_write(KBD_OUTCMD_DISABLE);
420 /* switch to remote mode */
421 mouse_write(KBD_OUTCMD_SET_REMOTE_MODE);
422 /* we want data */
423 ack = data->u.ps2.expected_mouse_acks+1;
424 mouse_write(KBD_OUTCMD_READ_DATA);
425 while (data->u.ps2.expected_mouse_acks>=ack)
426 mouse_usleep(1000);
427 /* switch back to stream mode */
428 mouse_write(KBD_OUTCMD_SET_STREAM_MODE);
429 mouse_write(KBD_OUTCMD_ENABLE);
430 #endif
433 /****************************************************************************************/
435 static int detect_aux()
437 int loops = 10;
438 int retval = 0;
440 mouse_wait();
442 mouse_write_command(KBD_CTRLCMD_WRITE_AUX_OBUF);
444 mouse_wait();
445 mouse_write_output(0x5a);
449 unsigned char status = mouse_read_status();
451 if (status & KBD_STATUS_OBF)
453 (void) mouse_read_input();
454 if (status & KBD_STATUS_MOUSE_OBF)
456 retval = 1;
458 break;
461 mouse_usleep(1000);
463 } while (--loops);
465 D(bug("PS/2 Auxilliary port %sdetected\n", retval ? "" : "not "));
466 return retval;
469 /****************************************************************************************/
471 static int query_mouse(UBYTE *buf, int size, int timeout)
473 int ret = 0;
477 UBYTE status = mouse_read_status();
479 if (status & KBD_STATUS_OBF)
481 UBYTE c = mouse_read_input();
483 if ((c != KBD_REPLY_ACK) && (status & KBD_STATUS_MOUSE_OBF))
485 buf[ret++] = c;
488 else
490 mouse_usleep(1000);
493 } while ((--timeout) && (ret < size));
495 return ret;
499 /****************************************************************************************/
501 static int detect_intellimouse(void)
503 UBYTE id = 0;
505 /* Try to switch into IMPS2 mode */
507 mouse_write_ack(KBD_OUTCMD_SET_RATE);
508 mouse_write_ack(200);
509 mouse_write_ack(KBD_OUTCMD_SET_RATE);
510 mouse_write_ack(100);
511 mouse_write_ack(KBD_OUTCMD_SET_RATE);
512 mouse_write_ack(80);
513 mouse_write_ack(KBD_OUTCMD_GET_ID);
514 mouse_write_noack(KBD_OUTCMD_GET_ID);
516 query_mouse(&id, 1, 20);
518 return ((id == 3) || (id == 4)) ? id : 0;
521 /****************************************************************************************/
523 #define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT)
524 #define AUX_INTS_ON (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT)
526 /****************************************************************************************/
528 int mouse_ps2reset(struct mouse_data *data)
531 * The commands are for the mouse and nobody else.
534 mouse_write_command_w(KBD_CTRLCMD_MOUSE_ENABLE);
537 * Check for a mouse port.
539 if (!detect_aux())
540 return 0;
543 * Unfortunately on my computer these commands cause
544 * the mouse not to work at all if they are issued
545 * in a different order. So please keep it that way.
546 * - Stefan
550 * Turn interrupts off and the keyboard as well since the
551 * commands are all for the mouse.
553 mouse_write_cmd(AUX_INTS_OFF);
554 mouse_write_command_w(KBD_CTRLCMD_KBD_DISABLE);
556 /* Reset mouse */
557 mouse_write_ack(KBD_OUTCMD_RESET);
558 mouse_wait_for_input(); /* Test result (0xAA) */
559 mouse_wait_for_input(); /* Mouse type */
561 data->u.ps2.mouse_protocol = PS2_PROTOCOL_STANDARD;
562 data->u.ps2.mouse_packetsize = 3;
564 #if INTELLIMOUSE_SUPPORT
565 if (detect_intellimouse())
567 data->u.ps2.mouse_protocol = PS2_PROTOCOL_INTELLIMOUSE;
568 data->u.ps2.mouse_packetsize = 4;
570 #endif
573 * Now the commands themselves.
575 mouse_write_ack(KBD_OUTCMD_SET_RATE);
576 mouse_write_ack(100);
577 mouse_write_ack(KBD_OUTCMD_SET_RES);
578 mouse_write_ack(2);
579 mouse_write_ack(KBD_OUTCMD_SET_SCALE11);
581 /* Enable Aux device (and re-enable keyboard) */
583 mouse_write_command_w(KBD_CTRLCMD_KBD_ENABLE);
584 mouse_write_ack(KBD_OUTCMD_ENABLE);
585 mouse_write_cmd(AUX_INTS_ON);
588 * According to the specs there is an external
589 * latch that holds the level-sensitive interrupt
590 * request until the CPU reads port 0x60.
591 * If this is not read then the mouse does not
592 * work on my computer.- Stefan
595 mouse_read_data();
597 D(bug("[Mouse] Found and initialized PS/2 mouse!\n"));
599 return 1;
602 /****************************************************************************************/