vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / bus_managers / ps2 / ps2_synaptics.cpp
blob81cffc1514b3a3eb7587d9092848173a71afa3b7
1 /*
2 * Copyright 2008-2010, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
5 * Authors (in chronological order):
6 * Clemens Zeidler (haiku@Clemens-Zeidler.de)
7 * Axel Dörfler, axeld@pinc-software.de
8 */
11 //! PS/2 synaptics touchpad
14 #include "ps2_synaptics.h"
16 #include <string.h>
17 #include <stdlib.h>
19 #include <keyboard_mouse_driver.h>
21 #include "ps2_service.h"
24 // synaptics touchpad proportions
25 #define SYN_EDGE_MOTION_WIDTH 50
26 #define SYN_AREA_OFFSET 40
28 #define MIN_PRESSURE 30
29 #define REAL_MAX_PRESSURE 100
30 #define MAX_PRESSURE 200
33 static hardware_specs gHardwareSpecs;
36 const char* kSynapticsPath[4] = {
37 "input/touchpad/ps2/synaptics_0",
38 "input/touchpad/ps2/synaptics_1",
39 "input/touchpad/ps2/synaptics_2",
40 "input/touchpad/ps2/synaptics_3"
44 static touchpad_info sTouchpadInfo;
45 static ps2_dev *sPassthroughDevice = &ps2_device[PS2_DEVICE_SYN_PASSTHROUGH];
48 static void
49 default_settings(touchpad_settings *set)
51 memcpy(set, &kDefaultTouchpadSettings, sizeof(touchpad_settings));
55 static status_t
56 send_touchpad_arg_timeout(ps2_dev *dev, uint8 arg, bigtime_t timeout)
58 int8 i;
59 uint8 val[8];
61 for (i = 0; i < 4; i++) {
62 val[2 * i] = (arg >> (6 - 2 * i)) & 3;
63 val[2 * i + 1] = 0xE8;
65 return ps2_dev_command_timeout(dev, 0xE8, val, 7, NULL, 0, timeout);
69 static status_t
70 send_touchpad_arg(ps2_dev *dev, uint8 arg)
72 return send_touchpad_arg_timeout(dev, arg, 4000000);
76 static status_t
77 set_touchpad_mode(ps2_dev *dev, uint8 mode)
79 uint8 sample_rate = SYN_CHANGE_MODE;
80 send_touchpad_arg(dev, mode);
81 return ps2_dev_command(dev, PS2_CMD_SET_SAMPLE_RATE, &sample_rate, 1,
82 NULL, 0);
86 static status_t
87 get_synaptics_movment(synaptics_cookie *cookie, mouse_movement *movement)
89 status_t status;
90 touch_event event;
91 uint8 event_buffer[PS2_MAX_PACKET_SIZE];
92 uint8 wValue0, wValue1, wValue2, wValue3, wValue;
93 uint32 val32;
94 uint32 xTwelfBit, yTwelfBit;
96 status = acquire_sem_etc(cookie->synaptics_sem, 1, B_CAN_INTERRUPT, 0);
97 if (status < B_OK)
98 return status;
100 if (!cookie->dev->active) {
101 TRACE("SYNAPTICS: read_event: Error device no longer active\n");
102 return B_ERROR;
105 if (packet_buffer_read(cookie->synaptics_ring_buffer, event_buffer,
106 cookie->dev->packet_size) != cookie->dev->packet_size) {
107 TRACE("SYNAPTICS: error copying buffer\n");
108 return B_ERROR;
111 event.buttons = event_buffer[0] & 3;
112 event.zPressure = event_buffer[2];
114 if (sTouchpadInfo.capExtended) {
115 wValue0 = event_buffer[3] >> 2 & 1;
116 wValue1 = event_buffer[0] >> 2 & 1;
117 wValue2 = event_buffer[0] >> 4 & 1;
118 wValue3 = event_buffer[0] >> 5 & 1;
120 wValue = wValue0;
121 wValue = wValue | (wValue1 << 1);
122 wValue = wValue | (wValue2 << 2);
123 wValue = wValue | (wValue3 << 3);
125 event.wValue = wValue;
126 event.gesture = false;
128 if (sTouchpadInfo.capMiddleButton || sTouchpadInfo.capFourButtons)
129 event.buttons |= ((event_buffer[0] ^ event_buffer[3]) & 0x01) << 2;
130 } else {
131 bool finger = event_buffer[0] >> 5 & 1;
132 if (finger) {
133 // finger with normal width
134 event.wValue = 4;
136 event.gesture = event_buffer[0] >> 2 & 1;
139 event.xPosition = event_buffer[4];
140 event.yPosition = event_buffer[5];
142 val32 = event_buffer[1] & 0x0F;
143 event.xPosition += val32 << 8;
144 val32 = event_buffer[1] >> 4 & 0x0F;
145 event.yPosition += val32 << 8;
147 xTwelfBit = event_buffer[3] >> 4 & 1;
148 event.xPosition += xTwelfBit << 12;
149 yTwelfBit = event_buffer[3] >> 5 & 1;
150 event.yPosition += yTwelfBit << 12;
152 status = cookie->movementMaker.EventToMovement(&event, movement);
154 return status;
158 static void
159 query_capability(ps2_dev *dev)
161 uint8 val[3];
162 send_touchpad_arg(dev, 0x02);
163 ps2_dev_command(dev, 0xE9, NULL, 0, val, 3);
165 sTouchpadInfo.capExtended = val[0] >> 7 & 1;
166 TRACE("SYNAPTICS: extended mode %2x\n", val[0] >> 7 & 1);
167 TRACE("SYNAPTICS: middle button %2x\n", val[0] >> 2 & 1);
168 sTouchpadInfo.capMiddleButton = val[0] >> 2 & 1;
169 TRACE("SYNAPTICS: sleep mode %2x\n", val[2] >> 4 & 1);
170 sTouchpadInfo.capSleep = val[2] >> 4 & 1;
171 TRACE("SYNAPTICS: four buttons %2x\n", val[2] >> 3 & 1);
172 sTouchpadInfo.capFourButtons = val[2] >> 3 & 1;
173 TRACE("SYNAPTICS: multi finger %2x\n", val[2] >> 1 & 1);
174 sTouchpadInfo.capMultiFinger = val[2] >> 1 & 1;
175 TRACE("SYNAPTICS: palm detection %2x\n", val[2] & 1);
176 sTouchpadInfo.capPalmDetection = val[2] & 1;
177 TRACE("SYNAPTICS: pass through %2x\n", val[2] >> 7 & 1);
178 sTouchpadInfo.capPassThrough = val[2] >> 7 & 1;
182 // #pragma mark - exported functions
185 status_t
186 synaptics_pass_through_set_packet_size(ps2_dev *dev, uint8 size)
188 synaptics_cookie *synapticsCookie
189 = (synaptics_cookie*)dev->parent_dev->cookie;
191 status_t status = ps2_dev_command(dev->parent_dev, PS2_CMD_DISABLE, NULL,
192 0, NULL, 0);
193 if (status < B_OK) {
194 INFO("SYNAPTICS: cannot disable touchpad %s\n", dev->parent_dev->name);
195 return B_ERROR;
198 synapticsCookie->packet_index = 0;
200 if (size == 4)
201 synapticsCookie->mode |= SYN_FOUR_BYTE_CHILD;
202 else
203 synapticsCookie->mode &= ~SYN_FOUR_BYTE_CHILD;
205 set_touchpad_mode(dev->parent_dev, synapticsCookie->mode);
207 status = ps2_dev_command(dev->parent_dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
208 if (status < B_OK) {
209 INFO("SYNAPTICS: cannot enable touchpad %s\n", dev->parent_dev->name);
210 return B_ERROR;
212 return status;
216 status_t
217 passthrough_command(ps2_dev *dev, uint8 cmd, const uint8 *out, int outCount,
218 uint8 *in, int inCount, bigtime_t timeout)
220 status_t status;
221 uint8 passThroughCmd = SYN_PASSTHROUGH_CMD;
222 uint8 val;
223 uint32 passThroughInCount = (inCount + 1) * 6;
224 uint8 passThroughIn[passThroughInCount];
225 int8 i;
227 TRACE("SYNAPTICS: passthrough command 0x%x\n", cmd);
229 status = ps2_dev_command(dev->parent_dev, PS2_CMD_DISABLE, NULL, 0,
230 NULL, 0);
231 if (status != B_OK)
232 return status;
234 for (i = -1; i < outCount; i++) {
235 if (i == -1)
236 val = cmd;
237 else
238 val = out[i];
239 status = send_touchpad_arg_timeout(dev->parent_dev, val, timeout);
240 if (status != B_OK)
241 goto finalize;
242 if (i != outCount -1) {
243 status = ps2_dev_command_timeout(dev->parent_dev,
244 PS2_CMD_SET_SAMPLE_RATE, &passThroughCmd, 1, NULL, 0, timeout);
245 if (status != B_OK)
246 goto finalize;
249 status = ps2_dev_command_timeout(dev->parent_dev, PS2_CMD_SET_SAMPLE_RATE,
250 &passThroughCmd, 1, passThroughIn, passThroughInCount, timeout);
251 if (status != B_OK)
252 goto finalize;
254 for (i = 0; i < inCount + 1; i++) {
255 uint8 *inPointer = &passThroughIn[i * 6];
256 if (!IS_SYN_PT_PACKAGE(inPointer)) {
257 TRACE("SYNAPTICS: not a pass throught package\n");
258 goto finalize;
260 if (i == 0)
261 continue;
263 in[i - 1] = passThroughIn[i * 6 + 1];
266 finalize:
267 status_t statusOfEnable = ps2_dev_command(dev->parent_dev, PS2_CMD_ENABLE,
268 NULL, 0, NULL, 0);
269 if (statusOfEnable != B_OK)
270 TRACE("SYNAPTICS: enabling of parent failed: 0x%lx.\n", statusOfEnable);
272 return status != B_OK ? status : statusOfEnable;
276 status_t
277 probe_synaptics(ps2_dev *dev)
279 uint8 val[3];
280 uint8 deviceId;
281 status_t status;
282 TRACE("SYNAPTICS: probe\n");
284 // We reset the device here because it may have been left in a confused
285 // state by a previous probing attempt. Some synaptics touchpads are known
286 // to lockup when we attempt to detect them as IBM trackpoints.
287 ps2_reset_mouse(dev);
289 // Request "Identify touchpad"
290 // The touchpad will delay this, until it's ready and calibrated.
291 status = send_touchpad_arg(dev, 0x00);
292 if (status != B_OK)
293 return status;
295 // "Status request" (executes "Identify touchpad")
296 status = ps2_dev_command(dev, 0xE9, NULL, 0, val, 3);
297 if (status != B_OK)
298 return status;
300 sTouchpadInfo.minorVersion = val[0];
301 deviceId = val[1];
302 if (deviceId != SYN_TOUCHPAD) {
303 TRACE("SYNAPTICS: not found\n");
304 return B_ERROR;
307 TRACE("SYNAPTICS: Touchpad found id:l %2x\n", deviceId);
308 sTouchpadInfo.majorVersion = val[2] & 0x0F;
309 TRACE("SYNAPTICS: version %d.%d\n", sTouchpadInfo.majorVersion,
310 sTouchpadInfo.minorVersion);
311 // version >= 4.0?
312 if (sTouchpadInfo.minorVersion <= 2
313 && sTouchpadInfo.majorVersion <= 3) {
314 TRACE("SYNAPTICS: too old touchpad not supported\n");
315 return B_ERROR;
317 dev->name = kSynapticsPath[dev->idx];
318 return B_OK;
322 // #pragma mark - Device functions
325 status_t
326 synaptics_open(const char *name, uint32 flags, void **_cookie)
328 status_t status;
329 synaptics_cookie *cookie;
330 ps2_dev *dev;
331 int i;
333 for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
334 if (0 == strcmp(ps2_device[i].name, name)) {
335 dev = &ps2_device[i];
336 break;
340 if (dev == NULL) {
341 TRACE("ps2: dev = NULL\n");
342 return B_ERROR;
345 if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
346 return B_BUSY;
348 cookie = (synaptics_cookie*)malloc(sizeof(synaptics_cookie));
349 if (cookie == NULL)
350 goto err1;
351 memset(cookie, 0, sizeof(*cookie));
353 cookie->movementMaker.Init();
354 *_cookie = cookie;
356 cookie->dev = dev;
357 dev->cookie = cookie;
358 dev->disconnect = &synaptics_disconnect;
359 dev->handle_int = &synaptics_handle_int;
361 default_settings(&cookie->settings);
363 gHardwareSpecs.edgeMotionWidth = SYN_EDGE_MOTION_WIDTH;
365 gHardwareSpecs.areaStartX = SYN_AREA_START_X;
366 gHardwareSpecs.areaEndX = SYN_AREA_END_X;
367 gHardwareSpecs.areaStartY = SYN_AREA_START_Y;
368 gHardwareSpecs.areaEndY = SYN_AREA_END_Y;
370 gHardwareSpecs.minPressure = MIN_PRESSURE;
371 gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE;
372 gHardwareSpecs.maxPressure = MAX_PRESSURE;
374 cookie->movementMaker.SetSettings(&cookie->settings);
375 cookie->movementMaker.SetSpecs(&gHardwareSpecs);
377 dev->packet_size = PS2_PACKET_SYNAPTICS;
379 cookie->synaptics_ring_buffer
380 = create_packet_buffer(SYNAPTICS_HISTORY_SIZE * dev->packet_size);
381 if (cookie->synaptics_ring_buffer == NULL) {
382 TRACE("ps2: can't allocate mouse actions buffer\n");
383 goto err2;
386 // create the mouse semaphore, used for synchronization between
387 // the interrupt handler and the read operation
388 cookie->synaptics_sem = create_sem(0, "ps2_synaptics_sem");
389 if (cookie->synaptics_sem < 0) {
390 TRACE("SYNAPTICS: failed creating semaphore!\n");
391 goto err3;
393 query_capability(dev);
395 // create pass through dev
396 if (sTouchpadInfo.capPassThrough) {
397 TRACE("SYNAPTICS: pass through detected\n");
398 sPassthroughDevice->parent_dev = dev;
399 sPassthroughDevice->idx = dev->idx;
400 ps2_service_notify_device_added(sPassthroughDevice);
403 // Set Mode
404 if (sTouchpadInfo.capExtended)
405 cookie->mode = SYN_ABSOLUTE_W_MODE;
406 else
407 cookie->mode = SYN_ABSOLUTE_MODE;
409 status = set_touchpad_mode(dev, cookie->mode);
410 if (status < B_OK) {
411 INFO("SYNAPTICS: cannot set mode %s\n", name);
412 goto err4;
415 status = ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
416 if (status < B_OK) {
417 INFO("SYNAPTICS: cannot enable touchpad %s\n", name);
418 goto err4;
421 atomic_or(&dev->flags, PS2_FLAG_ENABLED);
423 TRACE("SYNAPTICS: open %s success\n", name);
424 return B_OK;
426 err4:
427 delete_sem(cookie->synaptics_sem);
428 err3:
429 delete_packet_buffer(cookie->synaptics_ring_buffer);
430 err2:
431 free(cookie);
432 err1:
433 atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
435 TRACE("SYNAPTICS: synaptics_open %s failed\n", name);
436 return B_ERROR;
440 status_t
441 synaptics_close(void *_cookie)
443 status_t status;
444 synaptics_cookie *cookie = (synaptics_cookie*)_cookie;
446 ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
447 150000);
449 delete_packet_buffer(cookie->synaptics_ring_buffer);
450 delete_sem(cookie->synaptics_sem);
452 atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
453 atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
455 // Reset the touchpad so it generate standard ps2 packets instead of
456 // extended ones. If not, BeOS is confused with such packets when rebooting
457 // without a complete shutdown.
458 status = ps2_reset_mouse(cookie->dev);
459 if (status != B_OK) {
460 INFO("ps2: reset failed\n");
461 return B_ERROR;
464 if (sTouchpadInfo.capPassThrough)
465 ps2_service_notify_device_removed(sPassthroughDevice);
467 TRACE("SYNAPTICS: close %s done\n", cookie->dev->name);
468 return B_OK;
472 status_t
473 synaptics_freecookie(void *_cookie)
475 free(_cookie);
476 return B_OK;
480 static status_t
481 synaptics_read(void *cookie, off_t pos, void *buffer, size_t *_length)
483 *_length = 0;
484 return B_NOT_ALLOWED;
488 static status_t
489 synaptics_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
491 *_length = 0;
492 return B_NOT_ALLOWED;
496 status_t
497 synaptics_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
499 synaptics_cookie *cookie = (synaptics_cookie*)_cookie;
500 mouse_movement movement;
501 status_t status;
503 switch (op) {
504 case MS_READ:
505 TRACE("SYNAPTICS: MS_READ get event\n");
506 if ((status = get_synaptics_movment(cookie, &movement)) != B_OK)
507 return status;
508 return user_memcpy(buffer, &movement, sizeof(movement));
510 case MS_IS_TOUCHPAD:
511 TRACE("SYNAPTICS: MS_IS_TOUCHPAD\n");
512 return B_OK;
514 case MS_SET_TOUCHPAD_SETTINGS:
515 TRACE("SYNAPTICS: MS_SET_TOUCHPAD_SETTINGS");
516 user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings));
517 return B_OK;
519 case MS_SET_CLICKSPEED:
520 TRACE("SYNAPTICS: ioctl MS_SETCLICK (set click speed)\n");
521 return user_memcpy(&cookie->movementMaker.click_speed, buffer,
522 sizeof(bigtime_t));
524 default:
525 TRACE("SYNAPTICS: unknown opcode: %ld\n", op);
526 return B_DEV_INVALID_IOCTL;
531 int32
532 synaptics_handle_int(ps2_dev *dev)
534 synaptics_cookie *cookie = (synaptics_cookie*)dev->cookie;
535 uint8 val;
537 val = cookie->dev->history[0].data;
539 if ((cookie->packet_index == 0 || cookie->packet_index == 3)
540 && (val & 8) != 0) {
541 INFO("SYNAPTICS: bad mouse data, trying resync\n");
542 cookie->packet_index = 0;
543 return B_UNHANDLED_INTERRUPT;
545 if (cookie->packet_index == 0 && val >> 6 != 0x02) {
546 TRACE("SYNAPTICS: first package begins not with bit 1, 0\n");
547 return B_UNHANDLED_INTERRUPT;
549 if (cookie->packet_index == 3 && val >> 6 != 0x03) {
550 TRACE("SYNAPTICS: third package begins not with bit 1, 1\n");
551 cookie->packet_index = 0;
552 return B_UNHANDLED_INTERRUPT;
554 cookie->buffer[cookie->packet_index] = val;
556 cookie->packet_index++;
557 if (cookie->packet_index >= 6) {
558 cookie->packet_index = 0;
560 // check if package is a pass through package if true pass it
561 // too the pass through interrupt handle
562 if (sPassthroughDevice->active
563 && sPassthroughDevice->handle_int != NULL
564 && IS_SYN_PT_PACKAGE(cookie->buffer)) {
565 status_t status;
567 sPassthroughDevice->history[0].data = cookie->buffer[1];
568 sPassthroughDevice->handle_int(sPassthroughDevice);
569 sPassthroughDevice->history[0].data = cookie->buffer[4];
570 sPassthroughDevice->handle_int(sPassthroughDevice);
571 sPassthroughDevice->history[0].data = cookie->buffer[5];
572 status = sPassthroughDevice->handle_int(sPassthroughDevice);
574 if (cookie->dev->packet_size == 4) {
575 sPassthroughDevice->history[0].data = cookie->buffer[2];
576 status = sPassthroughDevice->handle_int(sPassthroughDevice);
578 return status;
581 if (packet_buffer_write(cookie->synaptics_ring_buffer,
582 cookie->buffer, cookie->dev->packet_size)
583 != cookie->dev->packet_size) {
584 // buffer is full, drop new data
585 return B_HANDLED_INTERRUPT;
587 release_sem_etc(cookie->synaptics_sem, 1, B_DO_NOT_RESCHEDULE);
589 return B_INVOKE_SCHEDULER;
592 return B_HANDLED_INTERRUPT;
596 void
597 synaptics_disconnect(ps2_dev *dev)
599 synaptics_cookie *cookie = (synaptics_cookie*)dev->cookie;
600 // the mouse device might not be opened at this point
601 INFO("SYNAPTICS: synaptics_disconnect %s\n", dev->name);
602 if ((dev->flags & PS2_FLAG_OPEN) != 0)
603 release_sem(cookie->synaptics_sem);
607 device_hooks gSynapticsDeviceHooks = {
608 synaptics_open,
609 synaptics_close,
610 synaptics_freecookie,
611 synaptics_ioctl,
612 synaptics_read,
613 synaptics_write,