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
;
93 status
= probe_elantech(dev
);
95 *hooks
= &gElantechDeviceHooks
;
100 // reset the mouse for the case that the previous probes leaf the mouse in
102 status
= ps2_reset_mouse(dev
);
103 if (status
!= B_OK
) {
104 INFO("ps2: reset after probe failed\n");
108 status
= probe_standard_mouse(dev
);
109 if (status
== B_OK
) {
110 *hooks
= &gStandardMouseDeviceHooks
;
117 if (dev
== &(ps2_device
[PS2_DEVICE_SYN_PASSTHROUGH
]))
118 synaptics_pass_through_set_packet_size(dev
, dev
->packet_size
);
127 ps2_device
[0].name
= "input/mouse/ps2/0";
128 ps2_device
[0].active
= false;
129 ps2_device
[0].idx
= 0;
130 ps2_device
[0].result_sem
= -1;
131 ps2_device
[0].command
= standard_command_timeout
;
133 ps2_device
[1].name
= "input/mouse/ps2/1";
134 ps2_device
[1].active
= false;
135 ps2_device
[1].idx
= 1;
136 ps2_device
[1].result_sem
= -1;
137 ps2_device
[1].command
= standard_command_timeout
;
139 ps2_device
[2].name
= "input/mouse/ps2/2";
140 ps2_device
[2].active
= false;
141 ps2_device
[2].idx
= 2;
142 ps2_device
[2].result_sem
= -1;
143 ps2_device
[2].command
= standard_command_timeout
;
145 ps2_device
[3].name
= "input/mouse/ps2/3";
146 ps2_device
[3].active
= false;
147 ps2_device
[3].idx
= 3;
148 ps2_device
[3].result_sem
= -1;
149 ps2_device
[3].command
= standard_command_timeout
;
151 ps2_device
[4].name
= "input/mouse/ps2/synaptics_passthrough";
152 ps2_device
[4].active
= false;
153 ps2_device
[4].result_sem
= -1;
154 ps2_device
[4].command
= passthrough_command
;
156 ps2_device
[5].name
= "input/keyboard/at/0";
157 ps2_device
[5].active
= false;
158 ps2_device
[5].result_sem
= -1;
159 ps2_device
[5].flags
= PS2_FLAG_KEYB
;
160 ps2_device
[5].command
= standard_command_timeout
;
163 for (i
= 0; i
< PS2_DEVICE_COUNT
; i
++) {
164 ps2_dev
* dev
= &ps2_device
[i
];
165 dev
->result_sem
= create_sem(0, "ps2 result");
166 if (dev
->result_sem
< 0)
181 for (int i
= 0; i
< PS2_DEVICE_COUNT
; i
++) {
182 ps2_dev
* dev
= &ps2_device
[i
];
183 if (dev
->result_sem
>= 0) {
184 delete_sem(dev
->result_sem
);
185 dev
->result_sem
= -1;
192 ps2_dev_publish(ps2_dev
* dev
)
194 status_t status
= B_OK
;
195 TRACE("ps2: ps2_dev_publish %s\n", dev
->name
);
200 if (atomic_get(&dev
->flags
) & PS2_FLAG_KEYB
) {
201 status
= devfs_publish_device(dev
->name
, &gKeyboardDeviceHooks
);
203 // Check if this is the "pass-through" device and wait until
204 // the parent_dev goes to enabled state. It is required to prevent
205 // from messing up the Synaptics command sequences in synaptics_open.
206 if (dev
->parent_dev
) {
207 const bigtime_t timeout
= 2000000;
208 bigtime_t start
= system_time();
209 while (!(atomic_get(&dev
->parent_dev
->flags
) & PS2_FLAG_ENABLED
)) {
210 if ((system_time() - start
) > timeout
) {
214 snooze(timeout
/ 20);
216 TRACE("ps2: publishing %s: parent %s is %s; wait time %" B_PRId64
217 "\n", dev
->name
, dev
->parent_dev
->name
,
218 status
== B_OK
? "enabled" : "busy", system_time() - start
);
221 if (status
== B_OK
) {
223 status
= ps2_dev_detect_pointing(dev
, &hooks
);
224 if (status
== B_OK
) {
225 status
= devfs_publish_device(dev
->name
, hooks
);
232 INFO("ps2: devfs_publish_device %s, status = 0x%08" B_PRIx32
"\n",
238 ps2_dev_unpublish(ps2_dev
* dev
)
241 TRACE("ps2: ps2_dev_unpublish %s\n", dev
->name
);
248 status
= devfs_unpublish_device(dev
->name
, true);
250 if ((dev
->flags
& PS2_FLAG_ENABLED
) && dev
->disconnect
)
251 dev
->disconnect(dev
);
253 INFO("ps2: devfs_unpublish_device %s, status = 0x%08" B_PRIx32
"\n",
259 ps2_dev_handle_int(ps2_dev
* dev
)
261 const uint8 data
= dev
->history
[0].data
;
264 flags
= atomic_get(&dev
->flags
);
266 if (flags
& PS2_FLAG_CMD
) {
267 if ((flags
& (PS2_FLAG_ACK
| PS2_FLAG_NACK
)) == 0) {
269 if (data
== PS2_REPLY_ACK
) {
270 atomic_or(&dev
->flags
, PS2_FLAG_ACK
);
271 } else if (data
== PS2_REPLY_RESEND
|| data
== PS2_REPLY_ERROR
) {
272 atomic_or(&dev
->flags
, PS2_FLAG_NACK
);
273 } else if ((flags
& PS2_FLAG_GETID
)
274 && (data
== 0 || data
== 3 || data
== 4)) {
275 // workaround for broken mice that don't ack the "get id"
277 TRACE("ps2: ps2_dev_handle_int: mouse didn't ack the 'get id' "
279 atomic_or(&dev
->flags
, PS2_FLAG_ACK
);
280 if (dev
->result_buf_cnt
) {
281 dev
->result_buf
[dev
->result_buf_idx
] = data
;
282 dev
->result_buf_idx
++;
283 dev
->result_buf_cnt
--;
284 if (dev
->result_buf_cnt
== 0) {
285 atomic_and(&dev
->flags
, ~PS2_FLAG_CMD
);
289 } else if ((flags
& PS2_FLAG_RESEND
)) {
290 TRACE("ps2: ps2_dev_handle_int: processing RESEND request\n");
291 atomic_or(&dev
->flags
, PS2_FLAG_ACK
);
292 if (dev
->result_buf_cnt
) {
293 dev
->result_buf
[dev
->result_buf_idx
] = data
;
294 dev
->result_buf_idx
++;
295 dev
->result_buf_cnt
--;
296 if (dev
->result_buf_cnt
== 0) {
297 atomic_and(&dev
->flags
, ~PS2_FLAG_CMD
);
302 // TRACE("ps2: ps2_dev_handle_int unexpected data 0x%02x while "
303 // "waiting for ack\n", data);
304 TRACE("ps2: int1 %02x\n", data
);
305 goto pass_to_handler
;
307 release_sem_etc(dev
->result_sem
, cnt
, B_DO_NOT_RESCHEDULE
);
308 return B_INVOKE_SCHEDULER
;
309 } else if (dev
->result_buf_cnt
) {
310 dev
->result_buf
[dev
->result_buf_idx
] = data
;
311 dev
->result_buf_idx
++;
312 dev
->result_buf_cnt
--;
313 if (dev
->result_buf_cnt
== 0) {
314 atomic_and(&dev
->flags
, ~PS2_FLAG_CMD
);
315 release_sem_etc(dev
->result_sem
, 1, B_DO_NOT_RESCHEDULE
);
316 return B_INVOKE_SCHEDULER
;
319 // TRACE("ps2: ps2_dev_handle_int unexpected data 0x%02x during "
320 // "command processing\n", data);
321 TRACE("ps2: int2 %02x\n", data
);
322 goto pass_to_handler
;
324 return B_HANDLED_INTERRUPT
;
329 if ((flags
& PS2_FLAG_KEYB
) == 0) {
330 if (dev
->history
[0].error
&& data
== 0xfd) {
331 INFO("ps2: hot removal of %s\n", dev
->name
);
332 ps2_service_notify_device_removed(dev
);
333 return B_INVOKE_SCHEDULER
;
335 if (data
== 0x00 && dev
->history
[1].data
== 0xaa
336 && (dev
->history
[0].time
- dev
->history
[1].time
) < 50000) {
337 INFO("ps2: hot plugin of %s\n", dev
->name
);
339 INFO("ps2: device %s still active, republishing...\n",
341 ps2_service_notify_device_republish(dev
);
343 ps2_service_notify_device_added(dev
);
345 return B_INVOKE_SCHEDULER
;
350 TRACE("ps2: %s not active, data 0x%02x dropped\n", dev
->name
, data
);
351 if (data
!= 0x00 && data
!= 0xaa) {
352 INFO("ps2: possibly a hot plugin of %s\n", dev
->name
);
353 ps2_service_notify_device_added(dev
);
354 return B_INVOKE_SCHEDULER
;
356 return B_HANDLED_INTERRUPT
;
359 if ((flags
& PS2_FLAG_ENABLED
) == 0) {
360 TRACE("ps2: %s not enabled, data 0x%02x dropped\n", dev
->name
, data
);
361 return B_HANDLED_INTERRUPT
;
364 return dev
->handle_int(dev
);
369 standard_command_timeout(ps2_dev
* dev
, uint8 cmd
, const uint8
* out
,
370 int out_count
, uint8
* in
, int in_count
, bigtime_t timeout
)
377 TRACE("ps2: ps2_dev_command cmd 0x%02x, out-count %d, in-count %d, "
378 "dev %s\n", cmd
, out_count
, in_count
, dev
->name
);
379 for (i
= 0; i
< out_count
; i
++)
380 TRACE("ps2: ps2_dev_command tx: 0x%02x\n", out
[i
]);
382 res
= get_sem_count(dev
->result_sem
, &sem_count
);
383 if (res
== B_OK
&& sem_count
!= 0) {
384 TRACE("ps2: ps2_dev_command: sem_count %" B_PRId32
", fixing!\n",
387 acquire_sem_etc(dev
->result_sem
, sem_count
, 0, 0);
389 release_sem_etc(dev
->result_sem
, -sem_count
, 0);
392 dev
->result_buf_cnt
= in_count
;
393 dev
->result_buf_idx
= 0;
394 dev
->result_buf
= in
;
397 for (i
= -1; res
== B_OK
&& i
< out_count
; i
++) {
399 atomic_and(&dev
->flags
,
400 ~(PS2_FLAG_ACK
| PS2_FLAG_NACK
| PS2_FLAG_GETID
| PS2_FLAG_RESEND
));
402 acquire_sem(gControllerSem
);
404 if (!(atomic_get(&dev
->flags
) & PS2_FLAG_KEYB
)) {
406 if (gActiveMultiplexingEnabled
)
407 prefix_cmd
= 0x90 + dev
->idx
;
410 res
= ps2_wait_write();
412 ps2_write_ctrl(prefix_cmd
);
415 res
= ps2_wait_write();
418 if (cmd
== PS2_CMD_GET_DEVICE_ID
)
419 atomic_or(&dev
->flags
, PS2_FLAG_CMD
| PS2_FLAG_GETID
);
420 else if (cmd
== PS2_CMD_RESEND
)
421 atomic_or(&dev
->flags
, PS2_FLAG_CMD
| PS2_FLAG_RESEND
);
423 atomic_or(&dev
->flags
, PS2_FLAG_CMD
);
426 ps2_write_data(out
[i
]);
430 release_sem(gControllerSem
);
432 start
= system_time();
433 res
= acquire_sem_etc(dev
->result_sem
, 1, B_RELATIVE_TIMEOUT
, timeout
);
436 atomic_and(&dev
->flags
, ~PS2_FLAG_CMD
);
438 TRACE("ps2: ps2_dev_command wait for ack res 0x%08" B_PRIx32
", "
439 "wait-time %" B_PRId64
"\n", res
, system_time() - start
);
441 if (atomic_get(&dev
->flags
) & PS2_FLAG_ACK
) {
442 TRACE("ps2: ps2_dev_command got ACK\n");
445 if (atomic_get(&dev
->flags
) & PS2_FLAG_NACK
) {
446 atomic_and(&dev
->flags
, ~PS2_FLAG_CMD
);
448 TRACE("ps2: ps2_dev_command got NACK\n");
457 atomic_and(&dev
->flags
, ~PS2_FLAG_CMD
);
459 start
= system_time();
460 res
= acquire_sem_etc(dev
->result_sem
, 1, B_RELATIVE_TIMEOUT
,
463 atomic_and(&dev
->flags
, ~PS2_FLAG_CMD
);
465 if (dev
->result_buf_cnt
!= 0) {
466 TRACE("ps2: ps2_dev_command error: %d rx bytes not received\n",
467 dev
->result_buf_cnt
);
468 in_count
-= dev
->result_buf_cnt
;
469 dev
->result_buf_cnt
= 0;
473 TRACE("ps2: ps2_dev_command wait for input res 0x%08" B_PRIx32
", "
474 "wait-time %" B_PRId64
"\n", res
, system_time() - start
);
476 for (i
= 0; i
< in_count
; i
++)
477 TRACE("ps2: ps2_dev_command rx: 0x%02x\n", in
[i
]);
481 TRACE("ps2: ps2_dev_command result 0x%08" B_PRIx32
"\n", res
);
488 ps2_dev_command(ps2_dev
* dev
, uint8 cmd
, const uint8
* out
, int out_count
,
489 uint8
* in
, int in_count
)
491 return ps2_dev_command_timeout(dev
, cmd
, out
, out_count
, in
, in_count
,
497 ps2_dev_command_timeout(ps2_dev
* dev
, uint8 cmd
, const uint8
* out
,
498 int out_count
, uint8
* in
, int in_count
, bigtime_t timeout
)
500 return dev
->command(dev
, cmd
, out
, out_count
, in
, in_count
, timeout
);
505 ps2_dev_sliced_command(ps2_dev
* dev
, uint8 cmd
)
508 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_SCALE11
) != B_OK
)
511 val
= (cmd
>> 6) & 3;
512 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_RES
, &val
, 1) != B_OK
)
515 val
= (cmd
>> 4) & 3;
516 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_RES
, &val
, 1) != B_OK
)
519 val
= (cmd
>> 2) & 3;
520 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_RES
, &val
, 1) != B_OK
)
524 if (ps2_dev_command(dev
, PS2_CMD_MOUSE_SET_RES
, &val
, 1) != B_OK
)