2 * Copyright 2001-2010 Haiku, Inc.
3 * Distributed under the terms of the MIT License.
5 * Authors (in chronological order):
6 * Elad Lahav (elad@eldarshany.com)
7 * Stefano Ceccherini (burton666@libero.it)
8 * Axel Dörfler, axeld@pinc-software.de
9 * Marcus Overhagen <marcus@overhagen.de>
10 * Clemens Zeidler <czeidler@gmx.de>
14 /*! PS/2 mouse device driver
16 A PS/2 mouse is connected to the IBM 8042 controller, and gets its
17 name from the IBM PS/2 personal computer, which was the first to
18 use this device. All resources are shared between the keyboard, and
19 the mouse, referred to as the "Auxiliary Device".
23 The controller has 3 I/O registers:
24 1. Status (input), mapped to port 64h
25 2. Control (output), mapped to port 64h
26 3. Data (input/output), mapped to port 60h
30 A packet read from the mouse data port is composed of
32 byte 0: status byte, where
33 - bit 7: Y overflow (1 = true)
34 - bit 6: X overflow (1 = true)
35 - bit 5: MSB of Y offset
36 - bit 4: MSB of X offset
37 - bit 3: Syncronization bit (always 1)
38 - bit 2: Middle button (1 = down)
39 - bit 1: Right button (1 = down)
40 - bit 0: Left button (1 = down)
41 byte 1: X position change, since last probed (-127 to +127)
42 byte 2: Y position change, since last probed (-127 to +127)
44 Intellimouse mice send a four byte packet, where the first three
45 bytes are the same as standard mice, and the last one reports the
46 Z position, which is, usually, the wheel movement.
50 The PS/2 mouse device is connected to interrupt 12.
51 The controller uses 3 consecutive interrupts to inform the computer
52 that it has new data. On the first the data register holds the status
53 byte, on the second the X offset, and on the 3rd the Y offset.
60 #include <keyboard_mouse_driver.h>
62 #include "ps2_service.h"
63 #include "ps2_standard_mouse.h"
66 const char* kStandardMousePath
[4] = {
67 "input/mouse/ps2/standard_0",
68 "input/mouse/ps2/standard_1",
69 "input/mouse/ps2/standard_2",
70 "input/mouse/ps2/standard_3"
72 const char* kIntelliMousePath
[4] = {
73 "input/mouse/ps2/intelli_0",
74 "input/mouse/ps2/intelli_1",
75 "input/mouse/ps2/intelli_2",
76 "input/mouse/ps2/intelli_3"
80 /*! Set sampling rate of the ps2 port.
82 static inline status_t
83 ps2_set_sample_rate(ps2_dev
*dev
, uint8 rate
)
85 return ps2_dev_command(dev
, PS2_CMD_SET_SAMPLE_RATE
, &rate
, 1, NULL
, 0);
89 /*! Converts a packet received by the mouse to a "movement".
92 ps2_packet_to_movement(standard_mouse_cookie
*cookie
, uint8 packet
[],
95 int buttons
= packet
[0] & 7;
96 int xDelta
= ((packet
[0] & 0x10) ? ~0xff : 0) | packet
[1];
97 int yDelta
= ((packet
[0] & 0x20) ? ~0xff : 0) | packet
[2];
100 bigtime_t currentTime
= system_time();
102 if (buttons
!= 0 && cookie
->buttons_state
== 0) {
103 if (cookie
->click_last_time
+ cookie
->click_speed
> currentTime
)
104 cookie
->click_count
++;
106 cookie
->click_count
= 1;
108 cookie
->click_last_time
= currentTime
;
111 cookie
->buttons_state
= buttons
;
113 if (cookie
->flags
& F_MOUSE_TYPE_INTELLIMOUSE
) {
114 yDeltaWheel
= packet
[3] & 0x07;
115 if (packet
[3] & 0x08)
116 yDeltaWheel
|= ~0x07;
119 if (cookie->flags & F_MOUSE_TYPE_2WHEELS) {
120 switch (packet[3] & 0x0F) {
121 case 0x01: yDeltaWheel = +1; break; // wheel 1 down
122 case 0x0F: yDeltaWheel = -1; break; // wheel 1 up
123 case 0x02: xDeltaWheel = +1; break; // wheel 2 down
124 case 0x0E: xDeltaWheel = -1; break; // wheel 2 up
129 // TRACE("packet: %02x %02x %02x %02x: xd %d, yd %d, 0x%x (%d), w-xd %d, "
130 // "w-yd %d\n", packet[0], packet[1], packet[2], packet[3],
131 // xDelta, yDelta, buttons, cookie->click_count, xDeltaWheel,
135 pos
->xdelta
= xDelta
;
136 pos
->ydelta
= yDelta
;
137 pos
->buttons
= buttons
;
138 pos
->clicks
= cookie
->click_count
;
140 pos
->timestamp
= currentTime
;
141 pos
->wheel_ydelta
= yDeltaWheel
;
142 pos
->wheel_xdelta
= xDeltaWheel
;
144 TRACE("ps2: ps2_packet_to_movement xdelta: %d, ydelta: %d, buttons %x, "
145 "clicks: %d, timestamp %Ld\n",
146 xDelta
, yDelta
, buttons
, cookie
->click_count
, currentTime
);
151 /*! Read a mouse event from the mouse events chain buffer.
154 standard_mouse_read_event(standard_mouse_cookie
*cookie
,
155 mouse_movement
*movement
)
157 uint8 packet
[PS2_MAX_PACKET_SIZE
];
160 TRACE("ps2: standard_mouse_read_event\n");
162 status
= acquire_sem_etc(cookie
->standard_mouse_sem
, 1, B_CAN_INTERRUPT
, 0);
163 TRACE("ps2: standard_mouse_read_event acquired\n");
167 if (!cookie
->dev
->active
) {
168 TRACE("ps2: standard_mouse_read_event: Error device no longer "
173 if (packet_buffer_read(cookie
->standard_mouse_buffer
, packet
,
174 cookie
->dev
->packet_size
) != cookie
->dev
->packet_size
) {
175 TRACE("ps2: error copying buffer\n");
179 if (!(packet
[0] & 8))
180 panic("ps2: got broken data from packet_buffer_read\n");
182 ps2_packet_to_movement(cookie
, packet
, movement
);
191 standard_mouse_disconnect(ps2_dev
*dev
)
193 // the mouse device might not be opened at this point
194 INFO("ps2: ps2_standard_mouse_disconnect %s\n", dev
->name
);
195 if (dev
->flags
& PS2_FLAG_OPEN
)
196 release_sem(((standard_mouse_cookie
*)dev
->cookie
)->standard_mouse_sem
);
200 /*! Interrupt handler for the mouse device. Called whenever the I/O
201 controller generates an interrupt for the PS/2 mouse. Reads mouse
202 information from the data port, and stores it, so it can be accessed
203 by read() operations. The full data is obtained using 3 consecutive
204 calls to the handler, each holds a different byte on the data port.
207 standard_mouse_handle_int(ps2_dev
*dev
)
209 standard_mouse_cookie
*cookie
= (standard_mouse_cookie
*)dev
->cookie
;
210 const uint8 data
= dev
->history
[0].data
;
212 TRACE("ps2: standard mouse: %1x\t%1x\t%1x\t%1x\t%1x\t%1x\t%1x\t%1x\n",
213 data
>> 7 & 1, data
>> 6 & 1, data
>> 5 & 1,
214 data
>> 4 & 1, data
>> 3 & 1, data
>> 2 & 1,
215 data
>> 1 & 1, data
>> 0 & 1);
217 if (cookie
->packet_index
== 0 && !(data
& 8)) {
218 INFO("ps2: bad mouse data, trying resync\n");
219 return B_HANDLED_INTERRUPT
;
222 // Workarounds for active multiplexing keyboard controllers
223 // that lose data or send them to the wrong port.
224 if (cookie
->packet_index
== 0 && (data
& 0xc0)) {
225 INFO("ps2: strange mouse data, x/y overflow, trying resync\n");
226 return B_HANDLED_INTERRUPT
;
228 if (cookie
->packet_index
== 1) {
230 = ((cookie
->buffer
[0] & 0x10) ? 0xFFFFFF00 : 0) | data
;
231 if (xDelta
< -100 || xDelta
> 100) {
232 INFO("ps2: strange mouse data, x-delta %d, trying resync\n",
234 cookie
->packet_index
= 0;
235 return B_HANDLED_INTERRUPT
;
238 if (cookie
->packet_index
== 2) {
240 = ((cookie
->buffer
[0] & 0x20) ? 0xFFFFFF00 : 0) | data
;
241 if (yDelta
< -100 || yDelta
> 100) {
242 INFO("ps2: strange mouse data, y-delta %d, trying resync\n",
244 cookie
->packet_index
= 0;
245 return B_HANDLED_INTERRUPT
;
249 cookie
->buffer
[cookie
->packet_index
++] = data
;
251 if (cookie
->packet_index
!= dev
->packet_size
) {
252 // packet not yet complete
253 return B_HANDLED_INTERRUPT
;
256 // complete packet is assembled
258 cookie
->packet_index
= 0;
259 if (packet_buffer_write(cookie
->standard_mouse_buffer
,
260 cookie
->buffer
, dev
->packet_size
) != dev
->packet_size
) {
261 // buffer is full, drop new data
262 return B_HANDLED_INTERRUPT
;
265 release_sem_etc(cookie
->standard_mouse_sem
, 1, B_DO_NOT_RESCHEDULE
);
266 return B_INVOKE_SCHEDULER
;
274 probe_standard_mouse(ps2_dev
* dev
)
280 status
= ps2_dev_command(dev
, PS2_CMD_GET_DEVICE_ID
, NULL
, 0,
282 if (status
!= B_OK
) {
283 INFO("ps2: probe_mouse get device id failed\n");
287 TRACE("ps2: probe_mouse device id: %2x\n", deviceId
);
289 // check for MS Intellimouse
291 uint8 alternate_device_id
;
292 status
= ps2_set_sample_rate(dev
, 200);
293 status
|= ps2_set_sample_rate(dev
, 100);
294 status
|= ps2_set_sample_rate(dev
, 80);
295 status
|= ps2_dev_command(dev
, PS2_CMD_GET_DEVICE_ID
, NULL
, 0,
296 &alternate_device_id
, 1);
298 TRACE("ps2: probe_mouse alternate device id: %2x\n",
299 alternate_device_id
);
300 deviceId
= alternate_device_id
;
304 if (deviceId
== PS2_DEV_ID_STANDARD
305 || deviceId
== PS2_DEV_ID_TOUCHPAD_RICATECH
) {
306 INFO("ps2: probe_mouse Standard PS/2 mouse found\n");
307 dev
->name
= kStandardMousePath
[dev
->idx
];
308 dev
->packet_size
= PS2_PACKET_STANDARD
;
309 } else if (deviceId
== PS2_DEV_ID_INTELLIMOUSE
) {
310 dev
->name
= kIntelliMousePath
[dev
->idx
];
311 dev
->packet_size
= PS2_PACKET_INTELLIMOUSE
;
312 INFO("ps2: probe_mouse Extended PS/2 mouse found\n");
314 INFO("ps2: probe_mouse Error unknown device id.\n");
322 // #pragma mark - Device functions
326 standard_mouse_open(const char *name
, uint32 flags
, void **_cookie
)
328 standard_mouse_cookie
*cookie
;
333 TRACE("ps2: standard_mouse_open %s\n", name
);
335 for (dev
= NULL
, i
= 0; i
< PS2_DEVICE_COUNT
; i
++) {
336 if (0 == strcmp(ps2_device
[i
].name
, name
)) {
337 dev
= &ps2_device
[i
];
340 /*if (0 == strcmp(g_passthrough_dev.name, name)) {
341 dev = &g_passthrough_dev;
342 isSynapticsPTDevice = true;
348 TRACE("ps2: dev = NULL\n");
352 if (atomic_or(&dev
->flags
, PS2_FLAG_OPEN
) & PS2_FLAG_OPEN
)
355 cookie
= (standard_mouse_cookie
*)malloc(sizeof(standard_mouse_cookie
));
360 memset(cookie
, 0, sizeof(*cookie
));
363 dev
->cookie
= cookie
;
364 dev
->disconnect
= &standard_mouse_disconnect
;
365 dev
->handle_int
= &standard_mouse_handle_int
;
367 if (strstr(dev
->name
, "standard") != NULL
)
368 cookie
->flags
= F_MOUSE_TYPE_STANDARD
;
370 if (strstr(dev
->name
, "intelli") != NULL
)
371 cookie
->flags
= F_MOUSE_TYPE_INTELLIMOUSE
;
373 cookie
->standard_mouse_buffer
374 = create_packet_buffer(MOUSE_HISTORY_SIZE
* dev
->packet_size
);
375 if (cookie
->standard_mouse_buffer
== NULL
) {
376 TRACE("ps2: can't allocate mouse actions buffer\n");
380 // create the mouse semaphore, used for synchronization between
381 // the interrupt handler and the read operation
382 cookie
->standard_mouse_sem
= create_sem(0, "ps2_standard_mouse_sem");
383 if (cookie
->standard_mouse_sem
< 0) {
384 TRACE("ps2: failed creating PS/2 mouse semaphore!\n");
388 status
= ps2_dev_command(dev
, PS2_CMD_ENABLE
, NULL
, 0, NULL
, 0);
390 INFO("ps2: cannot enable mouse %s\n", name
);
394 atomic_or(&dev
->flags
, PS2_FLAG_ENABLED
);
397 TRACE("ps2: standard_mouse_open %s success\n", name
);
401 delete_sem(cookie
->standard_mouse_sem
);
403 delete_packet_buffer(cookie
->standard_mouse_buffer
);
407 atomic_and(&dev
->flags
, ~PS2_FLAG_OPEN
);
409 TRACE("ps2: standard_mouse_open %s failed\n", name
);
415 standard_mouse_close(void *_cookie
)
417 standard_mouse_cookie
*cookie
= (standard_mouse_cookie
*)_cookie
;
419 TRACE("ps2: standard_mouse_close %s enter\n", cookie
->dev
->name
);
421 ps2_dev_command_timeout(cookie
->dev
, PS2_CMD_DISABLE
, NULL
, 0, NULL
, 0,
424 delete_packet_buffer(cookie
->standard_mouse_buffer
);
425 delete_sem(cookie
->standard_mouse_sem
);
427 atomic_and(&cookie
->dev
->flags
, ~PS2_FLAG_OPEN
);
428 atomic_and(&cookie
->dev
->flags
, ~PS2_FLAG_ENABLED
);
430 TRACE("ps2: standard_mouse_close %s done\n", cookie
->dev
->name
);
436 standard_mouse_freecookie(void *_cookie
)
438 standard_mouse_cookie
*cookie
= (standard_mouse_cookie
*)_cookie
;
445 standard_mouse_read(void *cookie
, off_t pos
, void *buffer
, size_t *_length
)
448 return B_NOT_ALLOWED
;
453 standard_mouse_write(void *cookie
, off_t pos
, const void *buffer
,
457 return B_NOT_ALLOWED
;
462 standard_mouse_ioctl(void *_cookie
, uint32 op
, void *buffer
, size_t length
)
464 standard_mouse_cookie
*cookie
= (standard_mouse_cookie
*)_cookie
;
470 TRACE("ps2: ioctl MS_NUM_EVENTS\n");
471 get_sem_count(cookie
->standard_mouse_sem
, &count
);
477 mouse_movement movement
;
479 TRACE("ps2: ioctl MS_READ\n");
480 if ((status
= standard_mouse_read_event(cookie
, &movement
)) < B_OK
)
482 // TRACE("%s %d %d %d %d\n", cookie->dev->name,
483 // movement.xdelta, movement.ydelta, movement.buttons,
485 return user_memcpy(buffer
, &movement
, sizeof(movement
));
489 TRACE("ps2: ioctl MS_SET_TYPE not implemented\n");
493 TRACE("ps2: ioctl MS_SET_MAP (set mouse mapping) not "
498 TRACE("ps2: ioctl MS_GET_ACCEL (get mouse acceleration) not "
503 TRACE("ps2: ioctl MS_SET_ACCEL (set mouse acceleration) not "
507 case MS_SET_CLICKSPEED
:
508 TRACE("ps2: ioctl MS_SETCLICK (set click speed)\n");
509 return user_memcpy(&cookie
->click_speed
, buffer
, sizeof(bigtime_t
));
512 TRACE("ps2: ioctl unknown mouse opcode: %ld\n", op
);
513 return B_DEV_INVALID_IOCTL
;
518 device_hooks gStandardMouseDeviceHooks
= {
520 standard_mouse_close
,
521 standard_mouse_freecookie
,
522 standard_mouse_ioctl
,
524 standard_mouse_write
,