xref: /haiku/src/add-ons/kernel/bus_managers/ps2/ps2_alps.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
1 /*
2  * Copyright 2011, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * The alps_model_info struct and all the hardware specs are taken from the
6  * linux driver, thanks a lot!
7  *
8  * Authors:
9  *		Clemens Zeidler (haiku@Clemens-Zeidler.de)
10  */
11 
12 
13 #include "ps2_alps.h"
14 
15 #include <stdlib.h>
16 #include <string.h>
17 
18 #include <keyboard_mouse_driver.h>
19 
20 #include "ps2_service.h"
21 
22 
23 //#define TRACE_PS2_ALPS
24 #ifdef TRACE_PS2_APLS
25 #	define TRACE(x...) dprintf(x)
26 #else
27 #	define TRACE(x...)
28 #endif
29 
30 
31 const char* kALPSPath[4] = {
32 	"input/touchpad/ps2/alps_0",
33 	"input/touchpad/ps2/alps_1",
34 	"input/touchpad/ps2/alps_2",
35 	"input/touchpad/ps2/alps_3"
36 };
37 
38 
39 typedef struct alps_model_info {
40 	uint8		id[3];
41 	uint8		firstByte;
42 	uint8		maskFirstByte;
43 	uint8		flags;
44 } alps_model_info;
45 
46 
47 #define ALPS_OLDPROTO           0x01	// old style input
48 #define ALPS_DUALPOINT          0x02	// touchpad has trackstick
49 #define ALPS_PASS               0x04    // device has a pass-through port
50 
51 #define ALPS_WHEEL              0x08	// hardware wheel present
52 #define ALPS_FW_BK_1            0x10	// front & back buttons present
53 #define ALPS_FW_BK_2            0x20	// front & back buttons present
54 #define ALPS_FOUR_BUTTONS       0x40	// 4 direction button present
55 #define ALPS_PS2_INTERLEAVED    0x80	// 3-byte PS/2 packet interleaved with
56 										// 6-byte ALPS packet
57 
58 static const struct alps_model_info gALPSModelInfos[] = {
59 	{{0x32, 0x02, 0x14}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
60 		// Toshiba Salellite Pro M10
61 //	{{0x33, 0x02, 0x0a}, 0x88, 0xf8, ALPS_OLDPROTO},
62 		// UMAX-530T
63 	{{0x53, 0x02, 0x0a}, 0xf8, 0xf8, 0},
64 	{{0x53, 0x02, 0x14}, 0xf8, 0xf8, 0},
65 	{{0x60, 0x03, 0xc8}, 0xf8, 0xf8, 0},
66 		// HP ze1115
67 	{{0x63, 0x02, 0x0a}, 0xf8, 0xf8, 0},
68 	{{0x63, 0x02, 0x14}, 0xf8, 0xf8, 0},
69 	{{0x63, 0x02, 0x28}, 0xf8, 0xf8, ALPS_FW_BK_2},
70 		// Fujitsu Siemens S6010
71 //	{{0x63, 0x02, 0x3c}, 0x8f, 0x8f, ALPS_WHEEL},
72 		// Toshiba Satellite S2400-103
73 	{{0x63, 0x02, 0x50}, 0xef, 0xef, ALPS_FW_BK_1},
74 		// NEC Versa L320
75 	{{0x63, 0x02, 0x64}, 0xf8, 0xf8, 0},
76 	{{0x63, 0x03, 0xc8}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
77 		// Dell Latitude D800
78 	{{0x73, 0x00, 0x0a}, 0xf8, 0xf8, ALPS_DUALPOINT},
79 		// ThinkPad R61 8918-5QG, x301
80 	{{0x73, 0x02, 0x0a}, 0xf8, 0xf8, 0},
81 	{{0x73, 0x02, 0x14}, 0xf8, 0xf8, ALPS_FW_BK_2},
82 		// Ahtec Laptop
83 	{{0x20, 0x02, 0x0e}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
84 		// XXX
85 	{{0x22, 0x02, 0x0a}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
86 	{{0x22, 0x02, 0x14}, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT},
87 		// Dell Latitude D600
88 //	{{0x62, 0x02, 0x14}, 0xcf, 0xcf,  ALPS_PASS | ALPS_DUALPOINT
89 //		| ALPS_PS2_INTERLEAVED},
90 		// Dell Latitude E5500, E6400, E6500, Precision M4400
91 	{{0x73, 0x02, 0x50}, 0xcf, 0xcf, ALPS_FOUR_BUTTONS},
92 		// Dell Vostro 1400
93 //	{{0x52, 0x01, 0x14}, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT
94 //		| ALPS_PS2_INTERLEAVED},
95 		// Toshiba Tecra A11-11L
96 	{{0, 0, 0}, 0, 0, 0}
97 };
98 
99 
100 static alps_model_info* sFoundModel = NULL;
101 
102 
103 // touchpad proportions
104 #define EDGE_MOTION_WIDTH	55
105 // increase the touchpad size a little bit
106 #define AREA_START_X		40
107 #define AREA_END_X			987
108 #define AREA_START_Y		40
109 #define AREA_END_Y			734
110 
111 #define MIN_PRESSURE		15
112 #define REAL_MAX_PRESSURE	70
113 #define MAX_PRESSURE		115
114 
115 
116 #define ALPS_HISTORY_SIZE	256
117 
118 
119 static touchpad_specs gHardwareSpecs;
120 
121 
122 /* Data taken from linux driver:
123 ALPS absolute Mode - new format
124 byte 0:  1    ?    ?    ?    1    ?    ?    ?
125 byte 1:  0   x6   x5   x4   x3   x2   x1   x0
126 byte 2:  0  x10   x9   x8   x7    ?  fin  ges
127 byte 3:  0   y9   y8   y7    1    M    R    L
128 byte 4:  0   y6   y5   y4   y3   y2   y1   y0
129 byte 5:  0   z6   z5   z4   z3   z2   z1   z0
130 */
131 static status_t
132 get_alps_movment(alps_cookie *cookie, touchpad_read *_read)
133 {
134 	status_t status;
135 	touchpad_movement event;
136 	uint8 event_buffer[PS2_PACKET_ALPS];
137 
138 	status = acquire_sem_etc(cookie->sem, 1, B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT,
139 		_read->timeout);
140 	if (status < B_OK)
141 		return status;
142 
143 	if (!cookie->dev->active) {
144 		TRACE("ALPS: read_event: Error device no longer active\n");
145 		return B_ERROR;
146 	}
147 
148 	if (packet_buffer_read(cookie->ring_buffer, event_buffer,
149 			cookie->dev->packet_size) != cookie->dev->packet_size) {
150 		TRACE("ALPS: error copying buffer\n");
151 		return B_ERROR;
152 	}
153 
154 	event.buttons = event_buffer[3] & 7;
155 	event.zPressure = event_buffer[5];
156 
157 	// finger on touchpad
158 	if (event_buffer[2] & 0x2) {
159 		// finger with normal width
160 		event.fingerWidth = 4;
161 	} else {
162 		event.fingerWidth = 3;
163 	}
164 
165 	// tab gesture
166 	if (event_buffer[2] & 0x1) {
167 		event.zPressure = 60;
168 		event.fingerWidth = 4;
169 	}
170 	// if hardware tab gesture is off a z pressure of 16 is reported
171 	if (cookie->previousZ == 0 && event.fingerWidth == 4 && event.zPressure == 16)
172 		event.zPressure = 60;
173 
174 	cookie->previousZ = event.zPressure;
175 
176 	event.xPosition = event_buffer[1] | ((event_buffer[2] & 0x78) << 4);
177 	event.yPosition = event_buffer[4] | ((event_buffer[3] & 0x70) << 3);
178 
179 	// check for trackpoint even (z pressure 127)
180 	if (sFoundModel->flags & ALPS_DUALPOINT && event.zPressure == 127) {
181 		mouse_movement movement;
182 		movement.xdelta = event.xPosition > 383 ? event.xPosition - 768
183 			: event.xPosition;
184 		movement.ydelta = event.yPosition > 255
185 			? event.yPosition - 512 : event.yPosition;
186 		movement.wheel_xdelta = 0;
187 		movement.wheel_ydelta = 0;
188 		movement.buttons = event.buttons;
189 		movement.timestamp = system_time();
190 
191 		_read->event = MS_READ;
192 		_read->u.mouse = movement;
193 	} else {
194 		event.yPosition = AREA_END_Y - (event.yPosition - AREA_START_Y);
195 
196 		_read->event = MS_READ_TOUCHPAD;
197 		_read->u.touchpad = event;
198 	}
199 
200 	return status;
201 }
202 
203 
204 status_t
205 probe_alps(ps2_dev* dev)
206 {
207 	int i;
208 	uint8 val[3];
209 	TRACE("ALPS: probe\n");
210 
211 	val[0] = 0;
212 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, val, 1, NULL, 0) != B_OK
213 		|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11, NULL, 0, NULL, 0)
214 			!= B_OK
215 		|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11, NULL, 0, NULL, 0)
216 			!= B_OK
217 		|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11, NULL, 0, NULL, 0)
218 			!= B_OK)
219 		return B_ERROR;
220 
221 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3)
222 		!= B_OK)
223 		return B_ERROR;
224 
225 	if (val[0] != 0 || val[1] != 0 || (val[2] != 10 && val[2] != 100))
226 		return B_ERROR;
227 
228 	val[0] = 0;
229 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, val, 1, NULL, 0) != B_OK
230 		|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE21, NULL, 0, NULL, 0)
231 			!= B_OK
232 		|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE21, NULL, 0, NULL, 0)
233 			!= B_OK
234 		|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE21, NULL, 0, NULL, 0)
235 			!= B_OK)
236 		return B_ERROR;
237 
238 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3)
239 		!= B_OK)
240 		return B_ERROR;
241 
242 	for (i = 0; ; i++) {
243 		const alps_model_info* info = &gALPSModelInfos[i];
244 		if (info->id[0] == 0) {
245 			INFO("ALPS not supported: %2.2x %2.2x %2.2x\n", val[0], val[1],
246 				val[2]);
247 			return B_ERROR;
248 		}
249 
250 		if (info->id[0] == val[0] && info->id[1] == val[1]
251 			&& info->id[2] == val[2]) {
252 			sFoundModel = (alps_model_info*)info;
253 			INFO("ALPS found: %2.2x %2.2x %2.2x\n", val[0], val[1], val[2]);
254 			break;
255 		}
256 	}
257 
258 	dev->name = kALPSPath[dev->idx];
259 	dev->packet_size = PS2_PACKET_ALPS;
260 
261 	return B_OK;
262 }
263 
264 
265 status_t
266 switch_hardware_tab(ps2_dev* dev, bool on)
267 {
268 	uint8 val[3];
269 	uint8 arg = 0x00;
270 	uint8 command = PS2_CMD_MOUSE_SET_RES;
271 	if (on) {
272 		arg = 0x0A;
273 		command = PS2_CMD_SET_TYPEMATIC;
274 	}
275 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3) != B_OK
276 		|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
277 		|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
278 		|| ps2_dev_command(dev, command, &arg, 1, NULL, 0) != B_OK)
279 		return B_ERROR;
280 
281 	return B_OK;
282 }
283 
284 
285 status_t
286 enable_passthrough(ps2_dev* dev, bool on)
287 {
288 	uint8 command = PS2_CMD_MOUSE_SET_SCALE11;
289 	if (on)
290 		command = PS2_CMD_MOUSE_SET_SCALE21;
291 
292 	if (ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK
293 		|| ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK
294 		|| ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK
295 		|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK)
296 		return B_ERROR;
297 
298 	return B_OK;
299 }
300 
301 
302 status_t
303 alps_open(const char *name, uint32 flags, void **_cookie)
304 {
305 	ps2_dev* dev;
306 	int i;
307 	for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
308 		if (0 == strcmp(ps2_device[i].name, name)) {
309 			dev = &ps2_device[i];
310 			break;
311 		}
312 	}
313 
314 	if (dev == NULL) {
315 		TRACE("ps2: dev = NULL\n");
316 		return B_ERROR;
317 	}
318 
319 	if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
320 		return B_BUSY;
321 
322 	alps_cookie* cookie = (alps_cookie*)malloc(sizeof(alps_cookie));
323 	if (cookie == NULL)
324 		goto err1;
325 	memset(cookie, 0, sizeof(*cookie));
326 
327 	cookie->previousZ = 0;
328 	*_cookie = cookie;
329 
330 	cookie->dev = dev;
331 	dev->cookie = cookie;
332 	dev->disconnect = &alps_disconnect;
333 	dev->handle_int = &alps_handle_int;
334 
335 	gHardwareSpecs.edgeMotionWidth = EDGE_MOTION_WIDTH;
336 
337 	gHardwareSpecs.areaStartX = AREA_START_X;
338 	gHardwareSpecs.areaEndX = AREA_END_X;
339 	gHardwareSpecs.areaStartY = AREA_START_Y;
340 	gHardwareSpecs.areaEndY = AREA_END_Y;
341 
342 	gHardwareSpecs.minPressure = MIN_PRESSURE;
343 	gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE;
344 	gHardwareSpecs.maxPressure = MAX_PRESSURE;
345 
346 	dev->packet_size = PS2_PACKET_ALPS;
347 
348 	cookie->ring_buffer = create_packet_buffer(
349 		ALPS_HISTORY_SIZE * dev->packet_size);
350 	if (cookie->ring_buffer == NULL) {
351 		TRACE("ALPS: can't allocate mouse actions buffer\n");
352 		goto err2;
353 	}
354 	// create the mouse semaphore, used for synchronization between
355 	// the interrupt handler and the read operation
356 	cookie->sem = create_sem(0, "ps2_alps_sem");
357 	if (cookie->sem < 0) {
358 		TRACE("ALPS: failed creating semaphore!\n");
359 		goto err3;
360 	}
361 
362 	if ((sFoundModel->flags & ALPS_PASS) != 0
363 		&& enable_passthrough(dev, true) != B_OK)
364 		goto err4;
365 
366 	// switch tap mode off
367 	if (switch_hardware_tab(dev, false) != B_OK)
368 		goto err4;
369 
370 	// init the alps device to absolut mode
371 	if (ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
372 		|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
373 		|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
374 		|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
375 		|| ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK)
376 		goto err4;
377 
378 	if ((sFoundModel->flags & ALPS_PASS) != 0
379 		&& enable_passthrough(dev, false) != B_OK)
380 		goto err4;
381 
382 	if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_STREAM, NULL, 0, NULL, 0) != B_OK)
383 		goto err4;
384 
385 	if (ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK)
386 		goto err4;
387 
388 	atomic_or(&dev->flags, PS2_FLAG_ENABLED);
389 
390 	TRACE("ALPS: open %s success\n", name);
391 	return B_OK;
392 
393 err4:
394 	delete_sem(cookie->sem);
395 err3:
396 	delete_packet_buffer(cookie->ring_buffer);
397 err2:
398 	free(cookie);
399 err1:
400 	atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
401 
402 	TRACE("ALPS: open %s failed\n", name);
403 	return B_ERROR;
404 }
405 
406 
407 status_t
408 alps_close(void *_cookie)
409 {
410 	alps_cookie *cookie = (alps_cookie*)_cookie;
411 
412 	ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
413 		150000);
414 
415 	delete_packet_buffer(cookie->ring_buffer);
416 	delete_sem(cookie->sem);
417 
418 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
419 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
420 
421 	// Reset the touchpad so it generate standard ps2 packets instead of
422 	// extended ones. If not, BeOS is confused with such packets when rebooting
423 	// without a complete shutdown.
424 	status_t status = ps2_reset_mouse(cookie->dev);
425 	if (status != B_OK) {
426 		INFO("ps2_alps: reset failed\n");
427 		return B_ERROR;
428 	}
429 
430 	TRACE("ALPS: close %s done\n", cookie->dev->name);
431 	return B_OK;
432 }
433 
434 
435 status_t
436 alps_freecookie(void *_cookie)
437 {
438 	free(_cookie);
439 	return B_OK;
440 }
441 
442 
443 status_t
444 alps_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
445 {
446 	alps_cookie *cookie = (alps_cookie*)_cookie;
447 	touchpad_read read;
448 	status_t status;
449 
450 	switch (op) {
451 		case MS_IS_TOUCHPAD:
452 			TRACE("ALPS: MS_IS_TOUCHPAD\n");
453 			if (buffer == NULL)
454 				return B_OK;
455 			return user_memcpy(buffer, &gHardwareSpecs, sizeof(gHardwareSpecs));
456 
457 		case MS_READ_TOUCHPAD:
458 			TRACE("ALPS: MS_READ get event\n");
459 			if (user_memcpy(&read.timeout, &(((touchpad_read*)buffer)->timeout),
460 					sizeof(bigtime_t)) != B_OK)
461 				return B_BAD_ADDRESS;
462 			if ((status = get_alps_movment(cookie, &read)) != B_OK)
463 				return status;
464 			return user_memcpy(buffer, &read, sizeof(read));
465 
466 		default:
467 			TRACE("ALPS: unknown opcode: %" B_PRIu32 "\n", op);
468 			return B_BAD_VALUE;
469 	}
470 }
471 
472 
473 static status_t
474 alps_read(void* cookie, off_t pos, void* buffer, size_t* _length)
475 {
476 	*_length = 0;
477 	return B_NOT_ALLOWED;
478 }
479 
480 
481 static status_t
482 alps_write(void* cookie, off_t pos, const void* buffer, size_t* _length)
483 {
484 	*_length = 0;
485 	return B_NOT_ALLOWED;
486 }
487 
488 
489 int32
490 alps_handle_int(ps2_dev* dev)
491 {
492 	alps_cookie* cookie = (alps_cookie*)dev->cookie;
493 
494 	uint8 val;
495 	val = cookie->dev->history[0].data;
496 	if (cookie->packet_index == 0
497 		&& (val & sFoundModel->maskFirstByte) != sFoundModel->firstByte) {
498 		INFO("ALPS: bad header, trying resync\n");
499 		cookie->packet_index = 0;
500 		return B_UNHANDLED_INTERRUPT;
501 	}
502 
503 	// data packages starting with a 0
504 	if (cookie->packet_index > 1 && (val & 0x80)) {
505 		INFO("ALPS: bad package data, trying resync\n");
506 		cookie->packet_index = 0;
507 		return B_UNHANDLED_INTERRUPT;
508 	}
509 
510  	cookie->buffer[cookie->packet_index] = val;
511 
512 	cookie->packet_index++;
513 	if (cookie->packet_index >= 6) {
514 		cookie->packet_index = 0;
515 
516 		if (packet_buffer_write(cookie->ring_buffer,
517 				cookie->buffer, cookie->dev->packet_size)
518 			!= cookie->dev->packet_size) {
519 			// buffer is full, drop new data
520 			return B_HANDLED_INTERRUPT;
521 		}
522 		release_sem_etc(cookie->sem, 1, B_DO_NOT_RESCHEDULE);
523 
524 		return B_INVOKE_SCHEDULER;
525 	}
526 
527 	return B_HANDLED_INTERRUPT;
528 }
529 
530 
531 void
532 alps_disconnect(ps2_dev *dev)
533 {
534 	alps_cookie *cookie = (alps_cookie*)dev->cookie;
535 	// the mouse device might not be opened at this point
536 	INFO("ALPS: alps_disconnect %s\n", dev->name);
537 	if ((dev->flags & PS2_FLAG_OPEN) != 0)
538 		release_sem(cookie->sem);
539 }
540 
541 
542 device_hooks gALPSDeviceHooks = {
543 	alps_open,
544 	alps_close,
545 	alps_freecookie,
546 	alps_ioctl,
547 	alps_read,
548 	alps_write,
549 };
550