vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / bus_managers / ps2 / ps2_dev.cpp
blobfaf7829b44d1f5b1015d5ce7ec5850f1c387d9e3
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 status = probe_elantech(dev);
93 if (status == B_OK) {
94 *hooks = &gElantechDeviceHooks;
95 goto dev_found;
98 // reset the mouse for the case that the previous probes left the mouse in
99 // a undefined state
100 status = ps2_reset_mouse(dev);
101 if (status != B_OK) {
102 INFO("ps2: reset after probe failed\n");
103 return B_ERROR;
106 status = probe_standard_mouse(dev);
107 if (status == B_OK) {
108 *hooks = &gStandardMouseDeviceHooks;
109 goto dev_found;
112 return B_ERROR;
114 dev_found:
115 if (dev == &(ps2_device[PS2_DEVICE_SYN_PASSTHROUGH]))
116 synaptics_pass_through_set_packet_size(dev, dev->packet_size);
118 return B_OK;
122 status_t
123 ps2_dev_init(void)
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;
160 int i;
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)
165 goto err;
167 return B_OK;
169 err:
170 ps2_dev_exit();
172 return B_ERROR;
176 void
177 ps2_dev_exit(void)
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;
189 void
190 ps2_dev_publish(ps2_dev* dev)
192 status_t status = B_OK;
193 TRACE("ps2: ps2_dev_publish %s\n", dev->name);
195 if (dev->active)
196 return;
198 if (atomic_get(&dev->flags) & PS2_FLAG_KEYB) {
199 status = devfs_publish_device(dev->name, &gKeyboardDeviceHooks);
200 } else {
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) {
209 status = B_BUSY;
210 break;
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) {
220 device_hooks* hooks;
221 status = ps2_dev_detect_pointing(dev, &hooks);
222 if (status == B_OK) {
223 status = devfs_publish_device(dev->name, hooks);
228 dev->active = true;
230 INFO("ps2: devfs_publish_device %s, status = 0x%08" B_PRIx32 "\n",
231 dev->name, status);
235 void
236 ps2_dev_unpublish(ps2_dev* dev)
238 status_t status;
239 TRACE("ps2: ps2_dev_unpublish %s\n", dev->name);
241 if (!dev->active)
242 return;
244 dev->active = false;
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",
252 dev->name, status);
256 int32
257 ps2_dev_handle_int(ps2_dev* dev)
259 const uint8 data = dev->history[0].data;
260 uint32 flags;
262 flags = atomic_get(&dev->flags);
264 if (flags & PS2_FLAG_CMD) {
265 if ((flags & (PS2_FLAG_ACK | PS2_FLAG_NACK)) == 0) {
266 int cnt = 1;
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"
274 // command
275 TRACE("ps2: ps2_dev_handle_int: mouse didn't ack the 'get id' "
276 "command\n");
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);
284 cnt++;
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);
296 cnt++;
299 } else {
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;
316 } else {
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;
325 pass_to_handler:
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);
336 if (dev->active) {
337 INFO("ps2: device %s still active, republishing...\n",
338 dev->name);
339 ps2_service_notify_device_republish(dev);
340 } else {
341 ps2_service_notify_device_added(dev);
343 return B_INVOKE_SCHEDULER;
347 if (!dev->active) {
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);
366 status_t
367 standard_command_timeout(ps2_dev* dev, uint8 cmd, const uint8* out,
368 int out_count, uint8* in, int in_count, bigtime_t timeout)
370 status_t res;
371 bigtime_t start;
372 int32 sem_count;
373 int i;
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",
383 sem_count);
384 if (sem_count > 0)
385 acquire_sem_etc(dev->result_sem, sem_count, 0, 0);
386 else
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;
394 res = B_OK;
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)) {
403 uint8 prefix_cmd;
404 if (gActiveMultiplexingEnabled)
405 prefix_cmd = 0x90 + dev->idx;
406 else
407 prefix_cmd = 0xd4;
408 res = ps2_wait_write();
409 if (res == B_OK)
410 ps2_write_ctrl(prefix_cmd);
413 res = ps2_wait_write();
414 if (res == B_OK) {
415 if (i == -1) {
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);
420 else
421 atomic_or(&dev->flags, PS2_FLAG_CMD);
422 ps2_write_data(cmd);
423 } else {
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);
433 if (res != B_OK)
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);
445 res = B_IO_ERROR;
446 TRACE("ps2: ps2_dev_command got NACK\n");
449 if (res != B_OK)
450 break;
453 if (res == B_OK) {
454 if (in_count == 0) {
455 atomic_and(&dev->flags, ~PS2_FLAG_CMD);
456 } else {
457 start = system_time();
458 res = acquire_sem_etc(dev->result_sem, 1, B_RELATIVE_TIMEOUT,
459 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;
468 res = B_IO_ERROR;
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);
481 return res;
485 status_t
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,
490 4000000);
494 status_t
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);
502 status_t
503 ps2_dev_sliced_command(ps2_dev* dev, uint8 cmd)
505 uint8 val;
506 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK)
507 return B_ERROR;
509 val = (cmd >> 6) & 3;
510 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
511 return B_ERROR;
513 val = (cmd >> 4) & 3;
514 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
515 return B_ERROR;
517 val = (cmd >> 2) & 3;
518 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
519 return B_ERROR;
521 val = cmd & 3;
522 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK)
523 return B_ERROR;
525 return B_OK;