2 * Copyright 2001-2014 Haiku, Inc. All rights reserved.
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
11 * John Scipione, jscipione@gmail.com
15 /*! PS/2 mouse device driver
17 A PS/2 mouse is connected to the IBM 8042 controller, and gets its
18 name from the IBM PS/2 personal computer, which was the first to
19 use this device. All resources are shared between the keyboard, and
20 the mouse, referred to as the "Auxiliary Device".
24 The controller has 3 I/O registers:
25 1. Status (input), mapped to port 64h
26 2. Control (output), mapped to port 64h
27 3. Data (input/output), mapped to port 60h
31 A packet read from the mouse data port is composed of
33 byte 0: status byte, where
34 - bit 7: Y overflow (1 = true)
35 - bit 6: X overflow (1 = true)
36 - bit 5: MSB of Y offset
37 - bit 4: MSB of X offset
38 - bit 3: Syncronization bit (always 1)
39 - bit 2: Middle button (1 = down)
40 - bit 1: Right button (1 = down)
41 - bit 0: Left button (1 = down)
42 byte 1: X position change, since last probed (-127 to +127)
43 byte 2: Y position change, since last probed (-127 to +127)
45 Intellimouse mice send a four byte packet, where the first three
46 bytes are the same as standard mice, and the last one reports the
47 Z position, which is, usually, the wheel movement.
51 The PS/2 mouse device is connected to interrupt 12.
52 The controller uses 3 consecutive interrupts to inform the computer
53 that it has new data. On the first the data register holds the status
54 byte, on the second the X offset, and on the 3rd the Y offset.
61 #include <keyboard_mouse_driver.h>
63 #include "ps2_service.h"
64 #include "ps2_standard_mouse.h"
67 const char* kStandardMousePath
[4] = {
68 "input/mouse/ps2/standard_0",
69 "input/mouse/ps2/standard_1",
70 "input/mouse/ps2/standard_2",
71 "input/mouse/ps2/standard_3"
74 const char* kIntelliMousePath
[4] = {
75 "input/mouse/ps2/intelli_0",
76 "input/mouse/ps2/intelli_1",
77 "input/mouse/ps2/intelli_2",
78 "input/mouse/ps2/intelli_3"
82 //! Set sampling rate of the ps2 port.
83 static inline status_t
84 ps2_set_sample_rate(ps2_dev
* dev
, uint8 rate
)
86 return ps2_dev_command(dev
, PS2_CMD_SET_SAMPLE_RATE
, &rate
, 1, NULL
, 0);
90 //! 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
130 TRACE("packet: %02x %02x %02x %02x: xd %d, yd %d, 0x%x (%d), w-xd %d, "
131 "w-yd %d\n", packet
[0], packet
[1], packet
[2], packet
[3],
132 xDelta
, yDelta
, buttons
, cookie
->click_count
, xDeltaWheel
,
137 pos
->xdelta
= xDelta
;
138 pos
->ydelta
= yDelta
;
139 pos
->buttons
= buttons
;
140 pos
->clicks
= cookie
->click_count
;
142 pos
->timestamp
= currentTime
;
143 pos
->wheel_ydelta
= yDeltaWheel
;
144 pos
->wheel_xdelta
= xDeltaWheel
;
146 TRACE("ps2: ps2_packet_to_movement xdelta: %d, ydelta: %d, buttons %x, "
147 "clicks: %d, timestamp %Ld\n",
148 xDelta
, yDelta
, buttons
, cookie
->click_count
, currentTime
);
153 //! Read a mouse event from the mouse events chain buffer.
155 standard_mouse_read_event(standard_mouse_cookie
* cookie
,
156 mouse_movement
* movement
)
158 uint8 packet
[PS2_MAX_PACKET_SIZE
];
161 TRACE("ps2: standard_mouse_read_event\n");
163 status
= acquire_sem_etc(cookie
->standard_mouse_sem
, 1, B_CAN_INTERRUPT
, 0);
164 TRACE("ps2: standard_mouse_read_event acquired\n");
168 if (!cookie
->dev
->active
) {
169 TRACE("ps2: standard_mouse_read_event: Error device no longer "
174 if (packet_buffer_read(cookie
->standard_mouse_buffer
, packet
,
175 cookie
->dev
->packet_size
) != cookie
->dev
->packet_size
) {
176 TRACE("ps2: error copying buffer\n");
180 if (!(packet
[0] & 8))
181 panic("ps2: got broken data from packet_buffer_read\n");
183 ps2_packet_to_movement(cookie
, packet
, movement
);
188 // #pragma mark - Interrupt handler functions
192 standard_mouse_disconnect(ps2_dev
* dev
)
194 // the mouse device might not be opened at this point
195 INFO("ps2: ps2_standard_mouse_disconnect %s\n", dev
->name
);
196 if (dev
->flags
& PS2_FLAG_OPEN
)
197 release_sem(((standard_mouse_cookie
*)dev
->cookie
)->standard_mouse_sem
);
201 /*! Interrupt handler for the mouse device. Called whenever the I/O
202 controller generates an interrupt for the PS/2 mouse. Reads mouse
203 information from the data port, and stores it, so it can be accessed
204 by read() operations. The full data is obtained using 3 consecutive
205 calls to the handler, each holds a different byte on the data port.
208 standard_mouse_handle_int(ps2_dev
* dev
)
210 standard_mouse_cookie
* cookie
= (standard_mouse_cookie
*)dev
->cookie
;
211 const uint8 data
= dev
->history
[0].data
;
213 TRACE("ps2: standard mouse: %1x\t%1x\t%1x\t%1x\t%1x\t%1x\t%1x\t%1x\n",
214 data
>> 7 & 1, data
>> 6 & 1, data
>> 5 & 1,
215 data
>> 4 & 1, data
>> 3 & 1, data
>> 2 & 1,
216 data
>> 1 & 1, data
>> 0 & 1);
218 if (cookie
->packet_index
== 0 && (data
& 8) == 0) {
219 INFO("ps2: bad mouse data, trying resync\n");
220 return B_HANDLED_INTERRUPT
;
223 // Workarounds for active multiplexing keyboard controllers
224 // that lose data or send them to the wrong port.
225 if (cookie
->packet_index
== 0 && (data
& 0xc0) != 0) {
226 INFO("ps2: strange mouse data, x/y overflow, trying resync\n");
227 return B_HANDLED_INTERRUPT
;
230 if (cookie
->packet_index
== 1) {
232 = ((cookie
->buffer
[0] & 0x10) ? 0xFFFFFF00 : 0) | data
;
233 if (xDelta
< -100 || xDelta
> 100) {
234 INFO("ps2: strange mouse data, x-delta %d, trying resync\n",
236 cookie
->packet_index
= 0;
237 return B_HANDLED_INTERRUPT
;
240 if (cookie
->packet_index
== 2) {
242 = ((cookie
->buffer
[0] & 0x20) ? 0xFFFFFF00 : 0) | data
;
243 if (yDelta
< -100 || yDelta
> 100) {
244 INFO("ps2: strange mouse data, y-delta %d, trying resync\n",
246 cookie
->packet_index
= 0;
247 return B_HANDLED_INTERRUPT
;
251 cookie
->buffer
[cookie
->packet_index
++] = data
;
253 if (cookie
->packet_index
!= dev
->packet_size
) {
254 // packet not yet complete
255 return B_HANDLED_INTERRUPT
;
258 // complete packet is assembled
260 cookie
->packet_index
= 0;
261 if (packet_buffer_write(cookie
->standard_mouse_buffer
,
262 cookie
->buffer
, dev
->packet_size
) != dev
->packet_size
) {
263 // buffer is full, drop new data
264 return B_HANDLED_INTERRUPT
;
267 release_sem_etc(cookie
->standard_mouse_sem
, 1, B_DO_NOT_RESCHEDULE
);
269 return B_INVOKE_SCHEDULER
;
273 // #pragma mark - probe_standard_mouse()
277 probe_standard_mouse(ps2_dev
* dev
)
283 status
= ps2_dev_command(dev
, PS2_CMD_GET_DEVICE_ID
, NULL
, 0,
285 if (status
!= B_OK
) {
286 INFO("ps2: probe_mouse get device id failed\n");
290 TRACE("ps2: probe_mouse device id: %2x\n", deviceId
);
292 // check for MS Intellimouse
294 uint8 alternate_device_id
;
295 status
= ps2_set_sample_rate(dev
, 200);
296 status
|= ps2_set_sample_rate(dev
, 100);
297 status
|= ps2_set_sample_rate(dev
, 80);
298 status
|= ps2_dev_command(dev
, PS2_CMD_GET_DEVICE_ID
, NULL
, 0,
299 &alternate_device_id
, 1);
301 TRACE("ps2: probe_mouse alternate device id: %2x\n",
302 alternate_device_id
);
303 deviceId
= alternate_device_id
;
307 if (deviceId
== PS2_DEV_ID_STANDARD
308 || deviceId
== PS2_DEV_ID_TOUCHPAD_RICATECH
) {
309 INFO("ps2: probe_mouse Standard PS/2 mouse found\n");
310 dev
->name
= kStandardMousePath
[dev
->idx
];
311 dev
->packet_size
= PS2_PACKET_STANDARD
;
312 } else if (deviceId
== PS2_DEV_ID_INTELLIMOUSE
) {
313 dev
->name
= kIntelliMousePath
[dev
->idx
];
314 dev
->packet_size
= PS2_PACKET_INTELLIMOUSE
;
315 INFO("ps2: probe_mouse Extended PS/2 mouse found\n");
317 INFO("ps2: probe_mouse Error unknown device id.\n");
325 // #pragma mark - Device functions
329 standard_mouse_open(const char* name
, uint32 flags
, void** _cookie
)
331 standard_mouse_cookie
* cookie
;
336 TRACE("ps2: standard_mouse_open %s\n", name
);
338 for (dev
= NULL
, i
= 0; i
< PS2_DEVICE_COUNT
; i
++) {
339 if (0 == strcmp(ps2_device
[i
].name
, name
)) {
340 dev
= &ps2_device
[i
];
344 if (0 == strcmp(g_passthrough_dev
.name
, name
)) {
345 dev
= &g_passthrough_dev
;
346 isSynapticsPTDevice
= true;
353 TRACE("ps2: dev = NULL\n");
357 if (atomic_or(&dev
->flags
, PS2_FLAG_OPEN
) & PS2_FLAG_OPEN
)
360 cookie
= (standard_mouse_cookie
*)malloc(sizeof(standard_mouse_cookie
));
365 memset(cookie
, 0, sizeof(*cookie
));
368 dev
->cookie
= cookie
;
369 dev
->disconnect
= &standard_mouse_disconnect
;
370 dev
->handle_int
= &standard_mouse_handle_int
;
372 if (strstr(dev
->name
, "standard") != NULL
)
373 cookie
->flags
= F_MOUSE_TYPE_STANDARD
;
375 if (strstr(dev
->name
, "intelli") != NULL
)
376 cookie
->flags
= F_MOUSE_TYPE_INTELLIMOUSE
;
378 cookie
->standard_mouse_buffer
379 = create_packet_buffer(MOUSE_HISTORY_SIZE
* dev
->packet_size
);
380 if (cookie
->standard_mouse_buffer
== NULL
) {
381 TRACE("ps2: can't allocate mouse actions buffer\n");
385 // create the mouse semaphore, used for synchronization between
386 // the interrupt handler and the read operation
387 cookie
->standard_mouse_sem
= create_sem(0, "ps2_standard_mouse_sem");
388 if (cookie
->standard_mouse_sem
< 0) {
389 TRACE("ps2: failed creating PS/2 mouse semaphore!\n");
393 status
= ps2_dev_command(dev
, PS2_CMD_ENABLE
, NULL
, 0, NULL
, 0);
395 INFO("ps2: cannot enable mouse %s\n", name
);
399 atomic_or(&dev
->flags
, PS2_FLAG_ENABLED
);
402 TRACE("ps2: standard_mouse_open %s success\n", name
);
406 delete_sem(cookie
->standard_mouse_sem
);
408 delete_packet_buffer(cookie
->standard_mouse_buffer
);
412 atomic_and(&dev
->flags
, ~PS2_FLAG_OPEN
);
414 TRACE("ps2: standard_mouse_open %s failed\n", name
);
420 standard_mouse_close(void* _cookie
)
422 standard_mouse_cookie
* cookie
= (standard_mouse_cookie
*)_cookie
;
424 TRACE("ps2: standard_mouse_close %s enter\n", cookie
->dev
->name
);
426 ps2_dev_command_timeout(cookie
->dev
, PS2_CMD_DISABLE
, NULL
, 0, NULL
, 0,
429 delete_packet_buffer(cookie
->standard_mouse_buffer
);
430 delete_sem(cookie
->standard_mouse_sem
);
432 atomic_and(&cookie
->dev
->flags
, ~PS2_FLAG_OPEN
);
433 atomic_and(&cookie
->dev
->flags
, ~PS2_FLAG_ENABLED
);
435 TRACE("ps2: standard_mouse_close %s done\n", cookie
->dev
->name
);
441 standard_mouse_freecookie(void* _cookie
)
443 standard_mouse_cookie
* cookie
= (standard_mouse_cookie
*)_cookie
;
450 standard_mouse_read(void* cookie
, off_t pos
, void* buffer
, size_t* _length
)
453 return B_NOT_ALLOWED
;
458 standard_mouse_write(void* cookie
, off_t pos
, const void* buffer
,
462 return B_NOT_ALLOWED
;
467 standard_mouse_ioctl(void* _cookie
, uint32 op
, void* buffer
, size_t length
)
469 standard_mouse_cookie
* cookie
= (standard_mouse_cookie
*)_cookie
;
475 TRACE("ps2: ioctl MS_NUM_EVENTS\n");
476 get_sem_count(cookie
->standard_mouse_sem
, &count
);
482 mouse_movement movement
;
484 TRACE("ps2: ioctl MS_READ\n");
485 if ((status
= standard_mouse_read_event(cookie
, &movement
)) < B_OK
)
487 // TRACE("%s %d %d %d %d\n", cookie->dev->name,
488 // movement.xdelta, movement.ydelta, movement.buttons,
490 return user_memcpy(buffer
, &movement
, sizeof(movement
));
494 TRACE("ps2: ioctl MS_SET_TYPE not implemented\n");
498 TRACE("ps2: ioctl MS_SET_MAP (set mouse mapping) not "
503 TRACE("ps2: ioctl MS_GET_ACCEL (get mouse acceleration) not "
508 TRACE("ps2: ioctl MS_SET_ACCEL (set mouse acceleration) not "
512 case MS_SET_CLICKSPEED
:
513 TRACE("ps2: ioctl MS_SETCLICK (set click speed)\n");
514 return user_memcpy(&cookie
->click_speed
, buffer
, sizeof(bigtime_t
));
517 TRACE("ps2: ioctl unknown mouse opcode: %ld\n", op
);
518 return B_DEV_INVALID_IOCTL
;
523 device_hooks gStandardMouseDeviceHooks
= {
525 standard_mouse_close
,
526 standard_mouse_freecookie
,
527 standard_mouse_ioctl
,
529 standard_mouse_write
,