BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / bus_managers / ps2 / ps2_dev.cpp
blob455ca5fd32e5f6d01910a0633d4952a09e25d2cb
1 /*
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)
8 */
11 /*! PS/2 bus manager */
13 #include "ps2_dev.h"
14 #include "ps2_service.h"
16 #include "ps2_alps.h"
17 #include "ps2_elantech.h"
18 #include "ps2_standard_mouse.h"
19 #include "ps2_synaptics.h"
20 #include "ps2_trackpoint.h"
22 #include <fs/devfs.h>
24 #include <string.h>
27 ps2_dev ps2_device[PS2_DEVICE_COUNT];
30 status_t
31 ps2_reset_mouse(ps2_dev* dev)
33 uint8 data[2];
34 status_t status;
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",
44 data[0], data[1]);
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",
50 data[0], data[1]);
51 status = B_ERROR;
52 } else if (status != B_OK) {
53 TRACE("ps2: reset mouse failed\n");
54 } else {
55 TRACE("ps2: reset mouse success\n");
58 return status;
62 status_t
63 ps2_dev_detect_pointing(ps2_dev* dev, device_hooks** hooks)
65 status_t status = ps2_reset_mouse(dev);
66 if (status != B_OK) {
67 INFO("ps2: reset failed\n");
68 return B_ERROR;
71 // probe devices
72 // the probe function has to set the dev name and the dev packet size
74 status = probe_trackpoint(dev);
75 if (status == B_OK) {
76 *hooks = &gStandardMouseDeviceHooks;
77 goto dev_found;
80 status = probe_synaptics(dev);
81 if (status == B_OK) {
82 *hooks = &gSynapticsDeviceHooks;
83 goto dev_found;
86 status = probe_alps(dev);
87 if (status == B_OK) {
88 *hooks = &gALPSDeviceHooks;
89 goto dev_found;
92 #if 0
93 status = probe_elantech(dev);
94 if (status == B_OK) {
95 *hooks = &gElantechDeviceHooks;
96 goto dev_found;
98 #endif
100 // reset the mouse for the case that the previous probes leaf the mouse in
101 // a undefined state
102 status = ps2_reset_mouse(dev);
103 if (status != B_OK) {
104 INFO("ps2: reset after probe failed\n");
105 return B_ERROR;
108 status = probe_standard_mouse(dev);
109 if (status == B_OK) {
110 *hooks = &gStandardMouseDeviceHooks;
111 goto dev_found;
114 return B_ERROR;
116 dev_found:
117 if (dev == &(ps2_device[PS2_DEVICE_SYN_PASSTHROUGH]))
118 synaptics_pass_through_set_packet_size(dev, dev->packet_size);
120 return B_OK;
124 status_t
125 ps2_dev_init(void)
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;
162 int i;
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)
167 goto err;
169 return B_OK;
171 err:
172 ps2_dev_exit();
174 return B_ERROR;
178 void
179 ps2_dev_exit(void)
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;
191 void
192 ps2_dev_publish(ps2_dev* dev)
194 status_t status = B_OK;
195 TRACE("ps2: ps2_dev_publish %s\n", dev->name);
197 if (dev->active)
198 return;
200 if (atomic_get(&dev->flags) & PS2_FLAG_KEYB) {
201 status = devfs_publish_device(dev->name, &gKeyboardDeviceHooks);
202 } else {
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) {
211 status = B_BUSY;
212 break;
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) {
222 device_hooks* hooks;
223 status = ps2_dev_detect_pointing(dev, &hooks);
224 if (status == B_OK) {
225 status = devfs_publish_device(dev->name, hooks);
230 dev->active = true;
232 INFO("ps2: devfs_publish_device %s, status = 0x%08" B_PRIx32 "\n",
233 dev->name, status);
237 void
238 ps2_dev_unpublish(ps2_dev* dev)
240 status_t status;
241 TRACE("ps2: ps2_dev_unpublish %s\n", dev->name);
243 if (!dev->active)
244 return;
246 dev->active = false;
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",
254 dev->name, status);
258 int32
259 ps2_dev_handle_int(ps2_dev* dev)
261 const uint8 data = dev->history[0].data;
262 uint32 flags;
264 flags = atomic_get(&dev->flags);
266 if (flags & PS2_FLAG_CMD) {
267 if ((flags & (PS2_FLAG_ACK | PS2_FLAG_NACK)) == 0) {
268 int cnt = 1;
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"
276 // command
277 TRACE("ps2: ps2_dev_handle_int: mouse didn't ack the 'get id' "
278 "command\n");
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);
286 cnt++;
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);
298 cnt++;
301 } else {
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;
318 } else {
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;
327 pass_to_handler:
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);
338 if (dev->active) {
339 INFO("ps2: device %s still active, republishing...\n",
340 dev->name);
341 ps2_service_notify_device_republish(dev);
342 } else {
343 ps2_service_notify_device_added(dev);
345 return B_INVOKE_SCHEDULER;
349 if (!dev->active) {
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);
368 status_t
369 standard_command_timeout(ps2_dev* dev, uint8 cmd, const uint8* out,
370 int out_count, uint8* in, int in_count, bigtime_t timeout)
372 status_t res;
373 bigtime_t start;
374 int32 sem_count;
375 int i;
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",
385 sem_count);
386 if (sem_count > 0)
387 acquire_sem_etc(dev->result_sem, sem_count, 0, 0);
388 else
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;
396 res = B_OK;
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)) {
405 uint8 prefix_cmd;
406 if (gActiveMultiplexingEnabled)
407 prefix_cmd = 0x90 + dev->idx;
408 else
409 prefix_cmd = 0xd4;
410 res = ps2_wait_write();
411 if (res == B_OK)
412 ps2_write_ctrl(prefix_cmd);
415 res = ps2_wait_write();
416 if (res == B_OK) {
417 if (i == -1) {
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);
422 else
423 atomic_or(&dev->flags, PS2_FLAG_CMD);
424 ps2_write_data(cmd);
425 } else {
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);
435 if (res != B_OK)
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);
447 res = B_IO_ERROR;
448 TRACE("ps2: ps2_dev_command got NACK\n");
451 if (res != B_OK)
452 break;
455 if (res == B_OK) {
456 if (in_count == 0) {
457 atomic_and(&dev->flags, ~PS2_FLAG_CMD);
458 } else {
459 start = system_time();
460 res = acquire_sem_etc(dev->result_sem, 1, B_RELATIVE_TIMEOUT,
461 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;
470 res = B_IO_ERROR;
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);
483 return res;
487 status_t
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,
492 4000000);
496 status_t
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);
504 status_t
505 ps2_dev_sliced_command(ps2_dev* dev, uint8 cmd)
507 uint8 val;
508 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK)
509 return B_ERROR;
511 val = (cmd >> 6) & 3;
512 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
513 return B_ERROR;
515 val = (cmd >> 4) & 3;
516 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
517 return B_ERROR;
519 val = (cmd >> 2) & 3;
520 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
521 return B_ERROR;
523 val = cmd & 3;
524 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
525 return B_ERROR;
527 return B_OK;