2 * Copyright 2005-2014 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors (in chronological order):
6 * Marcus Overhagen (marcus@overhagen.de)
7 * Clemens Zeidler (haiku@clemens-zeidler.de)
11 /*! PS/2 bus manager */
14 #include "ps2_service.h"
17 #include "ps2_elantech.h"
18 #include "ps2_standard_mouse.h"
19 #include "ps2_synaptics.h"
20 #include "ps2_trackpoint.h"
27 ps2_dev ps2_device
[PS2_DEVICE_COUNT
];
31 ps2_reset_mouse(ps2_dev
* dev
)
36 TRACE("ps2: ps2_reset_mouse\n");
38 status
= ps2_dev_command(dev
, PS2_CMD_RESET
, NULL
, 0, data
, 2);
40 if (status
== B_OK
&& data
[0] == 0xFE && data
[1] == 0xAA) {
41 // workaround for HP/Compaq KBCs timeout condition. #2867 #3594 #4315
42 TRACE("ps2: KBC has timed out the mouse reset request. "
43 "Response was: 0x%02x 0x%02x. Requesting the answer data.\n",
45 status
= ps2_dev_command(dev
, PS2_CMD_RESEND
, NULL
, 0, data
, 2);
48 if (status
== B_OK
&& data
[0] != 0xAA && data
[1] != 0x00) {
49 TRACE("ps2: reset mouse failed, response was: 0x%02x 0x%02x\n",
52 } else if (status
!= B_OK
) {
53 TRACE("ps2: reset mouse failed\n");
55 TRACE("ps2: reset mouse success\n");
63 ps2_dev_detect_pointing(ps2_dev
* dev
, device_hooks
** hooks
)
65 status_t status
= ps2_reset_mouse(dev
);
67 INFO("ps2: reset failed\n");
72 // the probe function has to set the dev name and the dev packet size
74 status
= probe_trackpoint(dev
);
76 *hooks
= &gStandardMouseDeviceHooks
;
80 status
= probe_synaptics(dev
);
82 *hooks
= &gSynapticsDeviceHooks
;
86 status
= probe_alps(dev
);
88 *hooks
= &gALPSDeviceHooks
;
92 status
= probe_elantech(dev
);
94 *hooks
= &gElantechDeviceHooks
;
98 // reset the mouse for the case that the previous probes left the mouse in
100 status
= ps2_reset_mouse(dev
);
101 if (status
!= B_OK
) {
102 INFO("ps2: reset after probe failed\n");
106 status
= probe_standard_mouse(dev
);
107 if (status
== B_OK
) {
108 *hooks
= &gStandardMouseDeviceHooks
;
115 if (dev
== &(ps2_device
[PS2_DEVICE_SYN_PASSTHROUGH
]))
116 synaptics_pass_through_set_packet_size(dev
, dev
->packet_size
);
125 ps2_device
[0].name
= "input/mouse/ps2/0";
126 ps2_device
[0].active
= false;
127 ps2_device
[0].idx
= 0;
128 ps2_device
[0].result_sem
= -1;
129 ps2_device
[0].command
= standard_command_timeout
;
131 ps2_device
[1].name
= "input/mouse/ps2/1";
132 ps2_device
[1].active
= false;
133 ps2_device
[1].idx
= 1;
134 ps2_device
[1].result_sem
= -1;
135 ps2_device
[1].command
= standard_command_timeout
;
137 ps2_device
[2].name
= "input/mouse/ps2/2";
138 ps2_device
[2].active
= false;
139 ps2_device
[2].idx
= 2;
140 ps2_device
[2].result_sem
= -1;
141 ps2_device
[2].command
= standard_command_timeout
;
143 ps2_device
[3].name
= "input/mouse/ps2/3";
144 ps2_device
[3].active
= false;
145 ps2_device
[3].idx
= 3;
146 ps2_device
[3].result_sem
= -1;
147 ps2_device
[3].command
= standard_command_timeout
;
149 ps2_device
[4].name
= "input/mouse/ps2/synaptics_passthrough";
150 ps2_device
[4].active
= false;
151 ps2_device
[4].result_sem
= -1;
152 ps2_device
[4].command
= passthrough_command
;
154 ps2_device
[5].name
= "input/keyboard/at/0";
155 ps2_device
[5].active
= false;
156 ps2_device
[5].result_sem
= -1;
157 ps2_device
[5].flags
= PS2_FLAG_KEYB
;
158 ps2_device
[5].command
= standard_command_timeout
;
161 for (i
= 0; i
< PS2_DEVICE_COUNT
; i
++) {
162 ps2_dev
* dev
= &ps2_device
[i
];
163 dev
->result_sem
= create_sem(0, "ps2 result");
164 if (dev
->result_sem
< 0)
179 for (int i
= 0; i
< PS2_DEVICE_COUNT
; i
++) {
180 ps2_dev
* dev
= &ps2_device
[i
];
181 if (dev
->result_sem
>= 0) {
182 delete_sem(dev
->result_sem
);
183 dev
->result_sem
= -1;
190 ps2_dev_publish(ps2_dev
* dev
)
192 status_t status
= B_OK
;
193 TRACE("ps2: ps2_dev_publish %s\n", dev
->name
);
198 if (atomic_get(&dev
->flags
) & PS2_FLAG_KEYB
) {
199 status
= devfs_publish_device(dev
->name
, &gKeyboardDeviceHooks
);
201 // Check if this is the "pass-through" device and wait until
202 // the parent_dev goes to enabled state. It is required to prevent
203 // from messing up the Synaptics command sequences in synaptics_open.
204 if (dev
->parent_dev
) {
205 const bigtime_t timeout
= 2000000;
206 bigtime_t start
= system_time();
207 while (!(atomic_get(&dev
->parent_dev
->flags
) & PS2_FLAG_ENABLED
)) {
208 if ((system_time() - start
) > timeout
) {
212 snooze(timeout
/ 20);
214 TRACE("ps2: publishing %s: parent %s is %s; wait time %" B_PRId64
215 "\n", dev
->name
, dev
->parent_dev
->name
,
216 status
== B_OK
? "enabled" : "busy", system_time() - start
);
219 if (status
== B_OK
) {
221 status
= ps2_dev_detect_pointing(dev
, &hooks
);
222 if (status
== B_OK
) {
223 status
= devfs_publish_device(dev
->name
, hooks
);
230 INFO("ps2: devfs_publish_device %s, status = 0x%08" B_PRIx32
"\n",
236 ps2_dev_unpublish(ps2_dev
* dev
)
239 TRACE("ps2: ps2_dev_unpublish %s\n", dev
->name
);
246 status
= devfs_unpublish_device(dev
->name
, true);
248 if ((dev
->flags
& PS2_FLAG_ENABLED
) && dev
->disconnect
)
249 dev
->disconnect(dev
);
251 INFO("ps2: devfs_unpublish_device %s, status = 0x%08" B_PRIx32
"\n",
257 ps2_dev_handle_int(ps2_dev
* dev
)
259 const uint8 data
= dev
->history
[0].data
;
262 flags
= atomic_get(&dev
->flags
);
264 if (flags
& PS2_FLAG_CMD
) {
265 if ((flags
& (PS2_FLAG_ACK
| PS2_FLAG_NACK
)) == 0) {
267 if (data
== PS2_REPLY_ACK
) {
268 atomic_or(&dev
->flags
, PS2_FLAG_ACK
);
269 } else if (data
== PS2_REPLY_RESEND
|| data
== PS2_REPLY_ERROR
) {
270 atomic_or(&dev
->flags
, PS2_FLAG_NACK
);
271 } else if ((flags
& PS2_FLAG_GETID
)
272 && (data
== 0 || data
== 3 || data
== 4)) {
273 // workaround for broken mice that don't ack the "get id"
275 TRACE("ps2: ps2_dev_handle_int: mouse didn't ack the 'get id' "
277 atomic_or(&dev
->flags
, PS2_FLAG_ACK
);
278 if (dev
->result_buf_cnt
) {
279 dev
->result_buf
[dev
->result_buf_idx
] = data
;
280 dev
->result_buf_idx
++;
281 dev
->result_buf_cnt
--;
282 if (dev
->result_buf_cnt
== 0) {
283 atomic_and(&dev
->flags
, ~PS2_FLAG_CMD
);
287 } else if ((flags
& PS2_FLAG_RESEND
)) {
288 TRACE("ps2: ps2_dev_handle_int: processing RESEND request\n");
289 atomic_or(&dev
->flags
, PS2_FLAG_ACK
);
290 if (dev
->result_buf_cnt
) {
291 dev
->result_buf
[dev
->result_buf_idx
] = data
;
292 dev
->result_buf_idx
++;
293 dev
->result_buf_cnt
--;
294 if (dev
->result_buf_cnt
== 0) {
295 atomic_and(&dev
->flags
, ~PS2_FLAG_CMD
);
300 // TRACE("ps2: ps2_dev_handle_int unexpected data 0x%02x while "
301 // "waiting for ack\n", data);
302 TRACE("ps2: int1 %02x\n", data
);
303 goto pass_to_handler
;
305 release_sem_etc(dev
->result_sem
, cnt
, B_DO_NOT_RESCHEDULE
);
306 return B_INVOKE_SCHEDULER
;
307 } else if (dev
->result_buf_cnt
) {
308 dev
->result_buf
[dev
->result_buf_idx
] = data
;
309 dev
->result_buf_idx
++;
310 dev
->result_buf_cnt
--;
311 if (dev
->result_buf_cnt
== 0) {
312 atomic_and(&dev
->flags
, ~PS2_FLAG_CMD
);
313 release_sem_etc(dev
->result_sem
, 1, B_DO_NOT_RESCHEDULE
);
314 return B_INVOKE_SCHEDULER
;
317 // TRACE("ps2: ps2_dev_handle_int unexpected data 0x%02x during "
318 // "command processing\n", data);
319 TRACE("ps2: int2 %02x\n", data
);
320 goto pass_to_handler
;
322 return B_HANDLED_INTERRUPT
;
327 if ((flags
& PS2_FLAG_KEYB
) == 0) {
328 if (dev
->history
[0].error
&& data
== 0xfd) {
329 INFO("ps2: hot removal of %s\n", dev
->name
);
330 ps2_service_notify_device_removed(dev
);
331 return B_INVOKE_SCHEDULER
;
333 if (data
== 0x00 && dev
->history
[1].data
== 0xaa
334 && (dev
->history
[0].time
- dev
->history
[1].time
) < 50000) {
335 INFO("ps2: hot plugin of %s\n", dev
->name
);
337 INFO("ps2: device %s still active, republishing...\n",
339 ps2_service_notify_device_republish(dev
);
341 ps2_service_notify_device_added(dev
);
343 return B_INVOKE_SCHEDULER
;
348 TRACE("ps2: %s not active, data 0x%02x dropped\n", dev
->name
, data
);
349 if (data
!= 0x00 && data
!= 0xaa) {
350 INFO("ps2: possibly a hot plugin of %s\n", dev
->name
);
351 ps2_service_notify_device_added(dev
);
352 return B_INVOKE_SCHEDULER
;
354 return B_HANDLED_INTERRUPT
;
357 if ((flags
& PS2_FLAG_ENABLED
) == 0) {
358 TRACE("ps2: %s not enabled, data 0x%02x dropped\n", dev
->name
, data
);
359 return B_HANDLED_INTERRUPT
;
362 return dev
->handle_int(dev
);
367 standard_command_timeout(ps2_dev
* dev
, uint8 cmd
, const uint8
* out
,
368 int out_count
, uint8
* in
, int in_count
, bigtime_t timeout
)
375 TRACE("ps2: ps2_dev_command cmd 0x%02x, out-count %d, in-count %d, "
376 "dev %s\n", cmd
, out_count
, in_count
, dev
->name
);
377 for (i
= 0; i
< out_count
; i
++)
378 TRACE("ps2: ps2_dev_command tx: 0x%02x\n", out
[i
]);
380 res
= get_sem_count(dev
->result_sem
, &sem_count
);
381 if (res
== B_OK
&& sem_count
!= 0) {
382 TRACE("ps2: ps2_dev_command: sem_count %" B_PRId32
", fixing!\n",
385 acquire_sem_etc(dev
->result_sem
, sem_count
, 0, 0);
387 release_sem_etc(dev
->result_sem
, -sem_count
, 0);
390 dev
->result_buf_cnt
= in_count
;
391 dev
->result_buf_idx
= 0;
392 dev
->result_buf
= in
;
395 for (i
= -1; res
== B_OK
&& i
< out_count
; i
++) {
397 atomic_and(&dev
->flags
,
398 ~(PS2_FLAG_ACK
| PS2_FLAG_NACK
| PS2_FLAG_GETID
| PS2_FLAG_RESEND
));
400 acquire_sem(gControllerSem
);
402 if (!(atomic_get(&dev
->flags
) & PS2_FLAG_KEYB
)) {
404 if (gActiveMultiplexingEnabled
)
405 prefix_cmd
= 0x90 + dev
->idx
;
408 res
= ps2_wait_write();
410 ps2_write_ctrl(prefix_cmd
);
413 res
= ps2_wait_write();
416 if (cmd
== PS2_CMD_GET_DEVICE_ID
)
417 atomic_or(&dev
->flags
, PS2_FLAG_CMD
| PS2_FLAG_GETID
);
418 else if (cmd
== PS2_CMD_RESEND
)
419 atomic_or(&dev
->flags
, PS2_FLAG_CMD
| PS2_FLAG_RESEND
);
421 atomic_or(&dev
->flags
, PS2_FLAG_CMD
);
424 ps2_write_data(out
[i
]);
428 release_sem(gControllerSem
);
430 start
= system_time();
431 res
= acquire_sem_etc(dev
->result_sem
, 1, B_RELATIVE_TIMEOUT
, timeout
);
434 atomic_and(&dev
->flags
, ~PS2_FLAG_CMD
);
436 TRACE("ps2: ps2_dev_command wait for ack res 0x%08" B_PRIx32
", "
437 "wait-time %" B_PRId64
"\n", res
, system_time() - start
);
439 if (atomic_get(&dev
->flags
) & PS2_FLAG_ACK
) {
440 TRACE("ps2: ps2_dev_command got ACK\n");
443 if (atomic_get(&dev
->flags
) & PS2_FLAG_NACK
) {
444 atomic_and(&dev
->flags
, ~PS2_FLAG_CMD
);
446 TRACE("ps2: ps2_dev_command got NACK\n");
455 atomic_and(&dev
->flags
, ~PS2_FLAG_CMD
);
457 start
= system_time();
458 res
= acquire_sem_etc(dev
->result_sem
, 1, B_RELATIVE_TIMEOUT
,
461 atomic_and(&dev
->flags
, ~PS2_FLAG_CMD
);
463 if (dev
->result_buf_cnt
!= 0) {
464 TRACE("ps2: ps2_dev_command error: %d rx bytes not received\n",
465 dev
->result_buf_cnt
);
466 in_count
-= dev
->result_buf_cnt
;
467 dev
->result_buf_cnt
= 0;
471 TRACE("ps2: ps2_dev_command wait for input res 0x%08" B_PRIx32
", "
472 "wait-time %" B_PRId64
"\n", res
, system_time() - start
);
474 for (i
= 0; i
< in_count
; i
++)
475 TRACE("ps2: ps2_dev_command rx: 0x%02x\n", in
[i
]);
479 TRACE("ps2: ps2_dev_command result 0x%08" B_PRIx32
"\n", res
);
486 ps2_dev_command(ps2_dev
* dev
, uint8 cmd
, const uint8
* out
, int out_count
,
487 uint8
* in
, int in_count
)
489 return ps2_dev_command_timeout(dev
, cmd
, out
, out_count
, in
, in_count
,
495 ps2_dev_command_timeout(ps2_dev
* dev
, uint8 cmd
, const uint8
* out
,
496 int out_count
, uint8
* in
, int in_count
, bigtime_t timeout
)
498 return dev
->command(dev
, cmd
, out
, out_count
, in
, in_count
, timeout
);
503 ps2_dev_sliced_command(ps2_dev
* dev
, uint8 cmd
)
506 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_SCALE11
) != B_OK
)
509 val
= (cmd
>> 6) & 3;
510 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_RES
, &val
, 1) != B_OK
)
513 val
= (cmd
>> 4) & 3;
514 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_RES
, &val
, 1) != B_OK
)
517 val
= (cmd
>> 2) & 3;
518 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_RES
, &val
, 1) != B_OK
)
522 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_RES
, &val
, 1) != B_OK
)