vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / bus_managers / ps2 / ps2_alps.cpp
blob0bc1dec74651e40b3cabbe59ae02d05525dad95a
1 /*
2 * Copyright 2011, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
5 * The alps_model_info struct and all the hardware specs are taken from the
6 * linux driver, thanks a lot!
8 * Authors:
9 * Clemens Zeidler (haiku@Clemens-Zeidler.de)
13 #include "ps2_alps.h"
15 #include <stdlib.h>
16 #include <string.h>
18 #include "ps2_service.h"
21 static int32 generate_event(timer* timer);
24 const bigtime_t kEventInterval = 1000 * 50;
27 class EventProducer {
28 public:
29 EventProducer()
31 fFired = false;
34 status_t
35 FireEvent(alps_cookie* cookie, uint8* package)
37 fCookie = cookie;
38 memcpy(fLastPackage, package, sizeof(uint8) * PS2_PACKET_ALPS);
40 status_t status = add_timer(&fEventTimer, &generate_event,
41 kEventInterval, B_ONE_SHOT_RELATIVE_TIMER);
42 if (status == B_OK)
43 fFired = true;
44 return status;
47 bool
48 CancelEvent()
50 if (!fFired)
51 return false;
52 fFired = false;
53 return cancel_timer(&fEventTimer);
56 void
57 InjectEvent()
59 if (packet_buffer_write(fCookie->ring_buffer, fLastPackage,
60 PS2_PACKET_ALPS) != PS2_PACKET_ALPS) {
61 // buffer is full, drop new data
62 return;
64 release_sem_etc(fCookie->sem, 1, B_DO_NOT_RESCHEDULE);
67 private:
68 bool fFired;
69 uint8 fLastPackage[PS2_PACKET_ALPS];
70 timer fEventTimer;
71 alps_cookie* fCookie;
75 static EventProducer gEventProducer;
78 static int32
79 generate_event(timer* timer)
81 gEventProducer.InjectEvent();
82 return 0;
86 const char* kALPSPath[4] = {
87 "input/touchpad/ps2/alps_0",
88 "input/touchpad/ps2/alps_1",
89 "input/touchpad/ps2/alps_2",
90 "input/touchpad/ps2/alps_3"
94 typedef struct alps_model_info {
95 uint8 id[3];
96 uint8 firstByte;
97 uint8 maskFirstByte;
98 uint8 flags;
99 } alps_model_info;
102 #define ALPS_OLDPROTO 0x01 // old style input
103 #define ALPS_DUALPOINT 0x02 // touchpad has trackstick
104 #define ALPS_PASS 0x04 // device has a pass-through port
106 #define ALPS_WHEEL 0x08 // hardware wheel present
107 #define ALPS_FW_BK_1 0x10 // front & back buttons present
108 #define ALPS_FW_BK_2 0x20 // front & back buttons present
109 #define ALPS_FOUR_BUTTONS 0x40 // 4 direction button present
110 #define ALPS_PS2_INTERLEAVED 0x80 // 3-byte PS/2 packet interleaved with
111 // 6-byte ALPS packet
113 static const struct alps_model_info gALPSModelInfos[] = {
114 {{0x32, 0x02, 0x14}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
115 // Toshiba Salellite Pro M10
116 // {{0x33, 0x02, 0x0a}, 0x88, 0xf8, ALPS_OLDPROTO},
117 // UMAX-530T
118 {{0x53, 0x02, 0x0a}, 0xf8, 0xf8, 0},
119 {{0x53, 0x02, 0x14}, 0xf8, 0xf8, 0},
120 {{0x60, 0x03, 0xc8}, 0xf8, 0xf8, 0},
121 // HP ze1115
122 {{0x63, 0x02, 0x0a}, 0xf8, 0xf8, 0},
123 {{0x63, 0x02, 0x14}, 0xf8, 0xf8, 0},
124 {{0x63, 0x02, 0x28}, 0xf8, 0xf8, ALPS_FW_BK_2},
125 // Fujitsu Siemens S6010
126 // {{0x63, 0x02, 0x3c}, 0x8f, 0x8f, ALPS_WHEEL},
127 // Toshiba Satellite S2400-103
128 {{0x63, 0x02, 0x50}, 0xef, 0xef, ALPS_FW_BK_1},
129 // NEC Versa L320
130 {{0x63, 0x02, 0x64}, 0xf8, 0xf8, 0},
131 {{0x63, 0x03, 0xc8}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
132 // Dell Latitude D800
133 {{0x73, 0x00, 0x0a}, 0xf8, 0xf8, ALPS_DUALPOINT},
134 // ThinkPad R61 8918-5QG, x301
135 {{0x73, 0x02, 0x0a}, 0xf8, 0xf8, 0},
136 {{0x73, 0x02, 0x14}, 0xf8, 0xf8, ALPS_FW_BK_2},
137 // Ahtec Laptop
138 {{0x20, 0x02, 0x0e}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
139 // XXX
140 {{0x22, 0x02, 0x0a}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
141 {{0x22, 0x02, 0x14}, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT},
142 // Dell Latitude D600
143 // {{0x62, 0x02, 0x14}, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT
144 // | ALPS_PS2_INTERLEAVED},
145 // Dell Latitude E5500, E6400, E6500, Precision M4400
146 {{0x73, 0x02, 0x50}, 0xcf, 0xcf, ALPS_FOUR_BUTTONS},
147 // Dell Vostro 1400
148 // {{0x52, 0x01, 0x14}, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT
149 // | ALPS_PS2_INTERLEAVED},
150 // Toshiba Tecra A11-11L
151 {{0, 0, 0}, 0, 0, 0}
155 static alps_model_info* sFoundModel = NULL;
158 // touchpad proportions
159 #define EDGE_MOTION_WIDTH 55
160 // increase the touchpad size a little bit
161 #define AREA_START_X 40
162 #define AREA_END_X 987
163 #define AREA_START_Y 40
164 #define AREA_END_Y 734
166 #define MIN_PRESSURE 15
167 #define REAL_MAX_PRESSURE 70
168 #define MAX_PRESSURE 115
171 #define ALPS_HISTORY_SIZE 256
174 static hardware_specs gHardwareSpecs;
177 /* Data taken from linux driver:
178 ALPS absolute Mode - new format
179 byte 0: 1 ? ? ? 1 ? ? ?
180 byte 1: 0 x6 x5 x4 x3 x2 x1 x0
181 byte 2: 0 x10 x9 x8 x7 ? fin ges
182 byte 3: 0 y9 y8 y7 1 M R L
183 byte 4: 0 y6 y5 y4 y3 y2 y1 y0
184 byte 5: 0 z6 z5 z4 z3 z2 z1 z0
186 static status_t
187 get_alps_movment(alps_cookie *cookie, mouse_movement *movement)
189 status_t status;
190 touch_event event;
191 uint8 event_buffer[PS2_PACKET_ALPS];
193 status = acquire_sem_etc(cookie->sem, 1, B_CAN_INTERRUPT, 0);
194 if (status < B_OK)
195 return status;
197 if (!cookie->dev->active) {
198 TRACE("ALPS: read_event: Error device no longer active\n");
199 return B_ERROR;
202 if (packet_buffer_read(cookie->ring_buffer, event_buffer,
203 cookie->dev->packet_size) != cookie->dev->packet_size) {
204 TRACE("ALPS: error copying buffer\n");
205 return B_ERROR;
208 event.buttons = event_buffer[3] & 7;
209 event.zPressure = event_buffer[5];
211 // finger on touchpad
212 if (event_buffer[2] & 0x2) {
213 // finger with normal width
214 event.wValue = 4;
215 } else {
216 event.wValue = 3;
219 // tab gesture
220 if (event_buffer[2] & 0x1) {
221 event.zPressure = 60;
222 event.wValue = 4;
224 // if hardware tab gesture is off a z pressure of 16 is reported
225 if (cookie->previousZ == 0 && event.wValue == 4 && event.zPressure == 16)
226 event.zPressure = 60;
228 cookie->previousZ = event.zPressure;
230 event.xPosition = event_buffer[1] | ((event_buffer[2] & 0x78) << 4);
231 event.yPosition = event_buffer[4] | ((event_buffer[3] & 0x70) << 3);
233 // check for trackpoint even (z pressure 127)
234 if (sFoundModel->flags & ALPS_DUALPOINT && event.zPressure == 127) {
235 movement->xdelta = event.xPosition > 383 ? event.xPosition - 768
236 : event.xPosition;
237 movement->ydelta = event.yPosition > 255
238 ? event.yPosition - 512 : event.yPosition;
239 movement->wheel_xdelta = 0;
240 movement->wheel_ydelta = 0;
241 movement->buttons = event.buttons;
242 movement->timestamp = system_time();
243 cookie->movementMaker.UpdateButtons(movement);
244 } else {
245 event.yPosition = AREA_END_Y - (event.yPosition - AREA_START_Y);
246 status = cookie->movementMaker.EventToMovement(&event, movement);
249 if (cookie->movementMaker.WasEdgeMotion()
250 || cookie->movementMaker.TapDragStarted()) {
251 gEventProducer.FireEvent(cookie, event_buffer);
254 return status;
258 static void
259 default_settings(touchpad_settings *set)
261 memcpy(set, &kDefaultTouchpadSettings, sizeof(touchpad_settings));
265 status_t
266 probe_alps(ps2_dev* dev)
268 int i;
269 uint8 val[3];
270 TRACE("ALPS: probe\n");
272 val[0] = 0;
273 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, val, 1, NULL, 0) != B_OK
274 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11, NULL, 0, NULL, 0)
275 != B_OK
276 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11, NULL, 0, NULL, 0)
277 != B_OK
278 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11, NULL, 0, NULL, 0)
279 != B_OK)
280 return B_ERROR;
282 if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3)
283 != B_OK)
284 return B_ERROR;
286 if (val[0] != 0 || val[1] != 0 || (val[2] != 10 && val[2] != 100))
287 return B_ERROR;
289 val[0] = 0;
290 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, val, 1, NULL, 0) != B_OK
291 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE21, NULL, 0, NULL, 0)
292 != B_OK
293 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE21, NULL, 0, NULL, 0)
294 != B_OK
295 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE21, NULL, 0, NULL, 0)
296 != B_OK)
297 return B_ERROR;
299 if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3)
300 != B_OK)
301 return B_ERROR;
303 for (i = 0; ; i++) {
304 const alps_model_info* info = &gALPSModelInfos[i];
305 if (info->id[0] == 0) {
306 INFO("ALPS not supported: %2.2x %2.2x %2.2x\n", val[0], val[1],
307 val[2]);
308 return B_ERROR;
311 if (info->id[0] == val[0] && info->id[1] == val[1]
312 && info->id[2] == val[2]) {
313 sFoundModel = (alps_model_info*)info;
314 INFO("ALPS found: %2.2x %2.2x %2.2x\n", val[0], val[1], val[2]);
315 break;
319 dev->name = kALPSPath[dev->idx];
320 dev->packet_size = PS2_PACKET_ALPS;
322 return B_OK;
326 status_t
327 switch_hardware_tab(ps2_dev* dev, bool on)
329 uint8 val[3];
330 uint8 arg = 0x00;
331 uint8 command = PS2_CMD_MOUSE_SET_RES;
332 if (on) {
333 arg = 0x0A;
334 command = PS2_CMD_SET_TYPEMATIC;
336 if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3) != B_OK
337 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
338 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
339 || ps2_dev_command(dev, command, &arg, 1, NULL, 0) != B_OK)
340 return B_ERROR;
342 return B_OK;
346 status_t
347 enable_passthrough(ps2_dev* dev, bool on)
349 uint8 command = PS2_CMD_MOUSE_SET_SCALE11;
350 if (on)
351 command = PS2_CMD_MOUSE_SET_SCALE21;
353 if (ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK
354 || ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK
355 || ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK
356 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK)
357 return B_ERROR;
359 return B_OK;
363 status_t
364 alps_open(const char *name, uint32 flags, void **_cookie)
366 ps2_dev* dev;
367 int i;
368 for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
369 if (0 == strcmp(ps2_device[i].name, name)) {
370 dev = &ps2_device[i];
371 break;
375 if (dev == NULL) {
376 TRACE("ps2: dev = NULL\n");
377 return B_ERROR;
380 if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
381 return B_BUSY;
383 alps_cookie* cookie = (alps_cookie*)malloc(sizeof(alps_cookie));
384 if (cookie == NULL)
385 goto err1;
386 memset(cookie, 0, sizeof(*cookie));
388 cookie->movementMaker.Init();
389 cookie->previousZ = 0;
390 *_cookie = cookie;
392 cookie->dev = dev;
393 dev->cookie = cookie;
394 dev->disconnect = &alps_disconnect;
395 dev->handle_int = &alps_handle_int;
397 default_settings(&cookie->settings);
399 gHardwareSpecs.edgeMotionWidth = EDGE_MOTION_WIDTH;
401 gHardwareSpecs.areaStartX = AREA_START_X;
402 gHardwareSpecs.areaEndX = AREA_END_X;
403 gHardwareSpecs.areaStartY = AREA_START_Y;
404 gHardwareSpecs.areaEndY = AREA_END_Y;
406 gHardwareSpecs.minPressure = MIN_PRESSURE;
407 gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE;
408 gHardwareSpecs.maxPressure = MAX_PRESSURE;
410 cookie->movementMaker.SetSettings(&cookie->settings);
411 cookie->movementMaker.SetSpecs(&gHardwareSpecs);
413 dev->packet_size = PS2_PACKET_ALPS;
415 cookie->ring_buffer = create_packet_buffer(
416 ALPS_HISTORY_SIZE * dev->packet_size);
417 if (cookie->ring_buffer == NULL) {
418 TRACE("ALPS: can't allocate mouse actions buffer\n");
419 goto err2;
421 // create the mouse semaphore, used for synchronization between
422 // the interrupt handler and the read operation
423 cookie->sem = create_sem(0, "ps2_alps_sem");
424 if (cookie->sem < 0) {
425 TRACE("ALPS: failed creating semaphore!\n");
426 goto err3;
429 if ((sFoundModel->flags & ALPS_PASS) != 0
430 && enable_passthrough(dev, true) != B_OK)
431 goto err4;
433 // switch tap mode off
434 if (switch_hardware_tab(dev, false) != B_OK)
435 goto err4;
437 // init the alps device to absolut mode
438 if (ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
439 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
440 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
441 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
442 || ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK)
443 goto err4;
445 if ((sFoundModel->flags & ALPS_PASS) != 0
446 && enable_passthrough(dev, false) != B_OK)
447 goto err4;
449 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_STREAM, NULL, 0, NULL, 0) != B_OK)
450 goto err4;
452 if (ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK)
453 goto err4;
455 atomic_or(&dev->flags, PS2_FLAG_ENABLED);
457 TRACE("ALPS: open %s success\n", name);
458 return B_OK;
460 err4:
461 delete_sem(cookie->sem);
462 err3:
463 delete_packet_buffer(cookie->ring_buffer);
464 err2:
465 free(cookie);
466 err1:
467 atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
469 TRACE("ALPS: open %s failed\n", name);
470 return B_ERROR;
474 status_t
475 alps_close(void *_cookie)
477 gEventProducer.CancelEvent();
479 alps_cookie *cookie = (alps_cookie*)_cookie;
481 ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
482 150000);
484 delete_packet_buffer(cookie->ring_buffer);
485 delete_sem(cookie->sem);
487 atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
488 atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
490 // Reset the touchpad so it generate standard ps2 packets instead of
491 // extended ones. If not, BeOS is confused with such packets when rebooting
492 // without a complete shutdown.
493 status_t status = ps2_reset_mouse(cookie->dev);
494 if (status != B_OK) {
495 INFO("ps2: reset failed\n");
496 return B_ERROR;
499 TRACE("ALPS: close %s done\n", cookie->dev->name);
500 return B_OK;
504 status_t
505 alps_freecookie(void *_cookie)
507 free(_cookie);
508 return B_OK;
512 status_t
513 alps_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
515 alps_cookie *cookie = (alps_cookie*)_cookie;
516 mouse_movement movement;
517 status_t status;
519 switch (op) {
520 case MS_READ:
521 TRACE("ALPS: MS_READ get event\n");
522 if ((status = get_alps_movment(cookie, &movement)) != B_OK)
523 return status;
524 return user_memcpy(buffer, &movement, sizeof(movement));
526 case MS_IS_TOUCHPAD:
527 TRACE("ALPS: MS_IS_TOUCHPAD\n");
528 return B_OK;
530 case MS_SET_TOUCHPAD_SETTINGS:
531 TRACE("ALPS: MS_SET_TOUCHPAD_SETTINGS");
532 user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings));
533 return B_OK;
535 case MS_SET_CLICKSPEED:
536 TRACE("ALPS: ioctl MS_SETCLICK (set click speed)\n");
537 return user_memcpy(&cookie->movementMaker.click_speed, buffer,
538 sizeof(bigtime_t));
540 default:
541 TRACE("ALPS: unknown opcode: %ld\n", op);
542 return B_BAD_VALUE;
547 static status_t
548 alps_read(void* cookie, off_t pos, void* buffer, size_t* _length)
550 *_length = 0;
551 return B_NOT_ALLOWED;
555 static status_t
556 alps_write(void* cookie, off_t pos, const void* buffer, size_t* _length)
558 *_length = 0;
559 return B_NOT_ALLOWED;
563 int32
564 alps_handle_int(ps2_dev* dev)
566 alps_cookie* cookie = (alps_cookie*)dev->cookie;
568 // we got a real event cancel the fake event
569 gEventProducer.CancelEvent();
571 uint8 val;
572 val = cookie->dev->history[0].data;
573 if (cookie->packet_index == 0
574 && (val & sFoundModel->maskFirstByte) != sFoundModel->firstByte) {
575 INFO("ALPS: bad header, trying resync\n");
576 cookie->packet_index = 0;
577 return B_UNHANDLED_INTERRUPT;
580 // data packages starting with a 0
581 if (cookie->packet_index > 1 && (val & 0x80)) {
582 INFO("ALPS: bad package data, trying resync\n");
583 cookie->packet_index = 0;
584 return B_UNHANDLED_INTERRUPT;
587 cookie->buffer[cookie->packet_index] = val;
589 cookie->packet_index++;
590 if (cookie->packet_index >= 6) {
591 cookie->packet_index = 0;
593 if (packet_buffer_write(cookie->ring_buffer,
594 cookie->buffer, cookie->dev->packet_size)
595 != cookie->dev->packet_size) {
596 // buffer is full, drop new data
597 return B_HANDLED_INTERRUPT;
599 release_sem_etc(cookie->sem, 1, B_DO_NOT_RESCHEDULE);
601 return B_INVOKE_SCHEDULER;
604 return B_HANDLED_INTERRUPT;
608 void
609 alps_disconnect(ps2_dev *dev)
611 alps_cookie *cookie = (alps_cookie*)dev->cookie;
612 // the mouse device might not be opened at this point
613 INFO("ALPS: alps_disconnect %s\n", dev->name);
614 if ((dev->flags & PS2_FLAG_OPEN) != 0)
615 release_sem(cookie->sem);
619 device_hooks gALPSDeviceHooks = {
620 alps_open,
621 alps_close,
622 alps_freecookie,
623 alps_ioctl,
624 alps_read,
625 alps_write,