xref: /haiku/src/add-ons/kernel/bus_managers/ps2/ps2_standard_mouse.cpp (revision fdbbdccc0568fd34dfdef028ad50d3b7722e0cfa)
1e637ccf5SClemens Zeidler /*
2819b4547SJohn Scipione  * Copyright 2001-2014 Haiku, Inc. All rights reserved.
3e637ccf5SClemens Zeidler  * Distributed under the terms of the MIT License.
4e637ccf5SClemens Zeidler  *
5e637ccf5SClemens Zeidler  * Authors (in chronological order):
6819b4547SJohn Scipione  *		Elad Lahav, elad@eldarshany.com
7819b4547SJohn Scipione  *		Stefano Ceccherini, burton666@libero.it
8e637ccf5SClemens Zeidler  *		Axel Dörfler, axeld@pinc-software.de
9819b4547SJohn Scipione  *		Marcus Overhagen, marcus@overhagen.de
10819b4547SJohn Scipione  *		Clemens Zeidler, czeidler@gmx.de
11819b4547SJohn Scipione  *		John Scipione, jscipione@gmail.com
12e637ccf5SClemens Zeidler  */
13e637ccf5SClemens Zeidler 
14e637ccf5SClemens Zeidler 
15e637ccf5SClemens Zeidler /*!	PS/2 mouse device driver
16e637ccf5SClemens Zeidler 
17e637ccf5SClemens Zeidler 	A PS/2 mouse is connected to the IBM 8042 controller, and gets its
18e637ccf5SClemens Zeidler 	name from the IBM PS/2 personal computer, which was the first to
19e637ccf5SClemens Zeidler 	use this device. All resources are shared between the keyboard, and
20e637ccf5SClemens Zeidler 	the mouse, referred to as the "Auxiliary Device".
21e637ccf5SClemens Zeidler 
22e637ccf5SClemens Zeidler 	I/O:
23e637ccf5SClemens Zeidler 	~~~
24e637ccf5SClemens Zeidler 	The controller has 3 I/O registers:
25e637ccf5SClemens Zeidler 	1. Status (input), mapped to port 64h
26e637ccf5SClemens Zeidler 	2. Control (output), mapped to port 64h
27e637ccf5SClemens Zeidler 	3. Data (input/output), mapped to port 60h
28e637ccf5SClemens Zeidler 
29e637ccf5SClemens Zeidler 	Data:
30e637ccf5SClemens Zeidler 	~~~~
31e637ccf5SClemens Zeidler 	A packet read from the mouse data port is composed of
32e637ccf5SClemens Zeidler 	three bytes:
33e637ccf5SClemens Zeidler 		byte 0: status byte, where
34e637ccf5SClemens Zeidler 			- bit 7: Y overflow (1 = true)
35e637ccf5SClemens Zeidler 			- bit 6: X overflow (1 = true)
36e637ccf5SClemens Zeidler 			- bit 5: MSB of Y offset
37e637ccf5SClemens Zeidler 			- bit 4: MSB of X offset
38e637ccf5SClemens Zeidler 			- bit 3: Syncronization bit (always 1)
39e637ccf5SClemens Zeidler 			- bit 2: Middle button (1 = down)
40e637ccf5SClemens Zeidler 			- bit 1: Right button (1 = down)
41e637ccf5SClemens Zeidler 			- bit 0: Left button (1 = down)
42e637ccf5SClemens Zeidler 		byte 1: X position change, since last probed (-127 to +127)
43e637ccf5SClemens Zeidler 		byte 2: Y position change, since last probed (-127 to +127)
44e637ccf5SClemens Zeidler 
45e637ccf5SClemens Zeidler 	Intellimouse mice send a four byte packet, where the first three
46e637ccf5SClemens Zeidler 	bytes are the same as standard mice, and the last one reports the
47e637ccf5SClemens Zeidler 	Z position, which is, usually, the wheel movement.
48e637ccf5SClemens Zeidler 
49e637ccf5SClemens Zeidler 	Interrupts:
50e637ccf5SClemens Zeidler 	~~~~~~~~~~
51e637ccf5SClemens Zeidler 	The PS/2 mouse device is connected to interrupt 12.
52e637ccf5SClemens Zeidler 	The controller uses 3 consecutive interrupts to inform the computer
53e637ccf5SClemens Zeidler 	that it has new data. On the first the data register holds the status
54e637ccf5SClemens Zeidler 	byte, on the second the X offset, and on the 3rd the Y offset.
55e637ccf5SClemens Zeidler */
56e637ccf5SClemens Zeidler 
57e637ccf5SClemens Zeidler 
58e637ccf5SClemens Zeidler #include <stdlib.h>
59e637ccf5SClemens Zeidler #include <string.h>
60e637ccf5SClemens Zeidler 
61e637ccf5SClemens Zeidler #include <keyboard_mouse_driver.h>
62e637ccf5SClemens Zeidler 
63e637ccf5SClemens Zeidler #include "ps2_service.h"
64e637ccf5SClemens Zeidler #include "ps2_standard_mouse.h"
65e637ccf5SClemens Zeidler 
66e637ccf5SClemens Zeidler 
67*fdbbdcccSAdrien Destugues //#define TRACE_PS2_MOUSE
68*fdbbdcccSAdrien Destugues #ifdef TRACE_PS2_MOUSE
69*fdbbdcccSAdrien Destugues #	define TRACE(x...) dprintf(x)
70*fdbbdcccSAdrien Destugues #else
71*fdbbdcccSAdrien Destugues #	define TRACE(x...)
72*fdbbdcccSAdrien Destugues #endif
73*fdbbdcccSAdrien Destugues 
74*fdbbdcccSAdrien Destugues 
75e637ccf5SClemens Zeidler const char* kStandardMousePath[4] = {
76e637ccf5SClemens Zeidler 	"input/mouse/ps2/standard_0",
77e637ccf5SClemens Zeidler 	"input/mouse/ps2/standard_1",
78e637ccf5SClemens Zeidler 	"input/mouse/ps2/standard_2",
79e637ccf5SClemens Zeidler 	"input/mouse/ps2/standard_3"
80e637ccf5SClemens Zeidler };
81819b4547SJohn Scipione 
82e637ccf5SClemens Zeidler const char* kIntelliMousePath[4] = {
83e637ccf5SClemens Zeidler 	"input/mouse/ps2/intelli_0",
84e637ccf5SClemens Zeidler 	"input/mouse/ps2/intelli_1",
85e637ccf5SClemens Zeidler 	"input/mouse/ps2/intelli_2",
86e637ccf5SClemens Zeidler 	"input/mouse/ps2/intelli_3"
87e637ccf5SClemens Zeidler };
88e637ccf5SClemens Zeidler 
89e637ccf5SClemens Zeidler 
90819b4547SJohn Scipione //!	Set sampling rate of the ps2 port.
91e637ccf5SClemens Zeidler static inline status_t
ps2_set_sample_rate(ps2_dev * dev,uint8 rate)92e637ccf5SClemens Zeidler ps2_set_sample_rate(ps2_dev* dev, uint8 rate)
93e637ccf5SClemens Zeidler {
94e637ccf5SClemens Zeidler 	return ps2_dev_command(dev, PS2_CMD_SET_SAMPLE_RATE, &rate, 1, NULL, 0);
95e637ccf5SClemens Zeidler }
96e637ccf5SClemens Zeidler 
97e637ccf5SClemens Zeidler 
98819b4547SJohn Scipione //!	Converts a packet received by the mouse to a "movement".
99e637ccf5SClemens Zeidler static void
ps2_packet_to_movement(standard_mouse_cookie * cookie,uint8 packet[],mouse_movement * pos)100e637ccf5SClemens Zeidler ps2_packet_to_movement(standard_mouse_cookie* cookie, uint8 packet[],
101e637ccf5SClemens Zeidler 	mouse_movement* pos)
102e637ccf5SClemens Zeidler {
103e637ccf5SClemens Zeidler 	int buttons = packet[0] & 7;
104e637ccf5SClemens Zeidler 	int xDelta = ((packet[0] & 0x10) ? ~0xff : 0) | packet[1];
105e637ccf5SClemens Zeidler 	int yDelta = ((packet[0] & 0x20) ? ~0xff : 0) | packet[2];
106e637ccf5SClemens Zeidler 	int xDeltaWheel = 0;
107e637ccf5SClemens Zeidler 	int yDeltaWheel = 0;
108e637ccf5SClemens Zeidler 	bigtime_t currentTime = system_time();
109e637ccf5SClemens Zeidler 
110e637ccf5SClemens Zeidler 	if (buttons != 0 && cookie->buttons_state == 0) {
111e637ccf5SClemens Zeidler 		if (cookie->click_last_time + cookie->click_speed > currentTime)
112e637ccf5SClemens Zeidler 			cookie->click_count++;
113e637ccf5SClemens Zeidler 		else
114e637ccf5SClemens Zeidler 			cookie->click_count = 1;
115e637ccf5SClemens Zeidler 
116e637ccf5SClemens Zeidler 		cookie->click_last_time = currentTime;
117e637ccf5SClemens Zeidler 	}
118e637ccf5SClemens Zeidler 
119e637ccf5SClemens Zeidler 	cookie->buttons_state = buttons;
120e637ccf5SClemens Zeidler 
121e637ccf5SClemens Zeidler 	if (cookie->flags & F_MOUSE_TYPE_INTELLIMOUSE) {
122e637ccf5SClemens Zeidler 		yDeltaWheel = packet[3] & 0x07;
123e637ccf5SClemens Zeidler  		if (packet[3] & 0x08)
124e637ccf5SClemens Zeidler 			yDeltaWheel |= ~0x07;
125e637ccf5SClemens Zeidler 	}
126819b4547SJohn Scipione #if 0
127e637ccf5SClemens Zeidler 	if (cookie->flags & F_MOUSE_TYPE_2WHEELS) {
128e637ccf5SClemens Zeidler 		switch (packet[3] & 0x0F) {
129e637ccf5SClemens Zeidler 			case 0x01: yDeltaWheel = +1; break; // wheel 1 down
130e637ccf5SClemens Zeidler 			case 0x0F: yDeltaWheel = -1; break; // wheel 1 up
131e637ccf5SClemens Zeidler 			case 0x02: xDeltaWheel = +1; break; // wheel 2 down
132e637ccf5SClemens Zeidler 			case 0x0E: xDeltaWheel = -1; break; // wheel 2 up
133e637ccf5SClemens Zeidler 		}
134e637ccf5SClemens Zeidler 	}
135819b4547SJohn Scipione #endif
136e637ccf5SClemens Zeidler 
137819b4547SJohn Scipione #if 0
138819b4547SJohn Scipione 	TRACE("packet: %02x %02x %02x %02x: xd %d, yd %d, 0x%x (%d), w-xd %d, "
139819b4547SJohn Scipione 		"w-yd %d\n", packet[0], packet[1], packet[2], packet[3],
140819b4547SJohn Scipione 		xDelta, yDelta, buttons, cookie->click_count, xDeltaWheel,
141819b4547SJohn Scipione 		yDeltaWheel);
142819b4547SJohn Scipione #endif
143e637ccf5SClemens Zeidler 
144819b4547SJohn Scipione 	if (pos != NULL) {
145e637ccf5SClemens Zeidler 		pos->xdelta = xDelta;
146e637ccf5SClemens Zeidler 		pos->ydelta = yDelta;
147e637ccf5SClemens Zeidler 		pos->buttons = buttons;
148e637ccf5SClemens Zeidler 		pos->clicks = cookie->click_count;
149e637ccf5SClemens Zeidler 		pos->modifiers = 0;
150e637ccf5SClemens Zeidler 		pos->timestamp = currentTime;
151e637ccf5SClemens Zeidler 		pos->wheel_ydelta = yDeltaWheel;
152e637ccf5SClemens Zeidler 		pos->wheel_xdelta = xDeltaWheel;
153e637ccf5SClemens Zeidler 
154e637ccf5SClemens Zeidler 		TRACE("ps2: ps2_packet_to_movement xdelta: %d, ydelta: %d, buttons %x, "
155ebdb22deSZoltán Mizsei 			"clicks: %d, timestamp %" B_PRIdBIGTIME "\n",
156e637ccf5SClemens Zeidler 			xDelta, yDelta, buttons, cookie->click_count, currentTime);
157e637ccf5SClemens Zeidler 	}
158e637ccf5SClemens Zeidler }
159e637ccf5SClemens Zeidler 
160e637ccf5SClemens Zeidler 
161819b4547SJohn Scipione //!	Read a mouse event from the mouse events chain buffer.
162e637ccf5SClemens Zeidler static status_t
standard_mouse_read_event(standard_mouse_cookie * cookie,mouse_movement * movement)163e637ccf5SClemens Zeidler standard_mouse_read_event(standard_mouse_cookie* cookie,
164e637ccf5SClemens Zeidler 	mouse_movement* movement)
165e637ccf5SClemens Zeidler {
166e637ccf5SClemens Zeidler 	uint8 packet[PS2_MAX_PACKET_SIZE];
167e637ccf5SClemens Zeidler 	status_t status;
168e637ccf5SClemens Zeidler 
169e637ccf5SClemens Zeidler 	TRACE("ps2: standard_mouse_read_event\n");
170e637ccf5SClemens Zeidler 
171e637ccf5SClemens Zeidler 	status = acquire_sem_etc(cookie->standard_mouse_sem, 1, B_CAN_INTERRUPT, 0);
172e637ccf5SClemens Zeidler 	TRACE("ps2: standard_mouse_read_event acquired\n");
173e637ccf5SClemens Zeidler 	if (status < B_OK)
174e637ccf5SClemens Zeidler 		return status;
175e637ccf5SClemens Zeidler 
176e637ccf5SClemens Zeidler 	if (!cookie->dev->active) {
177e637ccf5SClemens Zeidler 		TRACE("ps2: standard_mouse_read_event: Error device no longer "
178e637ccf5SClemens Zeidler 			"active\n");
179e637ccf5SClemens Zeidler 		return B_ERROR;
180e637ccf5SClemens Zeidler 	}
181e637ccf5SClemens Zeidler 
182e637ccf5SClemens Zeidler 	if (packet_buffer_read(cookie->standard_mouse_buffer, packet,
183e637ccf5SClemens Zeidler 		cookie->dev->packet_size) != cookie->dev->packet_size) {
184e637ccf5SClemens Zeidler 		TRACE("ps2: error copying buffer\n");
185e637ccf5SClemens Zeidler 		return B_ERROR;
186e637ccf5SClemens Zeidler 	}
187e637ccf5SClemens Zeidler 
188e637ccf5SClemens Zeidler 	if (!(packet[0] & 8))
189e637ccf5SClemens Zeidler 		panic("ps2: got broken data from packet_buffer_read\n");
190e637ccf5SClemens Zeidler 
191e637ccf5SClemens Zeidler 	ps2_packet_to_movement(cookie, packet, movement);
192e637ccf5SClemens Zeidler 	return B_OK;
193e637ccf5SClemens Zeidler }
194e637ccf5SClemens Zeidler 
195e637ccf5SClemens Zeidler 
196819b4547SJohn Scipione //	#pragma mark - Interrupt handler functions
197e637ccf5SClemens Zeidler 
198e637ccf5SClemens Zeidler 
199e637ccf5SClemens Zeidler void
standard_mouse_disconnect(ps2_dev * dev)200e637ccf5SClemens Zeidler standard_mouse_disconnect(ps2_dev* dev)
201e637ccf5SClemens Zeidler {
202e637ccf5SClemens Zeidler 	// the mouse device might not be opened at this point
203e637ccf5SClemens Zeidler 	INFO("ps2: ps2_standard_mouse_disconnect %s\n", dev->name);
204e637ccf5SClemens Zeidler 	if (dev->flags & PS2_FLAG_OPEN)
205e637ccf5SClemens Zeidler 		release_sem(((standard_mouse_cookie*)dev->cookie)->standard_mouse_sem);
206e637ccf5SClemens Zeidler }
207e637ccf5SClemens Zeidler 
208e637ccf5SClemens Zeidler 
209e637ccf5SClemens Zeidler /*!	Interrupt handler for the mouse device. Called whenever the I/O
210e637ccf5SClemens Zeidler 	controller generates an interrupt for the PS/2 mouse. Reads mouse
211e637ccf5SClemens Zeidler 	information from the data port, and stores it, so it can be accessed
212e637ccf5SClemens Zeidler 	by read() operations. The full data is obtained using 3 consecutive
213e637ccf5SClemens Zeidler 	calls to the handler, each holds a different byte on the data port.
214e637ccf5SClemens Zeidler */
215e637ccf5SClemens Zeidler int32
standard_mouse_handle_int(ps2_dev * dev)216e637ccf5SClemens Zeidler standard_mouse_handle_int(ps2_dev* dev)
217e637ccf5SClemens Zeidler {
218e637ccf5SClemens Zeidler 	standard_mouse_cookie* cookie = (standard_mouse_cookie*)dev->cookie;
219e637ccf5SClemens Zeidler 	const uint8 data = dev->history[0].data;
220e637ccf5SClemens Zeidler 
221e637ccf5SClemens Zeidler 	TRACE("ps2: standard mouse: %1x\t%1x\t%1x\t%1x\t%1x\t%1x\t%1x\t%1x\n",
222e637ccf5SClemens Zeidler 		data >> 7 & 1, data >> 6 & 1, data >> 5 & 1,
223e637ccf5SClemens Zeidler 		data >> 4 & 1, data >> 3 & 1, data >> 2 & 1,
224e637ccf5SClemens Zeidler 		data >> 1 & 1, data >> 0 & 1);
225e637ccf5SClemens Zeidler 
226819b4547SJohn Scipione 	if (cookie->packet_index == 0 && (data & 8) == 0) {
227e637ccf5SClemens Zeidler 		INFO("ps2: bad mouse data, trying resync\n");
228e637ccf5SClemens Zeidler 		return B_HANDLED_INTERRUPT;
229e637ccf5SClemens Zeidler 	}
230e637ccf5SClemens Zeidler 
231e637ccf5SClemens Zeidler 	// Workarounds for active multiplexing keyboard controllers
232e637ccf5SClemens Zeidler 	// that lose data or send them to the wrong port.
233819b4547SJohn Scipione 	if (cookie->packet_index == 0 && (data & 0xc0) != 0) {
234e637ccf5SClemens Zeidler 		INFO("ps2: strange mouse data, x/y overflow, trying resync\n");
235e637ccf5SClemens Zeidler 		return B_HANDLED_INTERRUPT;
236e637ccf5SClemens Zeidler 	}
237819b4547SJohn Scipione 
238e637ccf5SClemens Zeidler 	cookie->buffer[cookie->packet_index++] = data;
239e637ccf5SClemens Zeidler 
240e637ccf5SClemens Zeidler 	if (cookie->packet_index != dev->packet_size) {
241e637ccf5SClemens Zeidler 		// packet not yet complete
242e637ccf5SClemens Zeidler 		return B_HANDLED_INTERRUPT;
243e637ccf5SClemens Zeidler 	}
244e637ccf5SClemens Zeidler 
245e637ccf5SClemens Zeidler 	// complete packet is assembled
246e637ccf5SClemens Zeidler 
247e637ccf5SClemens Zeidler 	cookie->packet_index = 0;
248e637ccf5SClemens Zeidler 	if (packet_buffer_write(cookie->standard_mouse_buffer,
249e637ccf5SClemens Zeidler 		cookie->buffer, dev->packet_size) != dev->packet_size) {
250e637ccf5SClemens Zeidler 		// buffer is full, drop new data
251e637ccf5SClemens Zeidler 		return B_HANDLED_INTERRUPT;
252e637ccf5SClemens Zeidler 	}
253e637ccf5SClemens Zeidler 
254e637ccf5SClemens Zeidler 	release_sem_etc(cookie->standard_mouse_sem, 1, B_DO_NOT_RESCHEDULE);
255819b4547SJohn Scipione 
256e637ccf5SClemens Zeidler 	return B_INVOKE_SCHEDULER;
257e637ccf5SClemens Zeidler }
258e637ccf5SClemens Zeidler 
259e637ccf5SClemens Zeidler 
260819b4547SJohn Scipione //	#pragma mark - probe_standard_mouse()
261e637ccf5SClemens Zeidler 
262e637ccf5SClemens Zeidler 
263e637ccf5SClemens Zeidler status_t
probe_standard_mouse(ps2_dev * dev)264e637ccf5SClemens Zeidler probe_standard_mouse(ps2_dev* dev)
265e637ccf5SClemens Zeidler {
266e637ccf5SClemens Zeidler 	status_t status;
267e637ccf5SClemens Zeidler 	uint8 deviceId = 0;
268e637ccf5SClemens Zeidler 
269e637ccf5SClemens Zeidler 	// get device id
270e637ccf5SClemens Zeidler 	status = ps2_dev_command(dev, PS2_CMD_GET_DEVICE_ID, NULL, 0,
271e637ccf5SClemens Zeidler 		&deviceId, 1);
272e637ccf5SClemens Zeidler 	if (status != B_OK) {
273e637ccf5SClemens Zeidler 		INFO("ps2: probe_mouse get device id failed\n");
274e637ccf5SClemens Zeidler 		return B_ERROR;
275e637ccf5SClemens Zeidler 	}
276e637ccf5SClemens Zeidler 
277e637ccf5SClemens Zeidler 	TRACE("ps2: probe_mouse device id: %2x\n", deviceId);
278e637ccf5SClemens Zeidler 
279e637ccf5SClemens Zeidler 	// check for MS Intellimouse
280e637ccf5SClemens Zeidler 	if (deviceId == 0) {
281e637ccf5SClemens Zeidler 		uint8 alternate_device_id;
282e637ccf5SClemens Zeidler 		status  = ps2_set_sample_rate(dev, 200);
283e637ccf5SClemens Zeidler 		status |= ps2_set_sample_rate(dev, 100);
284e637ccf5SClemens Zeidler 		status |= ps2_set_sample_rate(dev, 80);
285e637ccf5SClemens Zeidler 		status |= ps2_dev_command(dev, PS2_CMD_GET_DEVICE_ID, NULL, 0,
286e637ccf5SClemens Zeidler 			&alternate_device_id, 1);
287e637ccf5SClemens Zeidler 		if (status == 0) {
288e637ccf5SClemens Zeidler 			TRACE("ps2: probe_mouse alternate device id: %2x\n",
289e637ccf5SClemens Zeidler 				alternate_device_id);
290e637ccf5SClemens Zeidler 			deviceId = alternate_device_id;
291e637ccf5SClemens Zeidler 		}
292e637ccf5SClemens Zeidler 	}
293e637ccf5SClemens Zeidler 
294e637ccf5SClemens Zeidler 	if (deviceId == PS2_DEV_ID_STANDARD
295e637ccf5SClemens Zeidler 		|| deviceId == PS2_DEV_ID_TOUCHPAD_RICATECH) {
296e637ccf5SClemens Zeidler 		INFO("ps2: probe_mouse Standard PS/2 mouse found\n");
297e637ccf5SClemens Zeidler 		dev->name = kStandardMousePath[dev->idx];
298e637ccf5SClemens Zeidler 		dev->packet_size = PS2_PACKET_STANDARD;
299e637ccf5SClemens Zeidler 	} else if (deviceId == PS2_DEV_ID_INTELLIMOUSE) {
300e637ccf5SClemens Zeidler 		dev->name = kIntelliMousePath[dev->idx];
301e637ccf5SClemens Zeidler 		dev->packet_size = PS2_PACKET_INTELLIMOUSE;
302e637ccf5SClemens Zeidler 		INFO("ps2: probe_mouse Extended PS/2 mouse found\n");
303e637ccf5SClemens Zeidler 	} else {
304e637ccf5SClemens Zeidler 		INFO("ps2: probe_mouse Error unknown device id.\n");
305e637ccf5SClemens Zeidler 		return B_ERROR;
306e637ccf5SClemens Zeidler 	}
307e637ccf5SClemens Zeidler 
308e637ccf5SClemens Zeidler 	return B_OK;
309e637ccf5SClemens Zeidler }
310e637ccf5SClemens Zeidler 
311e637ccf5SClemens Zeidler 
312e637ccf5SClemens Zeidler //	#pragma mark - Device functions
313e637ccf5SClemens Zeidler 
314e637ccf5SClemens Zeidler 
315e637ccf5SClemens Zeidler status_t
standard_mouse_open(const char * name,uint32 flags,void ** _cookie)316e637ccf5SClemens Zeidler standard_mouse_open(const char* name, uint32 flags, void** _cookie)
317e637ccf5SClemens Zeidler {
318e637ccf5SClemens Zeidler 	standard_mouse_cookie* cookie;
319e637ccf5SClemens Zeidler 	status_t status;
320e637ccf5SClemens Zeidler 	ps2_dev* dev = NULL;
321e637ccf5SClemens Zeidler 	int i;
322e637ccf5SClemens Zeidler 
323e637ccf5SClemens Zeidler 	TRACE("ps2: standard_mouse_open %s\n", name);
324e637ccf5SClemens Zeidler 
325e637ccf5SClemens Zeidler 	for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
326e637ccf5SClemens Zeidler 		if (0 == strcmp(ps2_device[i].name, name)) {
327e637ccf5SClemens Zeidler 			dev = &ps2_device[i];
328e637ccf5SClemens Zeidler 			break;
329e637ccf5SClemens Zeidler 		}
330819b4547SJohn Scipione #if 0
331819b4547SJohn Scipione 		if (0 == strcmp(g_passthrough_dev.name, name)) {
332e637ccf5SClemens Zeidler 			dev = &g_passthrough_dev;
333e637ccf5SClemens Zeidler 			isSynapticsPTDevice = true;
334e637ccf5SClemens Zeidler 			break;
335819b4547SJohn Scipione 		}
336819b4547SJohn Scipione #endif
337e637ccf5SClemens Zeidler 	}
338e637ccf5SClemens Zeidler 
339e637ccf5SClemens Zeidler 	if (dev == NULL) {
340e637ccf5SClemens Zeidler 		TRACE("ps2: dev = NULL\n");
341e637ccf5SClemens Zeidler 		return B_ERROR;
342e637ccf5SClemens Zeidler 	}
343e637ccf5SClemens Zeidler 
344e637ccf5SClemens Zeidler 	if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
345e637ccf5SClemens Zeidler 		return B_BUSY;
346e637ccf5SClemens Zeidler 
347e637ccf5SClemens Zeidler 	cookie = (standard_mouse_cookie*)malloc(sizeof(standard_mouse_cookie));
348e637ccf5SClemens Zeidler 	if (cookie == NULL)
349e637ccf5SClemens Zeidler 		goto err1;
350e637ccf5SClemens Zeidler 
351e637ccf5SClemens Zeidler 	*_cookie = cookie;
352e637ccf5SClemens Zeidler 	memset(cookie, 0, sizeof(*cookie));
353e637ccf5SClemens Zeidler 
354e637ccf5SClemens Zeidler 	cookie->dev = dev;
355e637ccf5SClemens Zeidler 	dev->cookie = cookie;
356e637ccf5SClemens Zeidler 	dev->disconnect = &standard_mouse_disconnect;
357e637ccf5SClemens Zeidler 	dev->handle_int = &standard_mouse_handle_int;
358e637ccf5SClemens Zeidler 
359e637ccf5SClemens Zeidler 	if (strstr(dev->name, "standard") != NULL)
360e637ccf5SClemens Zeidler 		cookie->flags = F_MOUSE_TYPE_STANDARD;
361e637ccf5SClemens Zeidler 
362e637ccf5SClemens Zeidler 	if (strstr(dev->name, "intelli") != NULL)
363e637ccf5SClemens Zeidler 		cookie->flags = F_MOUSE_TYPE_INTELLIMOUSE;
364e637ccf5SClemens Zeidler 
365e637ccf5SClemens Zeidler 	cookie->standard_mouse_buffer
366e637ccf5SClemens Zeidler 		= create_packet_buffer(MOUSE_HISTORY_SIZE * dev->packet_size);
367e637ccf5SClemens Zeidler 	if (cookie->standard_mouse_buffer == NULL) {
368e637ccf5SClemens Zeidler 		TRACE("ps2: can't allocate mouse actions buffer\n");
369e637ccf5SClemens Zeidler 		goto err2;
370e637ccf5SClemens Zeidler 	}
371e637ccf5SClemens Zeidler 
372e637ccf5SClemens Zeidler 	// create the mouse semaphore, used for synchronization between
373e637ccf5SClemens Zeidler 	// the interrupt handler and the read operation
374e637ccf5SClemens Zeidler 	cookie->standard_mouse_sem = create_sem(0, "ps2_standard_mouse_sem");
375e637ccf5SClemens Zeidler 	if (cookie->standard_mouse_sem < 0) {
376e637ccf5SClemens Zeidler 		TRACE("ps2: failed creating PS/2 mouse semaphore!\n");
377e637ccf5SClemens Zeidler 		goto err3;
378e637ccf5SClemens Zeidler 	}
379e637ccf5SClemens Zeidler 
380e637ccf5SClemens Zeidler 	status = ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
381e637ccf5SClemens Zeidler 	if (status < B_OK) {
382e637ccf5SClemens Zeidler 		INFO("ps2: cannot enable mouse %s\n", name);
383e637ccf5SClemens Zeidler 		goto err4;
384e637ccf5SClemens Zeidler 	}
385e637ccf5SClemens Zeidler 
386e637ccf5SClemens Zeidler 	atomic_or(&dev->flags, PS2_FLAG_ENABLED);
387e637ccf5SClemens Zeidler 
388e637ccf5SClemens Zeidler 
389e637ccf5SClemens Zeidler 	TRACE("ps2: standard_mouse_open %s success\n", name);
390e637ccf5SClemens Zeidler 	return B_OK;
391e637ccf5SClemens Zeidler 
392e637ccf5SClemens Zeidler err4:
393e637ccf5SClemens Zeidler 	delete_sem(cookie->standard_mouse_sem);
394e637ccf5SClemens Zeidler err3:
395e637ccf5SClemens Zeidler 	delete_packet_buffer(cookie->standard_mouse_buffer);
396e637ccf5SClemens Zeidler err2:
397e637ccf5SClemens Zeidler 	free(cookie);
398e637ccf5SClemens Zeidler err1:
399e637ccf5SClemens Zeidler 	atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
400e637ccf5SClemens Zeidler 
401e637ccf5SClemens Zeidler 	TRACE("ps2: standard_mouse_open %s failed\n", name);
402e637ccf5SClemens Zeidler 	return B_ERROR;
403e637ccf5SClemens Zeidler }
404e637ccf5SClemens Zeidler 
405e637ccf5SClemens Zeidler 
406e637ccf5SClemens Zeidler status_t
standard_mouse_close(void * _cookie)407e637ccf5SClemens Zeidler standard_mouse_close(void* _cookie)
408e637ccf5SClemens Zeidler {
409e637ccf5SClemens Zeidler 	standard_mouse_cookie* cookie = (standard_mouse_cookie*)_cookie;
410e637ccf5SClemens Zeidler 
411e637ccf5SClemens Zeidler 	TRACE("ps2: standard_mouse_close %s enter\n", cookie->dev->name);
412e637ccf5SClemens Zeidler 
413e637ccf5SClemens Zeidler 	ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
414e637ccf5SClemens Zeidler 		150000);
415e637ccf5SClemens Zeidler 
416e637ccf5SClemens Zeidler 	delete_packet_buffer(cookie->standard_mouse_buffer);
417e637ccf5SClemens Zeidler 	delete_sem(cookie->standard_mouse_sem);
418e637ccf5SClemens Zeidler 
419e637ccf5SClemens Zeidler 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
420e637ccf5SClemens Zeidler 	atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
421e637ccf5SClemens Zeidler 
422e637ccf5SClemens Zeidler 	TRACE("ps2: standard_mouse_close %s done\n", cookie->dev->name);
423e637ccf5SClemens Zeidler 	return B_OK;
424e637ccf5SClemens Zeidler }
425e637ccf5SClemens Zeidler 
426e637ccf5SClemens Zeidler 
427e637ccf5SClemens Zeidler status_t
standard_mouse_freecookie(void * _cookie)428e637ccf5SClemens Zeidler standard_mouse_freecookie(void* _cookie)
429e637ccf5SClemens Zeidler {
430e637ccf5SClemens Zeidler 	standard_mouse_cookie* cookie = (standard_mouse_cookie*)_cookie;
431e637ccf5SClemens Zeidler 	free(cookie);
432e637ccf5SClemens Zeidler 	return B_OK;
433e637ccf5SClemens Zeidler }
434e637ccf5SClemens Zeidler 
435e637ccf5SClemens Zeidler 
436e637ccf5SClemens Zeidler static status_t
standard_mouse_read(void * cookie,off_t pos,void * buffer,size_t * _length)437e637ccf5SClemens Zeidler standard_mouse_read(void* cookie, off_t pos, void* buffer, size_t* _length)
438e637ccf5SClemens Zeidler {
439e637ccf5SClemens Zeidler 	*_length = 0;
440e637ccf5SClemens Zeidler 	return B_NOT_ALLOWED;
441e637ccf5SClemens Zeidler }
442e637ccf5SClemens Zeidler 
443e637ccf5SClemens Zeidler 
444e637ccf5SClemens Zeidler static status_t
standard_mouse_write(void * cookie,off_t pos,const void * buffer,size_t * _length)445e637ccf5SClemens Zeidler standard_mouse_write(void* cookie, off_t pos, const void* buffer,
446e637ccf5SClemens Zeidler 	size_t* _length)
447e637ccf5SClemens Zeidler {
448e637ccf5SClemens Zeidler 	*_length = 0;
449e637ccf5SClemens Zeidler 	return B_NOT_ALLOWED;
450e637ccf5SClemens Zeidler }
451e637ccf5SClemens Zeidler 
452e637ccf5SClemens Zeidler 
453e637ccf5SClemens Zeidler status_t
standard_mouse_ioctl(void * _cookie,uint32 op,void * buffer,size_t length)454e637ccf5SClemens Zeidler standard_mouse_ioctl(void* _cookie, uint32 op, void* buffer, size_t length)
455e637ccf5SClemens Zeidler {
456e637ccf5SClemens Zeidler 	standard_mouse_cookie* cookie = (standard_mouse_cookie*)_cookie;
457e637ccf5SClemens Zeidler 
458e637ccf5SClemens Zeidler 	switch (op) {
459e637ccf5SClemens Zeidler 		case MS_NUM_EVENTS:
460e637ccf5SClemens Zeidler 		{
461e637ccf5SClemens Zeidler 			int32 count;
462e637ccf5SClemens Zeidler 			TRACE("ps2: ioctl MS_NUM_EVENTS\n");
463e637ccf5SClemens Zeidler 			get_sem_count(cookie->standard_mouse_sem, &count);
464e637ccf5SClemens Zeidler 			return count;
465e637ccf5SClemens Zeidler 		}
466e637ccf5SClemens Zeidler 
467e637ccf5SClemens Zeidler 		case MS_READ:
468e637ccf5SClemens Zeidler 		{
469e637ccf5SClemens Zeidler 			mouse_movement movement;
470e637ccf5SClemens Zeidler 			status_t status;
471e637ccf5SClemens Zeidler 			TRACE("ps2: ioctl MS_READ\n");
472e637ccf5SClemens Zeidler 			if ((status = standard_mouse_read_event(cookie, &movement)) < B_OK)
473e637ccf5SClemens Zeidler 				return status;
474e637ccf5SClemens Zeidler //			TRACE("%s %d %d %d %d\n", cookie->dev->name,
475e637ccf5SClemens Zeidler //				movement.xdelta, movement.ydelta, movement.buttons,
476e637ccf5SClemens Zeidler //				movement.clicks);
477e637ccf5SClemens Zeidler 			return user_memcpy(buffer, &movement, sizeof(movement));
478e637ccf5SClemens Zeidler 		}
479e637ccf5SClemens Zeidler 
480e637ccf5SClemens Zeidler 		case MS_SET_TYPE:
481e637ccf5SClemens Zeidler 			TRACE("ps2: ioctl MS_SET_TYPE not implemented\n");
482e637ccf5SClemens Zeidler 			return B_BAD_VALUE;
483e637ccf5SClemens Zeidler 
484e637ccf5SClemens Zeidler 		case MS_SET_MAP:
485e637ccf5SClemens Zeidler 			TRACE("ps2: ioctl MS_SET_MAP (set mouse mapping) not "
486e637ccf5SClemens Zeidler 				"implemented\n");
487e637ccf5SClemens Zeidler 			return B_BAD_VALUE;
488e637ccf5SClemens Zeidler 
489e637ccf5SClemens Zeidler 		case MS_GET_ACCEL:
490e637ccf5SClemens Zeidler 			TRACE("ps2: ioctl MS_GET_ACCEL (get mouse acceleration) not "
491e637ccf5SClemens Zeidler 				"implemented\n");
492e637ccf5SClemens Zeidler 			return B_BAD_VALUE;
493e637ccf5SClemens Zeidler 
494e637ccf5SClemens Zeidler 		case MS_SET_ACCEL:
495e637ccf5SClemens Zeidler 			TRACE("ps2: ioctl MS_SET_ACCEL (set mouse acceleration) not "
496e637ccf5SClemens Zeidler 				"implemented\n");
497e637ccf5SClemens Zeidler 			return B_BAD_VALUE;
498e637ccf5SClemens Zeidler 
499e637ccf5SClemens Zeidler 		case MS_SET_CLICKSPEED:
500e637ccf5SClemens Zeidler 			TRACE("ps2: ioctl MS_SETCLICK (set click speed)\n");
501e637ccf5SClemens Zeidler 			return user_memcpy(&cookie->click_speed, buffer, sizeof(bigtime_t));
502e637ccf5SClemens Zeidler 
503e637ccf5SClemens Zeidler 		default:
504ebdb22deSZoltán Mizsei 			TRACE("ps2: ioctl unknown mouse opcode: %" B_PRIu32 "\n", op);
505e637ccf5SClemens Zeidler 			return B_DEV_INVALID_IOCTL;
506e637ccf5SClemens Zeidler 	}
507e637ccf5SClemens Zeidler }
508e637ccf5SClemens Zeidler 
509e637ccf5SClemens Zeidler 
510e637ccf5SClemens Zeidler device_hooks gStandardMouseDeviceHooks = {
511e637ccf5SClemens Zeidler 	standard_mouse_open,
512e637ccf5SClemens Zeidler 	standard_mouse_close,
513e637ccf5SClemens Zeidler 	standard_mouse_freecookie,
514e637ccf5SClemens Zeidler 	standard_mouse_ioctl,
515e637ccf5SClemens Zeidler 	standard_mouse_read,
516e637ccf5SClemens Zeidler 	standard_mouse_write,
517e637ccf5SClemens Zeidler };
518