Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / arch / i386-pc / drivers / mouse.hidd / drv_ps2.c
blob4aa626052a375d7aa969ee0c6a77fcc2f68da23d
1 /*
2 Copyright © 1995-2001, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: PS/2 mouse driver.
6 Lang: English.
7 */
9 /****************************************************************************************/
11 #include <asm/io.h>
12 #include <asm/speaker.h>
13 #include <proto/exec.h>
14 #include <proto/utility.h>
15 #include <proto/oop.h>
16 #include <oop/oop.h>
18 #include <exec/alerts.h>
19 #include <exec/memory.h>
21 #include <hidd/hidd.h>
22 #include <hidd/mouse.h>
23 #include <hidd/irq.h>
24 #include <devices/inputevent.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 /* from Drivers/keyboard/kbd_common.c */
48 #warning "it is probably better to get rid of calling functions from external module"
50 int kbd_read_data(void);
51 void kb_wait(void);
52 void kbd_write_cmd(int cmd);
53 void aux_write_ack(int val);
54 void aux_write_noack(int val);
55 void kbd_write_command_w(int data);
57 /****************************************************************************************/
59 void mouse_usleep(ULONG usec);
61 /****************************************************************************************/
63 void mouse_ps2int(HIDDT_IRQ_Handler *, HIDDT_IRQ_HwInfo *);
64 int mouse_ps2reset(struct mouse_data *);
66 /****************************************************************************************/
68 int test_mouse_ps2(OOP_Class *cl, OOP_Object *o)
70 struct mouse_data *data = OOP_INST_DATA(cl, o);
72 /* Open IRQ Hidd */
73 if ((data->u.ps2.irqhidd = OOP_NewObject(NULL, CLID_Hidd_IRQ, NULL)))
75 HIDDT_IRQ_Handler *irq;
77 data->u.ps2.irq = irq = AllocMem(sizeof(HIDDT_IRQ_Handler), MEMF_CLEAR | MEMF_PUBLIC);
78 if (irq != NULL)
80 int result;
82 irq->h_Node.ln_Pri = 127;
83 irq->h_Node.ln_Name = "PS/2 mouse class irq";
84 irq->h_Code = mouse_ps2int;
85 irq->h_Data = (APTR)data;
87 HIDD_IRQ_AddHandler(data->u.ps2.irqhidd, irq, vHidd_IRQ_Mouse);
89 Disable();
90 result = mouse_ps2reset(data);
91 Enable();
93 /* If mouse_ps2reset() returned non-zero value, there is vaild PS/2 mouse */
94 if (result)
96 return 1;
98 /* Either no PS/2 mouse or problems with it */
99 /* Remove mouse interrupt */
100 HIDD_IRQ_RemHandler(data->u.ps2.irqhidd, irq);
101 /* Free IRQ structure as it's not needed anymore */
102 FreeMem(irq, sizeof(HIDDT_IRQ_Handler));
104 /* Dispose IRQ object */
105 OOP_DisposeObject(data->u.ps2.irqhidd);
107 /* Report no PS/2 mouse */
108 return 0;
111 void dispose_mouse_ps2(OOP_Class *cl, OOP_Object *o) {
112 struct mouse_data *data = OOP_INST_DATA(cl, o);
114 HIDD_IRQ_RemHandler(data->u.ps2.irqhidd, data->u.ps2.irq);
115 FreeMem(data->u.ps2.irq, sizeof(HIDDT_IRQ_Handler));
116 OOP_DisposeObject(data->u.ps2.irqhidd);
119 /****************************************************************************************/
121 #define AUX_RECONNECT 170
122 #define AUX_ACK 0xFA
124 #define aux_write(val) \
125 ({ data->u.ps2.expected_mouse_acks++; \
126 aux_write_ack(val); \
129 /****************************************************************************************/
131 void getps2Event(struct getps2data *, struct pHidd_Mouse_Event *);
133 void getps2State(OOP_Class *cl, OOP_Object *o, struct pHidd_Mouse_Event *event) {
134 struct mouse_data *data = OOP_INST_DATA(cl, o);
135 UBYTE ack;
137 aux_write(KBD_OUTCMD_DISABLE);
138 /* switch to remote mode */
139 aux_write(KBD_OUTCMD_SET_REMOTE_MODE);
140 /* we want data */
141 ack = data->u.ps2.expected_mouse_acks+1;
142 aux_write(KBD_OUTCMD_READ_DATA);
143 while (data->u.ps2.expected_mouse_acks>=ack)
144 mouse_usleep(1000);
145 /* switch back to sream mode */
146 aux_write(KBD_OUTCMD_SET_STREAM_MODE);
147 aux_write(KBD_OUTCMD_ENABLE);
150 /****************************************************************************************/
152 int kbd_detect_aux()
154 int loops = 10;
155 int retval = 0;
157 kb_wait();
159 kbd_write_command(KBD_CTRLCMD_WRITE_AUX_OBUF);
161 kb_wait();
162 kbd_write_output(0x5a);
166 unsigned char status = kbd_read_status();
168 if (status & KBD_STATUS_OBF)
170 (void) kbd_read_input();
171 if (status & KBD_STATUS_MOUSE_OBF)
173 retval = 1;
175 break;
178 mouse_usleep(1000);
180 } while (--loops);
182 return retval;
185 /****************************************************************************************/
187 static int query_mouse(UBYTE *buf, int size, int timeout)
189 int ret = 0;
193 UBYTE status = kbd_read_status();
195 if (status & KBD_STATUS_OBF)
197 UBYTE c = kbd_read_input();
199 if ((c != KBD_REPLY_ACK) && (status & KBD_STATUS_MOUSE_OBF))
201 buf[ret++] = c;
204 else
206 mouse_usleep(1000);
209 } while ((--timeout) && (ret < size));
211 return ret;
216 /****************************************************************************************/
218 static int detect_intellimouse(void)
220 UBYTE id = 0;
222 /* Try to switch into IMPS2 mode */
224 aux_write_ack(KBD_OUTCMD_SET_RATE);
225 aux_write_ack(200);
226 aux_write_ack(KBD_OUTCMD_SET_RATE);
227 aux_write_ack(100);
228 aux_write_ack(KBD_OUTCMD_SET_RATE);
229 aux_write_ack(80);
230 aux_write_ack(KBD_OUTCMD_GET_ID);
231 aux_write_noack(KBD_OUTCMD_GET_ID);
233 query_mouse(&id, 1, 20);
235 return ((id == 3) || (id == 4)) ? id : 0;
238 /****************************************************************************************/
240 #undef SysBase
241 #define SysBase (hw->sysBase)
244 void mouse_ps2int(HIDDT_IRQ_Handler *irq, HIDDT_IRQ_HwInfo *hw)
246 struct mouse_data *data =(struct mouse_data *)irq->h_Data;
247 struct pHidd_Mouse_Event *e = &data->u.ps2.event;
248 UWORD buttonstate;
249 WORD work = 10000;
250 UBYTE info, mousecode, *mouse_data;
252 info = kbd_read_status();
254 for(; ((info = kbd_read_status()) & KBD_STATUS_OBF) && work; work--)
256 if (!(info & KBD_STATUS_MOUSE_OBF))
259 ** Data from keyboard. Hopefully this gets through to keyboard interrupt
260 ** if we break out of for loop here :-\
262 break;
265 mousecode = kbd_read_input();
267 if (info & (KBD_STATUS_GTO | KBD_STATUS_PERR))
269 /* Ignore errors and messages for keyboard -> eat status/error byte */
270 continue;
273 if ((mousecode == AUX_ACK) && (data->u.ps2.expected_mouse_acks))
275 D(bug(" Got a mouse ACK!\n"));
276 data->u.ps2.expected_mouse_acks--;
277 continue;
280 #if 1
281 if (mousecode == AUX_RECONNECT)
283 data->u.ps2.mouse_collected_bytes = 0;
285 /* Ping mouse */
286 aux_write(KBD_OUTCMD_ENABLE);
288 #endif
290 /* Mouse Packet Byte */
292 mouse_data = data->u.ps2.mouse_data;
294 data->u.ps2.expected_mouse_acks = 0;
295 mouse_data[data->u.ps2.mouse_collected_bytes] = mousecode;
297 /* Packet validity check. Bit 3 of first mouse packet byte must be set */
299 if ((mouse_data[0] & 8) == 0)
301 data->u.ps2.mouse_collected_bytes = 0;
302 continue;
305 data->u.ps2.mouse_collected_bytes++;
307 if (data->u.ps2.mouse_collected_bytes != data->u.ps2.mouse_packetsize)
309 /* Mouse Packet not yet complete */
310 continue;
313 /* We have a complete mouse packet :-) */
315 data->u.ps2.mouse_collected_bytes = 0;
318 * Let's see whether these data can be right...
320 * D7 D6 D5 D4 D3 D2 D1 D0
321 * YV XV YS X2 1 M R L
322 * X7 . . . . . . X1 (X, signed)
323 * Y7 . . . . . . Y1 (Y, signed)
325 * YV,XV : over flow in x/y direction
326 * XS,YS : represents sign of X and Y
327 * X,Y : displacement in x and y direction.
328 * X and Y are signed, XS, YS are there to double check the
329 * sign and correctnes of the collected data (?).
331 * http://www.hut.fi/~then/mytexts/mouse.htm
334 #if 0
335 D(bug("Got the following: 1. byte: 0x%x, dx=%d, dy=%d\n",
336 mouse_data[0],
337 mouse_data[1],
338 mouse_data[2]));
339 #endif
341 e->x = mouse_data[1];
342 e->y = mouse_data[2];
344 if (mouse_data[0] & 0x10) e->x -= 256;
345 if (mouse_data[0] & 0x20) e->y -= 256;
347 /* dy is reversed! */
348 e->y = -(e->y);
350 if (e->x || e->y)
352 e->button = vHidd_Mouse_NoButton;
353 e->type = vHidd_Mouse_Motion;
355 data->mouse_callback(data->callbackdata, e);
358 buttonstate = mouse_data[0] & 0x07;
360 if ((buttonstate & LEFT_BUTTON) != (data->buttonstate & LEFT_BUTTON))
362 e->button = vHidd_Mouse_Button1;
363 e->type = (buttonstate & LEFT_BUTTON) ? vHidd_Mouse_Press : vHidd_Mouse_Release;
365 data->mouse_callback(data->callbackdata, e);
368 if ((buttonstate & RIGHT_BUTTON) != (data->buttonstate & RIGHT_BUTTON))
370 e->button = vHidd_Mouse_Button2;
371 e->type = (buttonstate & RIGHT_BUTTON) ? vHidd_Mouse_Press : vHidd_Mouse_Release;
373 data->mouse_callback(data->callbackdata, e);
376 if ((buttonstate & MIDDLE_BUTTON) != (data->buttonstate & MIDDLE_BUTTON))
378 e->button = vHidd_Mouse_Button3;
379 e->type = (buttonstate & MIDDLE_BUTTON) ? vHidd_Mouse_Press : vHidd_Mouse_Release;
381 data->mouse_callback(data->callbackdata, e);
384 data->buttonstate = buttonstate;
386 #if INTELLIMOUSE_SUPPORT
387 /* mouse wheel */
388 e->y = (mouse_data[3] & 8) ? (mouse_data[3] & 15) - 16 : (mouse_data[3] & 15);
389 if (e->y)
391 e->x = 0;
392 e->type = vHidd_Mouse_WheelMotion;
393 e->button = vHidd_Mouse_NoButton;
395 data->mouse_callback(data->callbackdata, e);
397 #endif
399 } /* for(; ((info = kbd_read_statues()) & KBD_STATUS_OBF) && work; work--) */
401 if (!work)
403 D(bug("kbd.hidd: controller jammed (0x%02X).\n", info));
408 /****************************************************************************************/
410 #undef SysBase
411 #define SysBase (*(struct ExecBase **)4L)
413 #define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT)
414 #define AUX_INTS_ON (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT)
416 /****************************************************************************************/
418 int mouse_ps2reset(struct mouse_data *data)
421 * The commands are for the mouse and nobody else.
424 kbd_write_command_w(KBD_CTRLCMD_MOUSE_ENABLE);
426 if (!kbd_detect_aux())
427 return 0;
430 * Unfortunatley on my computer these commands cause
431 * the mouse not to work at all if they are issued
432 * in a different order. So please keep it that way.
433 * - Stefan
437 * Turn interrupts off and the keyboard as well since the
438 * commands are all for the mouse.
440 kbd_write_cmd(AUX_INTS_OFF);
441 kbd_write_command_w(KBD_CTRLCMD_KBD_DISABLE);
443 data->u.ps2.mouse_protocol = PS2_PROTOCOL_STANDARD;
444 data->u.ps2.mouse_packetsize = 3;
446 #if INTELLIMOUSE_SUPPORT
447 if (detect_intellimouse())
449 data->u.ps2.mouse_protocol = PS2_PROTOCOL_INTELLIMOUSE;
450 data->u.ps2.mouse_packetsize = 4;
452 #endif
455 * Now the commands themselves.
457 aux_write(KBD_OUTCMD_SET_RATE);
458 aux_write(100);
459 aux_write(KBD_OUTCMD_SET_RES);
460 aux_write(2);
461 aux_write(KBD_OUTCMD_SET_SCALE11);
463 /* Enable Aux device */
465 kbd_write_command_w(KBD_CTRLCMD_KBD_ENABLE);
466 aux_write(KBD_OUTCMD_ENABLE);
467 kbd_write_cmd(AUX_INTS_ON);
470 * According to the specs there is an external
471 * latch that holds the level-sensitive interrupt
472 * request until the CPU readsport 0x60.
473 * If this is not read then the mouse does not
474 * work on my computer.- Stefan
477 kbd_read_data();
479 D(bug("[Mouse] Found and initialized PS/2 mouse!\n"));
481 return 1;
484 /****************************************************************************************/