xref: /haiku/src/add-ons/kernel/bus_managers/ps2/ps2_alps.cpp (revision 25a7b01d15612846f332751841da3579db313082)
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 "ps2_service.h"
19 
20 
21 static int32 generate_event(timer* timer);
22 
23 
24 const bigtime_t kEventInterval = 1000 * 50;
25 
26 
27 class EventProducer {
28 public:
29 	EventProducer()
30 	{
31 		fFired = false;
32 	}
33 
34 	status_t
35 	FireEvent(alps_cookie* cookie, uint8* package)
36 	{
37 		fCookie = cookie;
38 		memcpy(fLastPackage, package, sizeof(uint8) * PS2_PACKET_ALPS);
39 
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;
45 	}
46 
47 	bool
48 	CancelEvent()
49 	{
50 		if (!fFired)
51 			return false;
52 		fFired = false;
53 		return cancel_timer(&fEventTimer);
54 	}
55 
56 	void
57 	InjectEvent()
58 	{
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;
63 		}
64 		release_sem_etc(fCookie->sem, 1, B_DO_NOT_RESCHEDULE);
65 	}
66 
67 private:
68 	bool				fFired;
69 	uint8				fLastPackage[PS2_PACKET_ALPS];
70 	timer				fEventTimer;
71 	alps_cookie*		fCookie;
72 };
73 
74 
75 static EventProducer gEventProducer;
76 
77 
78 static int32
79 generate_event(timer* timer)
80 {
81 	gEventProducer.InjectEvent();
82 	return 0;
83 }
84 
85 
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"
91 };
92 
93 
94 typedef struct alps_model_info {
95 	uint8		id[3];
96 	uint8		firstByte;
97 	uint8		maskFirstByte;
98 	uint8		flags;
99 } alps_model_info;
100 
101 
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
105 
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
112 
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}
152 };
153 
154 
155 static alps_model_info* sFoundModel = NULL;
156 
157 
158 #define PS2_MOUSE_CMD_SET_SCALE11		0xe6
159 #define PS2_MOUSE_CMD_SET_SCALE21		0xe7
160 #define PS2_MOUSE_CMD_SET_RES			0xe8
161 #define PS2_MOUSE_CMD_GET_INFO			0xe9
162 #define PS2_MOUSE_CMD_SET_STREAM  		0xea
163 #define PS2_MOUSE_CMD_SET_POLL			0xf0
164 #define PS2_MOUSE_CMD_SET_RATE			0xf3
165 
166 
167 // touchpad proportions
168 #define EDGE_MOTION_WIDTH	55
169 // increase the touchpad size a little bit
170 #define AREA_START_X		40
171 #define AREA_END_X			987
172 #define AREA_START_Y		40
173 #define AREA_END_Y			734
174 
175 #define MIN_PRESSURE		15
176 #define REAL_MAX_PRESSURE	70
177 #define MAX_PRESSURE		115
178 
179 
180 #define ALPS_HISTORY_SIZE	256
181 
182 
183 static hardware_specs gHardwareSpecs;
184 
185 
186 /* Data taken from linux driver:
187 ALPS absolute Mode - new format
188 byte 0:  1    ?    ?    ?    1    ?    ?    ?
189 byte 1:  0   x6   x5   x4   x3   x2   x1   x0
190 byte 2:  0  x10   x9   x8   x7    ?  fin  ges
191 byte 3:  0   y9   y8   y7    1    M    R    L
192 byte 4:  0   y6   y5   y4   y3   y2   y1   y0
193 byte 5:  0   z6   z5   z4   z3   z2   z1   z0
194 */
195 static status_t
196 get_alps_movment(alps_cookie *cookie, mouse_movement *movement)
197 {
198 	status_t status;
199 	touch_event event;
200 	uint8 event_buffer[PS2_PACKET_ALPS];
201 
202 	status = acquire_sem_etc(cookie->sem, 1, B_CAN_INTERRUPT, 0);
203 	if (status < B_OK)
204 		return status;
205 
206 	if (!cookie->dev->active) {
207 		TRACE("ALPS: read_event: Error device no longer active\n");
208 		return B_ERROR;
209 	}
210 
211 	if (packet_buffer_read(cookie->ring_buffer, event_buffer,
212 			cookie->dev->packet_size) != cookie->dev->packet_size) {
213 		TRACE("ALPS: error copying buffer\n");
214 		return B_ERROR;
215 	}
216 
217 	event.buttons = event_buffer[3] & 7;
218 	event.zPressure = event_buffer[5];
219 
220 	// finger on touchpad
221 	if (event_buffer[2] & 0x2) {
222 		// finger with normal width
223 		event.wValue = 4;
224 	} else {
225 		event.wValue = 3;
226 	}
227 
228 	// tab gesture
229 	if (event_buffer[2] & 0x1) {
230 		event.zPressure = 60;
231 		event.wValue = 4;
232 	}
233 	// if hardware tab gesture is off a z pressure of 16 is reported
234 	if (cookie->previousZ == 0 && event.wValue == 4 && event.zPressure == 16)
235 		event.zPressure = 60;
236 
237 	cookie->previousZ = event.zPressure;
238 
239 	event.xPosition = event_buffer[1] | ((event_buffer[2] & 0x78) << 4);
240 	event.yPosition = event_buffer[4] | ((event_buffer[3] & 0x70) << 3);
241 
242 	// check for trackpoint even (z pressure 127)
243 	if (sFoundModel->flags & ALPS_DUALPOINT && event.zPressure == 127) {
244 		movement->xdelta = event.xPosition > 383 ? event.xPosition - 768
245 			: event.xPosition;
246 		movement->ydelta = event.yPosition > 255
247 			? event.yPosition - 512 : event.yPosition;
248 		movement->wheel_xdelta = 0;
249 		movement->wheel_ydelta = 0;
250 		movement->buttons = event.buttons;
251 		movement->timestamp = system_time();
252 		cookie->movementMaker.UpdateButtons(movement);
253 	} else {
254 		event.yPosition = AREA_END_Y - (event.yPosition - AREA_START_Y);
255 		status = cookie->movementMaker.EventToMovement(&event, movement);
256 	}
257 
258 	if (cookie->movementMaker.WasEdgeMotion()
259 		|| cookie->movementMaker.TapDragStarted()) {
260 		gEventProducer.FireEvent(cookie, event_buffer);
261 	}
262 
263 	return status;
264 }
265 
266 
267 static void
268 default_settings(touchpad_settings *set)
269 {
270 	memcpy(set, &kDefaultTouchpadSettings, sizeof(touchpad_settings));
271 }
272 
273 
274 status_t
275 probe_alps(ps2_dev* dev)
276 {
277 	int i;
278 	uint8 val[3];
279 	TRACE("ALPS: probe\n");
280 
281 	val[0] = 0;
282 	if (ps2_dev_command(dev, PS2_MOUSE_CMD_SET_RES, val, 1, NULL, 0) != B_OK
283 		|| ps2_dev_command(dev, PS2_MOUSE_CMD_SET_SCALE11, NULL, 0, NULL, 0)
284 			!= B_OK
285 		|| ps2_dev_command(dev, PS2_MOUSE_CMD_SET_SCALE11, NULL, 0, NULL, 0)
286 			!= B_OK
287 		|| ps2_dev_command(dev, PS2_MOUSE_CMD_SET_SCALE11, NULL, 0, NULL, 0)
288 			!= B_OK)
289 		return B_ERROR;
290 
291 	if (ps2_dev_command(dev, PS2_MOUSE_CMD_GET_INFO, NULL, 0, val, 3)
292 		!= B_OK)
293 		return B_ERROR;
294 
295 	if (val[0] != 0 || val[1] != 0 || (val[2] != 10 && val[2] != 100))
296 		return B_ERROR;
297 
298 	val[0] = 0;
299 	if (ps2_dev_command(dev, PS2_MOUSE_CMD_SET_RES, val, 1, NULL, 0) != B_OK
300 		|| ps2_dev_command(dev, PS2_MOUSE_CMD_SET_SCALE21, NULL, 0, NULL, 0)
301 			!= B_OK
302 		|| ps2_dev_command(dev, PS2_MOUSE_CMD_SET_SCALE21, NULL, 0, NULL, 0)
303 			!= B_OK
304 		|| ps2_dev_command(dev, PS2_MOUSE_CMD_SET_SCALE21, NULL, 0, NULL, 0)
305 			!= B_OK)
306 		return B_ERROR;
307 
308 	if (ps2_dev_command(dev, PS2_MOUSE_CMD_GET_INFO, NULL, 0, val, 3)
309 		!= B_OK)
310 		return B_ERROR;
311 
312 	for (i = 0; ; i++) {
313 		const alps_model_info* info = &gALPSModelInfos[i];
314 		if (info->id[0] == 0) {
315 			INFO("ALPS not supported: %2.2x %2.2x %2.2x\n", val[0], val[1],
316 				val[2]);
317 			return B_ERROR;
318 		}
319 
320 		if (info->id[0] == val[0] && info->id[1] == val[1]
321 			&& info->id[2] == val[2]) {
322 			sFoundModel = (alps_model_info*)info;
323 			INFO("ALPS found: %2.2x %2.2x %2.2x\n", val[0], val[1], val[2]);
324 			break;
325 		}
326 	}
327 
328 	dev->name = kALPSPath[dev->idx];
329 	dev->packet_size = PS2_PACKET_ALPS;
330 
331 	return B_OK;
332 }
333 
334 
335 status_t
336 switch_hardware_tab(ps2_dev* dev, bool on)
337 {
338 	uint8 val[3];
339 	uint8 arg = 0x00;
340 	uint8 command = PS2_MOUSE_CMD_SET_RES;
341 	if (on) {
342 		arg = 0x0A;
343 		command = PS2_MOUSE_CMD_SET_RATE;
344 	}
345 	if (ps2_dev_command(dev, PS2_MOUSE_CMD_GET_INFO, NULL, 0, val, 3) != B_OK
346 		|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
347 		|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
348 		|| ps2_dev_command(dev, command, &arg, 1, NULL, 0) != B_OK)
349 		return B_ERROR;
350 
351 	return B_OK;
352 }
353 
354 
355 status_t
356 enable_passthrough(ps2_dev* dev, bool on)
357 {
358 	uint8 command = PS2_MOUSE_CMD_SET_SCALE11;
359 	if (on)
360 		command = PS2_MOUSE_CMD_SET_SCALE21;
361 
362 	if (ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK
363 		|| ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK
364 		|| ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK
365 		|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK)
366 		return B_ERROR;
367 
368 	return B_OK;
369 }
370 
371 
372 status_t
373 alps_open(const char *name, uint32 flags, void **_cookie)
374 {
375 	ps2_dev* dev;
376 	int i;
377 	for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
378 		if (0 == strcmp(ps2_device[i].name, name)) {
379 			dev = &ps2_device[i];
380 			break;
381 		}
382 	}
383 
384 	if (dev == NULL) {
385 		TRACE("ps2: dev = NULL\n");
386 		return B_ERROR;
387 	}
388 
389 	if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
390 		return B_BUSY;
391 
392 	alps_cookie* cookie = (alps_cookie*)malloc(sizeof(alps_cookie));
393 	if (cookie == NULL)
394 		goto err1;
395 	memset(cookie, 0, sizeof(*cookie));
396 
397 	cookie->movementMaker.Init();
398 	cookie->previousZ = 0;
399 	*_cookie = cookie;
400 
401 	cookie->dev = dev;
402 	dev->cookie = cookie;
403 	dev->disconnect = &alps_disconnect;
404 	dev->handle_int = &alps_handle_int;
405 
406 	default_settings(&cookie->settings);
407 
408 	gHardwareSpecs.edgeMotionWidth = EDGE_MOTION_WIDTH;
409 
410 	gHardwareSpecs.areaStartX = AREA_START_X;
411 	gHardwareSpecs.areaEndX = AREA_END_X;
412 	gHardwareSpecs.areaStartY = AREA_START_Y;
413 	gHardwareSpecs.areaEndY = AREA_END_Y;
414 
415 	gHardwareSpecs.minPressure = MIN_PRESSURE;
416 	gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE;
417 	gHardwareSpecs.maxPressure = MAX_PRESSURE;
418 
419 	cookie->movementMaker.SetSettings(&cookie->settings);
420 	cookie->movementMaker.SetSpecs(&gHardwareSpecs);
421 
422 	dev->packet_size = PS2_PACKET_ALPS;
423 
424 	cookie->ring_buffer = create_packet_buffer(
425 		ALPS_HISTORY_SIZE * dev->packet_size);
426 	if (cookie->ring_buffer == NULL) {
427 		TRACE("ALPS: can't allocate mouse actions buffer\n");
428 		goto err2;
429 	}
430 	// create the mouse semaphore, used for synchronization between
431 	// the interrupt handler and the read operation
432 	cookie->sem = create_sem(0, "ps2_alps_sem");
433 	if (cookie->sem < 0) {
434 		TRACE("ALPS: failed creating semaphore!\n");
435 		goto err3;
436 	}
437 
438 	if ((sFoundModel->flags & ALPS_PASS) != 0
439 		&& enable_passthrough(dev, true) != B_OK)
440 		goto err4;
441 
442 	// switch tap mode off
443 	if (switch_hardware_tab(dev, false) != B_OK)
444 		goto err4;
445 
446 	// init the alps device to absolut mode
447 	if (ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
448 		|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
449 		|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
450 		|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
451 		|| ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK)
452 		goto err4;
453 
454 	if ((sFoundModel->flags & ALPS_PASS) != 0
455 		&& enable_passthrough(dev, false) != B_OK)
456 		goto err4;
457 
458 	if (ps2_dev_command(dev, PS2_MOUSE_CMD_SET_STREAM, NULL, 0, NULL, 0) != B_OK)
459 		goto err4;
460 
461 	if (ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK)
462 		goto err4;
463 
464 	atomic_or(&dev->flags, PS2_FLAG_ENABLED);
465 
466 	TRACE("ALPS: open %s success\n", name);
467 	return B_OK;
468 
469 err4:
470 	delete_sem(cookie->sem);
471 err3:
472 	delete_packet_buffer(cookie->ring_buffer);
473 err2:
474 	free(cookie);
475 err1:
476 	atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
477 
478 	TRACE("ALPS: open %s failed\n", name);
479 	return B_ERROR;
480 }
481 
482 
483 status_t
484 alps_close(void *_cookie)
485 {
486 	gEventProducer.CancelEvent();
487 
488 	alps_cookie *cookie = (alps_cookie*)_cookie;
489 
490 	ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
491 		150000);
492 
493 	delete_packet_buffer(cookie->ring_buffer);
494 	delete_sem(cookie->sem);
495 
496 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
497 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
498 
499 	// Reset the touchpad so it generate standard ps2 packets instead of
500 	// extended ones. If not, BeOS is confused with such packets when rebooting
501 	// without a complete shutdown.
502 	status_t status = ps2_reset_mouse(cookie->dev);
503 	if (status != B_OK) {
504 		INFO("ps2: reset failed\n");
505 		return B_ERROR;
506 	}
507 
508 	TRACE("ALPS: close %s done\n", cookie->dev->name);
509 	return B_OK;
510 }
511 
512 
513 status_t
514 alps_freecookie(void *_cookie)
515 {
516 	free(_cookie);
517 	return B_OK;
518 }
519 
520 
521 status_t
522 alps_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
523 {
524 	alps_cookie *cookie = (alps_cookie*)_cookie;
525 	mouse_movement movement;
526 	status_t status;
527 
528 	switch (op) {
529 		case MS_READ:
530 			TRACE("ALPS: MS_READ get event\n");
531 			if ((status = get_alps_movment(cookie, &movement)) != B_OK)
532 				return status;
533 			return user_memcpy(buffer, &movement, sizeof(movement));
534 
535 		case MS_IS_TOUCHPAD:
536 			TRACE("ALPS: MS_IS_TOUCHPAD\n");
537 			return B_OK;
538 
539 		case MS_SET_TOUCHPAD_SETTINGS:
540 			TRACE("ALPS: MS_SET_TOUCHPAD_SETTINGS");
541 			user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings));
542 			return B_OK;
543 
544 		case MS_SET_CLICKSPEED:
545 			TRACE("ALPS: ioctl MS_SETCLICK (set click speed)\n");
546 			return user_memcpy(&cookie->movementMaker.click_speed, buffer,
547 				sizeof(bigtime_t));
548 
549 		default:
550 			TRACE("ALPS: unknown opcode: %ld\n", op);
551 			return B_BAD_VALUE;
552 	}
553 }
554 
555 
556 static status_t
557 alps_read(void* cookie, off_t pos, void* buffer, size_t* _length)
558 {
559 	*_length = 0;
560 	return B_NOT_ALLOWED;
561 }
562 
563 
564 static status_t
565 alps_write(void* cookie, off_t pos, const void* buffer, size_t* _length)
566 {
567 	*_length = 0;
568 	return B_NOT_ALLOWED;
569 }
570 
571 
572 int32
573 alps_handle_int(ps2_dev* dev)
574 {
575 	alps_cookie* cookie = (alps_cookie*)dev->cookie;
576 
577 	// we got a real event cancel the fake event
578 	gEventProducer.CancelEvent();
579 
580 	uint8 val;
581 	val = cookie->dev->history[0].data;
582 	if (cookie->packet_index == 0
583 		&& (val & sFoundModel->maskFirstByte) != sFoundModel->firstByte) {
584 		INFO("ALPS: bad header, trying resync\n");
585 		cookie->packet_index = 0;
586 		return B_UNHANDLED_INTERRUPT;
587 	}
588 
589 	// data packages starting with a 0
590 	if (cookie->packet_index > 1 && (val & 0x80)) {
591 		INFO("ALPS: bad package data, trying resync\n");
592 		cookie->packet_index = 0;
593 		return B_UNHANDLED_INTERRUPT;
594 	}
595 
596  	cookie->buffer[cookie->packet_index] = val;
597 
598 	cookie->packet_index++;
599 	if (cookie->packet_index >= 6) {
600 		cookie->packet_index = 0;
601 
602 		if (packet_buffer_write(cookie->ring_buffer,
603 				cookie->buffer, cookie->dev->packet_size)
604 			!= cookie->dev->packet_size) {
605 			// buffer is full, drop new data
606 			return B_HANDLED_INTERRUPT;
607 		}
608 		release_sem_etc(cookie->sem, 1, B_DO_NOT_RESCHEDULE);
609 
610 		return B_INVOKE_SCHEDULER;
611 	}
612 
613 	return B_HANDLED_INTERRUPT;
614 }
615 
616 
617 void
618 alps_disconnect(ps2_dev *dev)
619 {
620 	alps_cookie *cookie = (alps_cookie*)dev->cookie;
621 	// the mouse device might not be opened at this point
622 	INFO("ALPS: alps_disconnect %s\n", dev->name);
623 	if ((dev->flags & PS2_FLAG_OPEN) != 0)
624 		release_sem(cookie->sem);
625 }
626 
627 
628 device_hooks gALPSDeviceHooks = {
629 	alps_open,
630 	alps_close,
631 	alps_freecookie,
632 	alps_ioctl,
633 	alps_read,
634 	alps_write,
635 };
636 
637