On some machines (e.g. the Acer AspireOne A110), the first value
[AROS.git] / rom / hidds / i8042 / drv_ps2.c
blobf76db322eab4aa6efc7888b61e08f6370b809c10
1 /*
2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: PS/2 mouse driver.
6 Lang: English.
7 */
9 /****************************************************************************************/
11 #include <proto/exec.h>
12 #include <proto/kernel.h>
13 #include <proto/utility.h>
14 #include <proto/oop.h>
15 #include <oop/oop.h>
16 #include <hidd/hidd.h>
17 #include <hidd/mouse.h>
18 #include <devices/inputevent.h>
20 #include "mouse.h"
21 #include "kbd_common.h"
23 #define DEBUG 0
24 #include <aros/debug.h>
26 /****************************************************************************************/
28 /* defines for buttonstate */
30 #define LEFT_BUTTON 1
31 #define RIGHT_BUTTON 2
32 #define MIDDLE_BUTTON 4
34 /****************************************************************************************/
36 int mouse_ps2reset(struct mouse_data *);
38 /****************************************************************************************/
40 static void mouse_ps2int(struct mouse_data *data, void *unused)
42 struct pHidd_Mouse_Event *e = &data->event;
43 UWORD buttonstate;
44 WORD work = 10000;
45 UBYTE info, mousecode, *mouse_data;
47 info = kbd_read_status();
49 for(; ((info = kbd_read_status()) & KBD_STATUS_OBF) && work; work--)
51 if (!(info & KBD_STATUS_MOUSE_OBF))
54 ** Data from keyboard. Hopefully this gets through to keyboard interrupt
55 ** if we break out of for loop here :-\
57 break;
60 mousecode = kbd_read_input();
62 if (info & (KBD_STATUS_GTO | KBD_STATUS_PERR))
64 /* Ignore errors and messages for keyboard -> eat status/error byte */
65 continue;
68 /* Mouse Packet Byte */
70 mouse_data = data->mouse_data;
72 data->expected_mouse_acks = 0;
73 mouse_data[data->mouse_collected_bytes] = mousecode;
75 /* Packet validity check. Bit 3 of first mouse packet byte must be set */
77 if ((mouse_data[0] & 8) == 0)
79 data->mouse_collected_bytes = 0;
80 continue;
83 data->mouse_collected_bytes++;
85 if (data->mouse_collected_bytes != data->mouse_packetsize)
87 /* Mouse Packet not yet complete */
88 continue;
91 /* We have a complete mouse packet :-) */
93 data->mouse_collected_bytes = 0;
96 * Let's see whether these data can be right...
98 * D7 D6 D5 D4 D3 D2 D1 D0
99 * YV XV YS XS 1 M R L
100 * X7 . . . . . . X1 (X)
101 * Y7 . . . . . . Y1 (Y)
103 * XV,YV : overflow in x/y direction
104 * XS,YS : most significant bit of X and Y: represents sign
105 * X,Y : displacement in x and y direction (8 least significant bits).
107 * X, XS, Y and YS make up two 9-bit two's complement fields.
110 #if 0
111 D(bug("Got the following: 1. byte: 0x%x, dx=%d, dy=%d\n",
112 mouse_data[0],
113 mouse_data[1],
114 mouse_data[2]));
115 #endif
117 e->x = mouse_data[1];
118 e->y = mouse_data[2];
120 if (mouse_data[0] & 0x10) e->x -= 256;
121 if (mouse_data[0] & 0x20) e->y -= 256;
123 /* dy is reversed! */
124 e->y = -(e->y);
126 if (e->x || e->y)
128 e->button = vHidd_Mouse_NoButton;
129 e->type = vHidd_Mouse_Motion;
131 data->mouse_callback(data->callbackdata, e);
134 buttonstate = mouse_data[0] & 0x07;
136 if ((buttonstate & LEFT_BUTTON) != (data->buttonstate & LEFT_BUTTON))
138 e->button = vHidd_Mouse_Button1;
139 e->type = (buttonstate & LEFT_BUTTON) ? vHidd_Mouse_Press : vHidd_Mouse_Release;
141 data->mouse_callback(data->callbackdata, e);
144 if ((buttonstate & RIGHT_BUTTON) != (data->buttonstate & RIGHT_BUTTON))
146 e->button = vHidd_Mouse_Button2;
147 e->type = (buttonstate & RIGHT_BUTTON) ? vHidd_Mouse_Press : vHidd_Mouse_Release;
149 data->mouse_callback(data->callbackdata, e);
152 if ((buttonstate & MIDDLE_BUTTON) != (data->buttonstate & MIDDLE_BUTTON))
154 e->button = vHidd_Mouse_Button3;
155 e->type = (buttonstate & MIDDLE_BUTTON) ? vHidd_Mouse_Press : vHidd_Mouse_Release;
157 data->mouse_callback(data->callbackdata, e);
160 data->buttonstate = buttonstate;
162 #if INTELLIMOUSE_SUPPORT
163 /* mouse wheel */
164 e->y = (mouse_data[3] & 8) ? (mouse_data[3] & 15) - 16 : (mouse_data[3] & 15);
165 if (e->y)
167 e->x = 0;
168 e->type = vHidd_Mouse_WheelMotion;
169 e->button = vHidd_Mouse_NoButton;
171 data->mouse_callback(data->callbackdata, e);
173 #endif
175 } /* for(; ((info = kbd_read_status()) & KBD_STATUS_OBF) && work; work--) */
177 D(if (!work) bug("mouse.hidd: controller jammed (0x%02X).\n", info);)
180 /****************************************************************************************/
182 int test_mouse_ps2(OOP_Class *cl, OOP_Object *o)
184 struct mouse_data *data = OOP_INST_DATA(cl, o);
185 int result;
187 data->irq = KrnAddIRQHandler(12, mouse_ps2int, data, NULL);
189 Disable();
190 result = mouse_ps2reset(data);
191 Enable();
193 /* If mouse_ps2reset() returned non-zero value, there is valid PS/2 mouse */
194 if (result)
196 return 1;
198 /* Either no PS/2 mouse or problems with it */
199 /* Remove mouse interrupt */
200 KrnRemIRQHandler(data->irq);
202 /* Report no PS/2 mouse */
203 return 0;
206 /****************************************************************************************/
208 void getps2State(OOP_Class *cl, OOP_Object *o, struct pHidd_Mouse_Event *event)
210 #if 0
211 struct mouse_data *data = OOP_INST_DATA(cl, o);
212 UBYTE ack;
214 /* The following doesn't seem to do anything useful */
215 aux_write(KBD_OUTCMD_DISABLE);
216 /* switch to remote mode */
217 aux_write(KBD_OUTCMD_SET_REMOTE_MODE);
218 /* we want data */
219 ack = data->expected_mouse_acks+1;
220 aux_write(KBD_OUTCMD_READ_DATA);
221 while (data->expected_mouse_acks>=ack)
222 kbd_usleep(1000);
223 /* switch back to stream mode */
224 aux_write(KBD_OUTCMD_SET_STREAM_MODE);
225 aux_write(KBD_OUTCMD_ENABLE);
226 #endif
229 /****************************************************************************************/
231 static int detect_aux()
233 int loops = 10;
234 int retval = 0;
236 kb_wait(1000);
238 kbd_write_command(KBD_CTRLCMD_WRITE_AUX_OBUF);
240 kb_wait(1000);
241 kbd_write_output(0x5a);
245 unsigned char status = kbd_read_status();
247 if (status & KBD_STATUS_OBF)
249 (void) kbd_read_input();
250 if (status & KBD_STATUS_MOUSE_OBF)
252 retval = 1;
254 break;
257 kbd_usleep(1000);
259 } while (--loops);
261 D(bug("PS/2 Auxilliary port %sdetected\n", retval ? "" : "not "));
262 return retval;
265 /****************************************************************************************/
267 static int query_mouse(UBYTE *buf, int size, int timeout)
269 int ret = 0;
273 UBYTE status = kbd_read_status();
275 if (status & KBD_STATUS_OBF)
277 UBYTE c = kbd_read_input();
279 if ((c != KBD_REPLY_ACK) && (status & KBD_STATUS_MOUSE_OBF))
281 buf[ret++] = c;
284 else
286 kbd_usleep(1000);
289 } while ((--timeout) && (ret < size));
291 return ret;
295 /****************************************************************************************/
297 static int detect_intellimouse(void)
299 UBYTE id = 0;
301 /* Try to switch into IMPS2 mode */
303 aux_write_ack(KBD_OUTCMD_SET_RATE);
304 aux_write_ack(200);
305 aux_write_ack(KBD_OUTCMD_SET_RATE);
306 aux_write_ack(100);
307 aux_write_ack(KBD_OUTCMD_SET_RATE);
308 aux_write_ack(80);
309 aux_write_ack(KBD_OUTCMD_GET_ID);
310 aux_write_noack(KBD_OUTCMD_GET_ID);
312 query_mouse(&id, 1, 20);
314 return ((id == 3) || (id == 4)) ? id : 0;
317 /****************************************************************************************/
319 #define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT)
320 #define AUX_INTS_ON (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT)
322 /****************************************************************************************/
324 int mouse_ps2reset(struct mouse_data *data)
326 int result, timeout = 100;
329 * The commands are for the mouse and nobody else.
332 kbd_write_command_w(KBD_CTRLCMD_MOUSE_ENABLE);
335 * Check for a mouse port.
337 if (!detect_aux())
338 return 0;
341 * Unfortunately on my computer these commands cause
342 * the mouse not to work at all if they are issued
343 * in a different order. So please keep it that way.
344 * - Stefan
348 * Turn interrupts off and the keyboard as well since the
349 * commands are all for the mouse.
351 kbd_write_cmd(AUX_INTS_OFF);
352 kbd_write_command_w(KBD_CTRLCMD_KBD_DISABLE);
354 /* Reset mouse */
355 aux_write_ack(KBD_OUTCMD_RESET);
356 result = aux_wait_for_input(); /* Test result (0xAA) */
357 while (result == 0xfa && --timeout)
359 /* somehow the ACK isn't always swallowed above */
360 kbd_usleep(1000);
361 result = aux_wait_for_input();
363 aux_wait_for_input(); /* Mouse type */
365 if (result != 0xaa)
367 /* No mouse. Re-enable keyboard and return failure */
368 kbd_write_command_w(KBD_CTRLCMD_KBD_ENABLE);
369 aux_write_ack(KBD_OUTCMD_ENABLE);
370 return 0;
373 data->mouse_protocol = PS2_PROTOCOL_STANDARD;
374 data->mouse_packetsize = 3;
376 #if INTELLIMOUSE_SUPPORT
377 if (detect_intellimouse())
379 D(bug("[Mouse] PS/2 Intellimouse detected\n"));
380 data->mouse_protocol = PS2_PROTOCOL_INTELLIMOUSE;
381 data->mouse_packetsize = 4;
383 #endif
386 * Now the commands themselves.
388 aux_write_ack(KBD_OUTCMD_SET_RATE);
389 aux_write_ack(100);
390 aux_write_ack(KBD_OUTCMD_SET_RES);
391 aux_write_ack(2);
392 aux_write_ack(KBD_OUTCMD_SET_SCALE11);
394 /* Enable Aux device (and re-enable keyboard) */
396 kbd_write_command_w(KBD_CTRLCMD_KBD_ENABLE);
397 aux_write_ack(KBD_OUTCMD_ENABLE);
398 kbd_write_cmd(AUX_INTS_ON);
401 * According to the specs there is an external
402 * latch that holds the level-sensitive interrupt
403 * request until the CPU reads port 0x60.
404 * If this is not read then the mouse does not
405 * work on my computer.- Stefan
408 kbd_read_data();
410 D(bug("[Mouse] Found and initialized PS/2 mouse!\n"));
412 return 1;
415 /****************************************************************************************/