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 cookie
->buffer
[cookie
->packet_index
++] = data
;
232 if (cookie
->packet_index
!= dev
->packet_size
) {
233 // packet not yet complete
234 return B_HANDLED_INTERRUPT
;
237 // complete packet is assembled
239 cookie
->packet_index
= 0;
240 if (packet_buffer_write(cookie
->standard_mouse_buffer
,
241 cookie
->buffer
, dev
->packet_size
) != dev
->packet_size
) {
242 // buffer is full, drop new data
243 return B_HANDLED_INTERRUPT
;
246 release_sem_etc(cookie
->standard_mouse_sem
, 1, B_DO_NOT_RESCHEDULE
);
248 return B_INVOKE_SCHEDULER
;
252 // #pragma mark - probe_standard_mouse()
256 probe_standard_mouse(ps2_dev
* dev
)
262 status
= ps2_dev_command(dev
, PS2_CMD_GET_DEVICE_ID
, NULL
, 0,
264 if (status
!= B_OK
) {
265 INFO("ps2: probe_mouse get device id failed\n");
269 TRACE("ps2: probe_mouse device id: %2x\n", deviceId
);
271 // check for MS Intellimouse
273 uint8 alternate_device_id
;
274 status
= ps2_set_sample_rate(dev
, 200);
275 status
|= ps2_set_sample_rate(dev
, 100);
276 status
|= ps2_set_sample_rate(dev
, 80);
277 status
|= ps2_dev_command(dev
, PS2_CMD_GET_DEVICE_ID
, NULL
, 0,
278 &alternate_device_id
, 1);
280 TRACE("ps2: probe_mouse alternate device id: %2x\n",
281 alternate_device_id
);
282 deviceId
= alternate_device_id
;
286 if (deviceId
== PS2_DEV_ID_STANDARD
287 || deviceId
== PS2_DEV_ID_TOUCHPAD_RICATECH
) {
288 INFO("ps2: probe_mouse Standard PS/2 mouse found\n");
289 dev
->name
= kStandardMousePath
[dev
->idx
];
290 dev
->packet_size
= PS2_PACKET_STANDARD
;
291 } else if (deviceId
== PS2_DEV_ID_INTELLIMOUSE
) {
292 dev
->name
= kIntelliMousePath
[dev
->idx
];
293 dev
->packet_size
= PS2_PACKET_INTELLIMOUSE
;
294 INFO("ps2: probe_mouse Extended PS/2 mouse found\n");
296 INFO("ps2: probe_mouse Error unknown device id.\n");
304 // #pragma mark - Device functions
308 standard_mouse_open(const char* name
, uint32 flags
, void** _cookie
)
310 standard_mouse_cookie
* cookie
;
315 TRACE("ps2: standard_mouse_open %s\n", name
);
317 for (dev
= NULL
, i
= 0; i
< PS2_DEVICE_COUNT
; i
++) {
318 if (0 == strcmp(ps2_device
[i
].name
, name
)) {
319 dev
= &ps2_device
[i
];
323 if (0 == strcmp(g_passthrough_dev
.name
, name
)) {
324 dev
= &g_passthrough_dev
;
325 isSynapticsPTDevice
= true;
332 TRACE("ps2: dev = NULL\n");
336 if (atomic_or(&dev
->flags
, PS2_FLAG_OPEN
) & PS2_FLAG_OPEN
)
339 cookie
= (standard_mouse_cookie
*)malloc(sizeof(standard_mouse_cookie
));
344 memset(cookie
, 0, sizeof(*cookie
));
347 dev
->cookie
= cookie
;
348 dev
->disconnect
= &standard_mouse_disconnect
;
349 dev
->handle_int
= &standard_mouse_handle_int
;
351 if (strstr(dev
->name
, "standard") != NULL
)
352 cookie
->flags
= F_MOUSE_TYPE_STANDARD
;
354 if (strstr(dev
->name
, "intelli") != NULL
)
355 cookie
->flags
= F_MOUSE_TYPE_INTELLIMOUSE
;
357 cookie
->standard_mouse_buffer
358 = create_packet_buffer(MOUSE_HISTORY_SIZE
* dev
->packet_size
);
359 if (cookie
->standard_mouse_buffer
== NULL
) {
360 TRACE("ps2: can't allocate mouse actions buffer\n");
364 // create the mouse semaphore, used for synchronization between
365 // the interrupt handler and the read operation
366 cookie
->standard_mouse_sem
= create_sem(0, "ps2_standard_mouse_sem");
367 if (cookie
->standard_mouse_sem
< 0) {
368 TRACE("ps2: failed creating PS/2 mouse semaphore!\n");
372 status
= ps2_dev_command(dev
, PS2_CMD_ENABLE
, NULL
, 0, NULL
, 0);
374 INFO("ps2: cannot enable mouse %s\n", name
);
378 atomic_or(&dev
->flags
, PS2_FLAG_ENABLED
);
381 TRACE("ps2: standard_mouse_open %s success\n", name
);
385 delete_sem(cookie
->standard_mouse_sem
);
387 delete_packet_buffer(cookie
->standard_mouse_buffer
);
391 atomic_and(&dev
->flags
, ~PS2_FLAG_OPEN
);
393 TRACE("ps2: standard_mouse_open %s failed\n", name
);
399 standard_mouse_close(void* _cookie
)
401 standard_mouse_cookie
* cookie
= (standard_mouse_cookie
*)_cookie
;
403 TRACE("ps2: standard_mouse_close %s enter\n", cookie
->dev
->name
);
405 ps2_dev_command_timeout(cookie
->dev
, PS2_CMD_DISABLE
, NULL
, 0, NULL
, 0,
408 delete_packet_buffer(cookie
->standard_mouse_buffer
);
409 delete_sem(cookie
->standard_mouse_sem
);
411 atomic_and(&cookie
->dev
->flags
, ~PS2_FLAG_OPEN
);
412 atomic_and(&cookie
->dev
->flags
, ~PS2_FLAG_ENABLED
);
414 TRACE("ps2: standard_mouse_close %s done\n", cookie
->dev
->name
);
420 standard_mouse_freecookie(void* _cookie
)
422 standard_mouse_cookie
* cookie
= (standard_mouse_cookie
*)_cookie
;
429 standard_mouse_read(void* cookie
, off_t pos
, void* buffer
, size_t* _length
)
432 return B_NOT_ALLOWED
;
437 standard_mouse_write(void* cookie
, off_t pos
, const void* buffer
,
441 return B_NOT_ALLOWED
;
446 standard_mouse_ioctl(void* _cookie
, uint32 op
, void* buffer
, size_t length
)
448 standard_mouse_cookie
* cookie
= (standard_mouse_cookie
*)_cookie
;
454 TRACE("ps2: ioctl MS_NUM_EVENTS\n");
455 get_sem_count(cookie
->standard_mouse_sem
, &count
);
461 mouse_movement movement
;
463 TRACE("ps2: ioctl MS_READ\n");
464 if ((status
= standard_mouse_read_event(cookie
, &movement
)) < B_OK
)
466 // TRACE("%s %d %d %d %d\n", cookie->dev->name,
467 // movement.xdelta, movement.ydelta, movement.buttons,
469 return user_memcpy(buffer
, &movement
, sizeof(movement
));
473 TRACE("ps2: ioctl MS_SET_TYPE not implemented\n");
477 TRACE("ps2: ioctl MS_SET_MAP (set mouse mapping) not "
482 TRACE("ps2: ioctl MS_GET_ACCEL (get mouse acceleration) not "
487 TRACE("ps2: ioctl MS_SET_ACCEL (set mouse acceleration) not "
491 case MS_SET_CLICKSPEED
:
492 TRACE("ps2: ioctl MS_SETCLICK (set click speed)\n");
493 return user_memcpy(&cookie
->click_speed
, buffer
, sizeof(bigtime_t
));
496 TRACE("ps2: ioctl unknown mouse opcode: %ld\n", op
);
497 return B_DEV_INVALID_IOCTL
;
502 device_hooks gStandardMouseDeviceHooks
= {
504 standard_mouse_close
,
505 standard_mouse_freecookie
,
506 standard_mouse_ioctl
,
508 standard_mouse_write
,