xref: /haiku/src/add-ons/kernel/bus_managers/ps2/ps2_common.cpp (revision 805208b6676fea867149ebe4a8fdd7a2158c6181)
1e637ccf5SClemens Zeidler /*
2e637ccf5SClemens Zeidler  * Copyright 2004-2009 Haiku, Inc.
3e637ccf5SClemens Zeidler  * Distributed under the terms of the MIT License.
4e637ccf5SClemens Zeidler  *
5e637ccf5SClemens Zeidler  * Authors (in chronological order):
6e637ccf5SClemens Zeidler  *		Stefano Ceccherini (burton666@libero.it)
7e637ccf5SClemens Zeidler  *		Axel Dörfler, axeld@pinc-software.de
8e637ccf5SClemens Zeidler  *      Marcus Overhagen <marcus@overhagen.de>
9e637ccf5SClemens Zeidler  */
10e637ccf5SClemens Zeidler 
11e637ccf5SClemens Zeidler /*! PS/2 bus manager */
12e637ccf5SClemens Zeidler 
13e637ccf5SClemens Zeidler 
14e637ccf5SClemens Zeidler #include <string.h>
15e637ccf5SClemens Zeidler 
16e637ccf5SClemens Zeidler #include "ps2_common.h"
17e637ccf5SClemens Zeidler #include "ps2_service.h"
18e637ccf5SClemens Zeidler #include "ps2_dev.h"
19e637ccf5SClemens Zeidler 
20e637ccf5SClemens Zeidler 
21fdbbdcccSAdrien Destugues //#define TRACE_PS2_COMMON
22fdbbdcccSAdrien Destugues #ifdef TRACE_PS2_COMMON
23fdbbdcccSAdrien Destugues #	define TRACE(x...) dprintf(x)
2470d3b102SPulkoMandy #	define TRACE_ONLY
25fdbbdcccSAdrien Destugues #else
26fdbbdcccSAdrien Destugues #	define TRACE(x...)
2770d3b102SPulkoMandy #	define TRACE_ONLY __attribute__((unused))
28fdbbdcccSAdrien Destugues #endif
29fdbbdcccSAdrien Destugues 
30fdbbdcccSAdrien Destugues 
31e637ccf5SClemens Zeidler isa_module_info *gIsa = NULL;
32e637ccf5SClemens Zeidler bool gActiveMultiplexingEnabled = false;
33fb1d7157SPulkoMandy bool gSetupComplete = false;
34*805208b6SAugustin Cavalier mutex gControllerLock;
35e637ccf5SClemens Zeidler 
36e637ccf5SClemens Zeidler static int32 sIgnoreInterrupts = 0;
37e637ccf5SClemens Zeidler 
38e637ccf5SClemens Zeidler 
39e637ccf5SClemens Zeidler uint8
ps2_read_ctrl(void)40e637ccf5SClemens Zeidler ps2_read_ctrl(void)
41e637ccf5SClemens Zeidler {
42e637ccf5SClemens Zeidler 	return gIsa->read_io_8(PS2_PORT_CTRL);
43e637ccf5SClemens Zeidler }
44e637ccf5SClemens Zeidler 
45e637ccf5SClemens Zeidler 
46e637ccf5SClemens Zeidler uint8
ps2_read_data(void)47e637ccf5SClemens Zeidler ps2_read_data(void)
48e637ccf5SClemens Zeidler {
49e637ccf5SClemens Zeidler 	return gIsa->read_io_8(PS2_PORT_DATA);
50e637ccf5SClemens Zeidler }
51e637ccf5SClemens Zeidler 
52e637ccf5SClemens Zeidler 
53e637ccf5SClemens Zeidler void
ps2_write_ctrl(uint8 ctrl)54e637ccf5SClemens Zeidler ps2_write_ctrl(uint8 ctrl)
55e637ccf5SClemens Zeidler {
56e637ccf5SClemens Zeidler 	TRACE("ps2: ps2_write_ctrl 0x%02x\n", ctrl);
57e637ccf5SClemens Zeidler 
58e637ccf5SClemens Zeidler 	gIsa->write_io_8(PS2_PORT_CTRL, ctrl);
59e637ccf5SClemens Zeidler }
60e637ccf5SClemens Zeidler 
61e637ccf5SClemens Zeidler 
62e637ccf5SClemens Zeidler void
ps2_write_data(uint8 data)63e637ccf5SClemens Zeidler ps2_write_data(uint8 data)
64e637ccf5SClemens Zeidler {
65e637ccf5SClemens Zeidler 	TRACE("ps2: ps2_write_data 0x%02x\n", data);
66e637ccf5SClemens Zeidler 
67e637ccf5SClemens Zeidler 	gIsa->write_io_8(PS2_PORT_DATA, data);
68e637ccf5SClemens Zeidler }
69e637ccf5SClemens Zeidler 
70e637ccf5SClemens Zeidler 
71e637ccf5SClemens Zeidler status_t
ps2_wait_read(void)72e637ccf5SClemens Zeidler ps2_wait_read(void)
73e637ccf5SClemens Zeidler {
74e637ccf5SClemens Zeidler 	int i;
75e637ccf5SClemens Zeidler 	for (i = 0; i < PS2_CTRL_WAIT_TIMEOUT / 50; i++) {
76e637ccf5SClemens Zeidler 		if (ps2_read_ctrl() & PS2_STATUS_OUTPUT_BUFFER_FULL)
77e637ccf5SClemens Zeidler 			return B_OK;
78e637ccf5SClemens Zeidler 		snooze(50);
79e637ccf5SClemens Zeidler 	}
80e637ccf5SClemens Zeidler 	return B_ERROR;
81e637ccf5SClemens Zeidler }
82e637ccf5SClemens Zeidler 
83e637ccf5SClemens Zeidler 
84e637ccf5SClemens Zeidler status_t
ps2_wait_write(void)85e637ccf5SClemens Zeidler ps2_wait_write(void)
86e637ccf5SClemens Zeidler {
87e637ccf5SClemens Zeidler 	int i;
88e637ccf5SClemens Zeidler 	for (i = 0; i < PS2_CTRL_WAIT_TIMEOUT / 50; i++) {
89e637ccf5SClemens Zeidler 		if (!(ps2_read_ctrl() & PS2_STATUS_INPUT_BUFFER_FULL))
90e637ccf5SClemens Zeidler 			return B_OK;
91e637ccf5SClemens Zeidler 		snooze(50);
92e637ccf5SClemens Zeidler 	}
93e637ccf5SClemens Zeidler 	return B_ERROR;
94e637ccf5SClemens Zeidler }
95e637ccf5SClemens Zeidler 
96e637ccf5SClemens Zeidler 
97e637ccf5SClemens Zeidler //	#pragma mark -
98e637ccf5SClemens Zeidler 
99e637ccf5SClemens Zeidler 
100e637ccf5SClemens Zeidler void
ps2_flush(void)101e637ccf5SClemens Zeidler ps2_flush(void)
102e637ccf5SClemens Zeidler {
103e637ccf5SClemens Zeidler 	int i;
104e637ccf5SClemens Zeidler 
105*805208b6SAugustin Cavalier 	mutex_lock(&gControllerLock);
106e637ccf5SClemens Zeidler 	atomic_add(&sIgnoreInterrupts, 1);
107e637ccf5SClemens Zeidler 
108e637ccf5SClemens Zeidler 	for (i = 0; i < 64; i++) {
109e637ccf5SClemens Zeidler 		uint8 ctrl;
11070d3b102SPulkoMandy 		uint8 data TRACE_ONLY;
111e637ccf5SClemens Zeidler 		ctrl = ps2_read_ctrl();
112e637ccf5SClemens Zeidler 		if (!(ctrl & PS2_STATUS_OUTPUT_BUFFER_FULL))
113e637ccf5SClemens Zeidler 			break;
114e637ccf5SClemens Zeidler 		data = ps2_read_data();
115e637ccf5SClemens Zeidler 		TRACE("ps2: ps2_flush: ctrl 0x%02x, data 0x%02x (%s)\n", ctrl, data, (ctrl & PS2_STATUS_AUX_DATA) ? "aux" : "keyb");
116e637ccf5SClemens Zeidler 		snooze(100);
117e637ccf5SClemens Zeidler 	}
118e637ccf5SClemens Zeidler 
119e637ccf5SClemens Zeidler 	atomic_add(&sIgnoreInterrupts, -1);
120*805208b6SAugustin Cavalier 	mutex_unlock(&gControllerLock);
121e637ccf5SClemens Zeidler }
122e637ccf5SClemens Zeidler 
123e637ccf5SClemens Zeidler 
124e637ccf5SClemens Zeidler static status_t
ps2_selftest()125e637ccf5SClemens Zeidler ps2_selftest()
126e637ccf5SClemens Zeidler {
127e637ccf5SClemens Zeidler 	status_t res;
128e637ccf5SClemens Zeidler 	uint8 in;
129e637ccf5SClemens Zeidler 	res = ps2_command(PS2_CTRL_SELF_TEST, NULL, 0, &in, 1);
130e637ccf5SClemens Zeidler 	if (res != B_OK || in != 0x55) {
131ef67788fSAlex Smith 		INFO("ps2: controller self test failed, status 0x%08" B_PRIx32 ", data "
132ef67788fSAlex Smith 			"0x%02x\n", res, in);
133e637ccf5SClemens Zeidler 		return B_ERROR;
134e637ccf5SClemens Zeidler 	}
135e637ccf5SClemens Zeidler 	return B_OK;
136e637ccf5SClemens Zeidler }
137e637ccf5SClemens Zeidler 
138e637ccf5SClemens Zeidler 
139e637ccf5SClemens Zeidler static status_t
ps2_setup_command_byte(bool interruptsEnabled)1406b61d62eSSiarzhuk Zharski ps2_setup_command_byte(bool interruptsEnabled)
141e637ccf5SClemens Zeidler {
142e637ccf5SClemens Zeidler 	status_t res;
143e637ccf5SClemens Zeidler 	uint8 cmdbyte;
144e637ccf5SClemens Zeidler 
145e637ccf5SClemens Zeidler 	res = ps2_command(PS2_CTRL_READ_CMD, NULL, 0, &cmdbyte, 1);
146ef67788fSAlex Smith 	TRACE("ps2: get command byte: res 0x%08" B_PRIx32 ", cmdbyte 0x%02x\n",
147ef67788fSAlex Smith 		res, cmdbyte);
148e637ccf5SClemens Zeidler 	if (res != B_OK)
149e637ccf5SClemens Zeidler 		cmdbyte = 0x47;
150e637ccf5SClemens Zeidler 
1516b61d62eSSiarzhuk Zharski 	cmdbyte |= PS2_BITS_TRANSLATE_SCANCODES;
152e637ccf5SClemens Zeidler 	cmdbyte &= ~(PS2_BITS_KEYBOARD_DISABLED | PS2_BITS_MOUSE_DISABLED);
153e637ccf5SClemens Zeidler 
1546b61d62eSSiarzhuk Zharski 	if (interruptsEnabled)
1556b61d62eSSiarzhuk Zharski 		cmdbyte |= PS2_BITS_KEYBOARD_INTERRUPT | PS2_BITS_AUX_INTERRUPT;
1566b61d62eSSiarzhuk Zharski 	else
1576b61d62eSSiarzhuk Zharski 		cmdbyte &= ~(PS2_BITS_KEYBOARD_INTERRUPT | PS2_BITS_AUX_INTERRUPT);
1586b61d62eSSiarzhuk Zharski 
159e637ccf5SClemens Zeidler 	res = ps2_command(PS2_CTRL_WRITE_CMD, &cmdbyte, 1, NULL, 0);
160ef67788fSAlex Smith 	TRACE("ps2: set command byte: res 0x%08" B_PRIx32 ", cmdbyte 0x%02x\n",
161ef67788fSAlex Smith 		res, cmdbyte);
162e637ccf5SClemens Zeidler 
163e637ccf5SClemens Zeidler 	return res;
164e637ccf5SClemens Zeidler }
165e637ccf5SClemens Zeidler 
166e637ccf5SClemens Zeidler 
167e637ccf5SClemens Zeidler static status_t
ps2_setup_active_multiplexing(bool * enabled)168e637ccf5SClemens Zeidler ps2_setup_active_multiplexing(bool *enabled)
169e637ccf5SClemens Zeidler {
170e637ccf5SClemens Zeidler 	status_t res;
171e637ccf5SClemens Zeidler 	uint8 in, out;
172e637ccf5SClemens Zeidler 
173e866d6e9SPulkoMandy 	// Disable the keyboard port to avoid any interference with the keyboard
174e866d6e9SPulkoMandy 	ps2_command(PS2_CTRL_KEYBOARD_DISABLE, NULL, 0, NULL, 0);
175e866d6e9SPulkoMandy 
176e637ccf5SClemens Zeidler 	out = 0xf0;
177e866d6e9SPulkoMandy 	res = ps2_command(PS2_CTRL_AUX_LOOPBACK, &out, 1, &in, 1);
178e637ccf5SClemens Zeidler 	if (res)
179e637ccf5SClemens Zeidler 		goto fail;
180e637ccf5SClemens Zeidler 	// Step 1, if controller is good, in does match out.
181e637ccf5SClemens Zeidler 	// This test failes with MS Virtual PC.
182e637ccf5SClemens Zeidler 	if (in != out)
183e637ccf5SClemens Zeidler 		goto no_support;
184e637ccf5SClemens Zeidler 
185e637ccf5SClemens Zeidler 	out = 0x56;
186e866d6e9SPulkoMandy 	res = ps2_command(PS2_CTRL_AUX_LOOPBACK, &out, 1, &in, 1);
187e637ccf5SClemens Zeidler 	if (res)
188e637ccf5SClemens Zeidler 		goto fail;
189e637ccf5SClemens Zeidler 	// Step 2, if controller is good, in does match out.
190e637ccf5SClemens Zeidler 	if (in != out)
191e637ccf5SClemens Zeidler 		goto no_support;
192e637ccf5SClemens Zeidler 
193e637ccf5SClemens Zeidler 	out = 0xa4;
194e866d6e9SPulkoMandy 	res = ps2_command(PS2_CTRL_AUX_LOOPBACK, &out, 1, &in, 1);
195e637ccf5SClemens Zeidler 	if (res)
196e637ccf5SClemens Zeidler 		goto fail;
197e637ccf5SClemens Zeidler 	// Step 3, if the controller doesn't support active multiplexing,
198e637ccf5SClemens Zeidler 	// then in data does match out data (0xa4), else it's version number.
199e637ccf5SClemens Zeidler 	if (in == out)
200e637ccf5SClemens Zeidler 		goto no_support;
201e637ccf5SClemens Zeidler 
202e637ccf5SClemens Zeidler 	// With some broken USB legacy emulation, it's 0xac, and with
203e637ccf5SClemens Zeidler 	// MS Virtual PC, it's 0xa6. Since current active multiplexing
204e637ccf5SClemens Zeidler 	// specification version is 1.1 (0x11), we validate the data.
205e637ccf5SClemens Zeidler 	if (in > 0x9f) {
206e637ccf5SClemens Zeidler 		TRACE("ps2: active multiplexing v%d.%d detected, but ignored!\n", (in >> 4), in & 0xf);
207e637ccf5SClemens Zeidler 		goto no_support;
208e637ccf5SClemens Zeidler 	}
209e637ccf5SClemens Zeidler 
210e637ccf5SClemens Zeidler 	INFO("ps2: active multiplexing v%d.%d enabled\n", (in >> 4), in & 0xf);
211e637ccf5SClemens Zeidler 	*enabled = true;
212e637ccf5SClemens Zeidler 	goto done;
213e637ccf5SClemens Zeidler 
214e637ccf5SClemens Zeidler no_support:
215e637ccf5SClemens Zeidler 	TRACE("ps2: active multiplexing not supported\n");
216e637ccf5SClemens Zeidler 	*enabled = false;
217e637ccf5SClemens Zeidler 
218e637ccf5SClemens Zeidler done:
219e637ccf5SClemens Zeidler 	// Some controllers get upset by the d3 command and will continue data
220e637ccf5SClemens Zeidler 	// loopback, thus we need to send a harmless command (enable keyboard
221e637ccf5SClemens Zeidler 	// interface) next.
222e637ccf5SClemens Zeidler 	// This fixes bug report #1175
223e866d6e9SPulkoMandy 	res = ps2_command(PS2_CTRL_KEYBOARD_ENABLE, NULL, 0, NULL, 0);
224e637ccf5SClemens Zeidler 	if (res != B_OK) {
225ef67788fSAlex Smith 		INFO("ps2: active multiplexing d3 workaround failed, status 0x%08"
226ef67788fSAlex Smith 			B_PRIx32 "\n", res);
227e637ccf5SClemens Zeidler 	}
228e637ccf5SClemens Zeidler 	return B_OK;
229e637ccf5SClemens Zeidler 
230e637ccf5SClemens Zeidler fail:
231e637ccf5SClemens Zeidler 	TRACE("ps2: testing for active multiplexing failed\n");
232e637ccf5SClemens Zeidler 	*enabled = false;
233e637ccf5SClemens Zeidler 	// this should revert the controller into legacy mode,
234e637ccf5SClemens Zeidler 	// just in case it has switched to multiplexed mode
235e637ccf5SClemens Zeidler 	return ps2_selftest();
236e637ccf5SClemens Zeidler }
237e637ccf5SClemens Zeidler 
238e637ccf5SClemens Zeidler 
239e637ccf5SClemens Zeidler status_t
ps2_command(uint8 cmd,const uint8 * out,int outCount,uint8 * in,int inCount)240e637ccf5SClemens Zeidler ps2_command(uint8 cmd, const uint8 *out, int outCount, uint8 *in, int inCount)
241e637ccf5SClemens Zeidler {
242e637ccf5SClemens Zeidler 	status_t res;
243e637ccf5SClemens Zeidler 	int i;
244e637ccf5SClemens Zeidler 
245*805208b6SAugustin Cavalier 	mutex_lock(&gControllerLock);
246e637ccf5SClemens Zeidler 	atomic_add(&sIgnoreInterrupts, 1);
247e637ccf5SClemens Zeidler 
248e866d6e9SPulkoMandy #ifdef TRACE_PS2_COMMON
249e637ccf5SClemens Zeidler 	TRACE("ps2: ps2_command cmd 0x%02x, out %d, in %d\n", cmd, outCount, inCount);
250e637ccf5SClemens Zeidler 	for (i = 0; i < outCount; i++)
251e637ccf5SClemens Zeidler 		TRACE("ps2: ps2_command out 0x%02x\n", out[i]);
252e637ccf5SClemens Zeidler #endif
253e637ccf5SClemens Zeidler 
254e637ccf5SClemens Zeidler 	res = ps2_wait_write();
255e637ccf5SClemens Zeidler 	if (res == B_OK)
256e637ccf5SClemens Zeidler 		ps2_write_ctrl(cmd);
257e637ccf5SClemens Zeidler 
258e637ccf5SClemens Zeidler 	for (i = 0; res == B_OK && i < outCount; i++) {
259e637ccf5SClemens Zeidler 		res = ps2_wait_write();
260e637ccf5SClemens Zeidler 		if (res == B_OK)
261e637ccf5SClemens Zeidler 			ps2_write_data(out[i]);
262e637ccf5SClemens Zeidler 		else
263e637ccf5SClemens Zeidler 			TRACE("ps2: ps2_command out byte %d failed\n", i);
264e637ccf5SClemens Zeidler 	}
265e637ccf5SClemens Zeidler 
266e637ccf5SClemens Zeidler 	for (i = 0; res == B_OK && i < inCount; i++) {
267e637ccf5SClemens Zeidler 		res = ps2_wait_read();
268e637ccf5SClemens Zeidler 		if (res == B_OK)
269e637ccf5SClemens Zeidler 			in[i] = ps2_read_data();
270e637ccf5SClemens Zeidler 		else
271e637ccf5SClemens Zeidler 			TRACE("ps2: ps2_command in byte %d failed\n", i);
272e637ccf5SClemens Zeidler 	}
273e637ccf5SClemens Zeidler 
274e866d6e9SPulkoMandy #ifdef TRACE_PS2_COMMON
275e637ccf5SClemens Zeidler 	for (i = 0; i < inCount; i++)
276e637ccf5SClemens Zeidler 		TRACE("ps2: ps2_command in 0x%02x\n", in[i]);
277ef67788fSAlex Smith 	TRACE("ps2: ps2_command result 0x%08" B_PRIx32 "\n", res);
278e637ccf5SClemens Zeidler #endif
279e637ccf5SClemens Zeidler 
280e637ccf5SClemens Zeidler 	atomic_add(&sIgnoreInterrupts, -1);
281*805208b6SAugustin Cavalier 	mutex_unlock(&gControllerLock);
282e637ccf5SClemens Zeidler 
283e637ccf5SClemens Zeidler 	return res;
284e637ccf5SClemens Zeidler }
285e637ccf5SClemens Zeidler 
286e637ccf5SClemens Zeidler 
287e637ccf5SClemens Zeidler //	#pragma mark -
288e637ccf5SClemens Zeidler 
289e637ccf5SClemens Zeidler 
290e637ccf5SClemens Zeidler static int32
ps2_interrupt(void * cookie)291e637ccf5SClemens Zeidler ps2_interrupt(void* cookie)
292e637ccf5SClemens Zeidler {
293e637ccf5SClemens Zeidler 	uint8 ctrl;
294e637ccf5SClemens Zeidler 	uint8 data;
295e637ccf5SClemens Zeidler 	bool error;
296e637ccf5SClemens Zeidler 	ps2_dev *dev;
297e637ccf5SClemens Zeidler 
298e637ccf5SClemens Zeidler 	ctrl = ps2_read_ctrl();
2992191dfe4SSiarzhuk Zharski 	if (!(ctrl & PS2_STATUS_OUTPUT_BUFFER_FULL)) {
3002191dfe4SSiarzhuk Zharski 		TRACE("ps2: ps2_interrupt unhandled, OBF bit unset, ctrl 0x%02x (%s)\n",
3012191dfe4SSiarzhuk Zharski 				ctrl, (ctrl & PS2_STATUS_AUX_DATA) ? "aux" : "keyb");
302e637ccf5SClemens Zeidler 		return B_UNHANDLED_INTERRUPT;
3032191dfe4SSiarzhuk Zharski 	}
304e637ccf5SClemens Zeidler 
305e637ccf5SClemens Zeidler 	if (atomic_get(&sIgnoreInterrupts)) {
306e637ccf5SClemens Zeidler 		TRACE("ps2: ps2_interrupt ignoring, ctrl 0x%02x (%s)\n", ctrl,
307e637ccf5SClemens Zeidler 			(ctrl & PS2_STATUS_AUX_DATA) ? "aux" : "keyb");
308e637ccf5SClemens Zeidler 		return B_HANDLED_INTERRUPT;
309e637ccf5SClemens Zeidler 	}
310e637ccf5SClemens Zeidler 
311e637ccf5SClemens Zeidler 	data = ps2_read_data();
312e637ccf5SClemens Zeidler 
313e637ccf5SClemens Zeidler 	if ((ctrl & PS2_STATUS_AUX_DATA) != 0) {
314e637ccf5SClemens Zeidler 		uint8 idx;
315e637ccf5SClemens Zeidler 		if (gActiveMultiplexingEnabled) {
316e637ccf5SClemens Zeidler 			idx = ctrl >> 6;
317e637ccf5SClemens Zeidler 			error = (ctrl & 0x04) != 0;
318e637ccf5SClemens Zeidler 			TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (mouse %d)\n",
319e637ccf5SClemens Zeidler 				ctrl, data, idx);
320e637ccf5SClemens Zeidler 		} else {
321e637ccf5SClemens Zeidler 			idx = 0;
322e637ccf5SClemens Zeidler 			error = (ctrl & 0xC0) != 0;
323e637ccf5SClemens Zeidler 			TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (aux)\n", ctrl,
324e637ccf5SClemens Zeidler 				data);
325e637ccf5SClemens Zeidler 		}
326e637ccf5SClemens Zeidler 		dev = &ps2_device[PS2_DEVICE_MOUSE + idx];
327e637ccf5SClemens Zeidler 	} else {
328e637ccf5SClemens Zeidler 		TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (keyb)\n", ctrl,
329e637ccf5SClemens Zeidler 			data);
330e637ccf5SClemens Zeidler 
331e637ccf5SClemens Zeidler 		dev = &ps2_device[PS2_DEVICE_KEYB];
332e637ccf5SClemens Zeidler 		error = (ctrl & 0xC0) != 0;
333e637ccf5SClemens Zeidler 	}
334e637ccf5SClemens Zeidler 
335e637ccf5SClemens Zeidler 	dev->history[1] = dev->history[0];
336e637ccf5SClemens Zeidler 	dev->history[0].time = system_time();
337e637ccf5SClemens Zeidler 	dev->history[0].data = data;
338e637ccf5SClemens Zeidler 	dev->history[0].error = error;
339e637ccf5SClemens Zeidler 
340e637ccf5SClemens Zeidler 	return ps2_dev_handle_int(dev);
341e637ccf5SClemens Zeidler }
342e637ccf5SClemens Zeidler 
343e637ccf5SClemens Zeidler 
344e637ccf5SClemens Zeidler //	#pragma mark - driver interface
345e637ccf5SClemens Zeidler 
346e637ccf5SClemens Zeidler 
347e637ccf5SClemens Zeidler status_t
ps2_init(void)348e637ccf5SClemens Zeidler ps2_init(void)
349e637ccf5SClemens Zeidler {
350e637ccf5SClemens Zeidler 	status_t status;
351e637ccf5SClemens Zeidler 
352e637ccf5SClemens Zeidler 	TRACE("ps2: init\n");
353e637ccf5SClemens Zeidler 
354e637ccf5SClemens Zeidler 	status = get_module(B_ISA_MODULE_NAME, (module_info **)&gIsa);
355e637ccf5SClemens Zeidler 	if (status < B_OK)
356e637ccf5SClemens Zeidler 		return status;
357e637ccf5SClemens Zeidler 
358*805208b6SAugustin Cavalier 	mutex_init(&gControllerLock, "ps/2 keyb ctrl");
359e637ccf5SClemens Zeidler 
360e637ccf5SClemens Zeidler 	ps2_flush();
361e637ccf5SClemens Zeidler 
362e637ccf5SClemens Zeidler 	status = ps2_dev_init();
363e637ccf5SClemens Zeidler 	if (status < B_OK)
364e637ccf5SClemens Zeidler 		goto err1;
365e637ccf5SClemens Zeidler 
366e637ccf5SClemens Zeidler 	status = ps2_service_init();
367e637ccf5SClemens Zeidler 	if (status < B_OK)
368e637ccf5SClemens Zeidler 		goto err2;
369e637ccf5SClemens Zeidler 
370e637ccf5SClemens Zeidler 	status = install_io_interrupt_handler(INT_PS2_KEYBOARD, &ps2_interrupt,
371e637ccf5SClemens Zeidler 		NULL, 0);
372e637ccf5SClemens Zeidler 	if (status)
373e637ccf5SClemens Zeidler 		goto err3;
374e637ccf5SClemens Zeidler 
375e637ccf5SClemens Zeidler 	status = install_io_interrupt_handler(INT_PS2_MOUSE, &ps2_interrupt, NULL,
376e637ccf5SClemens Zeidler 		0);
377e637ccf5SClemens Zeidler 	if (status)
378e637ccf5SClemens Zeidler 		goto err4;
379e637ccf5SClemens Zeidler 
380e637ccf5SClemens Zeidler 	// While this might have fixed bug #1185, we can't do this unconditionally
381e637ccf5SClemens Zeidler 	// as it obviously messes up many controllers which couldn't reboot anymore
382e637ccf5SClemens Zeidler 	// after that
383e637ccf5SClemens Zeidler 	//ps2_selftest();
384e637ccf5SClemens Zeidler 
3856b61d62eSSiarzhuk Zharski 	// Setup the command byte with disabled keyboard and AUX interrupts
3866b61d62eSSiarzhuk Zharski 	// to prevent interrupts storm on some KBCs during active multiplexing
3876b61d62eSSiarzhuk Zharski 	// activation procedure. Fixes #7635.
3886b61d62eSSiarzhuk Zharski 	status = ps2_setup_command_byte(false);
389e637ccf5SClemens Zeidler 	if (status) {
3906b61d62eSSiarzhuk Zharski 		INFO("ps2: initial setup of command byte failed\n");
391e637ccf5SClemens Zeidler 		goto err5;
392e637ccf5SClemens Zeidler 	}
393e637ccf5SClemens Zeidler 
394e637ccf5SClemens Zeidler 	ps2_flush();
395e637ccf5SClemens Zeidler 	status = ps2_setup_active_multiplexing(&gActiveMultiplexingEnabled);
396e637ccf5SClemens Zeidler 	if (status) {
397e637ccf5SClemens Zeidler 		INFO("ps2: setting up active multiplexing failed\n");
398e637ccf5SClemens Zeidler 		goto err5;
399e637ccf5SClemens Zeidler 	}
400e637ccf5SClemens Zeidler 
4016b61d62eSSiarzhuk Zharski 	status = ps2_setup_command_byte(true);
4026b61d62eSSiarzhuk Zharski 	if (status) {
4036b61d62eSSiarzhuk Zharski 		INFO("ps2: setting up command byte with enabled interrupts failed\n");
4046b61d62eSSiarzhuk Zharski 		goto err5;
4056b61d62eSSiarzhuk Zharski 	}
4066b61d62eSSiarzhuk Zharski 
407e637ccf5SClemens Zeidler 	if (gActiveMultiplexingEnabled) {
408e866d6e9SPulkoMandy 		// The multiplexing spec recommends to leave device 0 unconnected because it saves some
409e866d6e9SPulkoMandy 		// confusion with the use of the D3 command which appears as if the replied data was
410e866d6e9SPulkoMandy 		// coming from device 0. So we enable it only if it really looks like there is a device
411e866d6e9SPulkoMandy 		// connected there.
4124c78b73bSJérôme Duval 		if (ps2_dev_command_timeout(&ps2_device[PS2_DEVICE_MOUSE],
413e866d6e9SPulkoMandy 				PS2_CMD_MOUSE_SET_SCALE11, NULL, 0, NULL, 0, 100000) == B_TIMED_OUT) {
414e637ccf5SClemens Zeidler 			INFO("ps2: accessing multiplexed mouse port 0 timed out, ignoring it!\n");
415e637ccf5SClemens Zeidler 		} else {
416e637ccf5SClemens Zeidler 			ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_MOUSE]);
417e637ccf5SClemens Zeidler 		}
418e866d6e9SPulkoMandy 
419e866d6e9SPulkoMandy 		for (int idx = 1; idx <= 3; idx++) {
420e866d6e9SPulkoMandy 			ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_MOUSE + idx]);
421e866d6e9SPulkoMandy 		}
422e637ccf5SClemens Zeidler 		ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_KEYB]);
423e637ccf5SClemens Zeidler 	} else {
424e637ccf5SClemens Zeidler 		ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_MOUSE]);
425e637ccf5SClemens Zeidler 		ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_KEYB]);
426e637ccf5SClemens Zeidler 	}
427e637ccf5SClemens Zeidler 
428fb1d7157SPulkoMandy 	gSetupComplete = true;
429fb1d7157SPulkoMandy 
430e637ccf5SClemens Zeidler 	TRACE("ps2: init done!\n");
431e637ccf5SClemens Zeidler 	return B_OK;
432e637ccf5SClemens Zeidler 
433e637ccf5SClemens Zeidler err5:
434e637ccf5SClemens Zeidler 	remove_io_interrupt_handler(INT_PS2_MOUSE, &ps2_interrupt, NULL);
435e637ccf5SClemens Zeidler err4:
436e637ccf5SClemens Zeidler 	remove_io_interrupt_handler(INT_PS2_KEYBOARD, &ps2_interrupt, NULL);
437e637ccf5SClemens Zeidler err3:
438e637ccf5SClemens Zeidler 	ps2_service_exit();
439e637ccf5SClemens Zeidler err2:
440e637ccf5SClemens Zeidler 	ps2_dev_exit();
441e637ccf5SClemens Zeidler err1:
442*805208b6SAugustin Cavalier 	mutex_destroy(&gControllerLock);
443e637ccf5SClemens Zeidler 	put_module(B_ISA_MODULE_NAME);
444e637ccf5SClemens Zeidler 	TRACE("ps2: init failed!\n");
445e637ccf5SClemens Zeidler 	return B_ERROR;
446e637ccf5SClemens Zeidler }
447e637ccf5SClemens Zeidler 
448e637ccf5SClemens Zeidler 
449e637ccf5SClemens Zeidler void
ps2_uninit(void)450e637ccf5SClemens Zeidler ps2_uninit(void)
451e637ccf5SClemens Zeidler {
452e637ccf5SClemens Zeidler 	TRACE("ps2: uninit\n");
453e637ccf5SClemens Zeidler 	remove_io_interrupt_handler(INT_PS2_MOUSE,    &ps2_interrupt, NULL);
454e637ccf5SClemens Zeidler 	remove_io_interrupt_handler(INT_PS2_KEYBOARD, &ps2_interrupt, NULL);
455e637ccf5SClemens Zeidler 	ps2_service_exit();
456e637ccf5SClemens Zeidler 	ps2_dev_exit();
457*805208b6SAugustin Cavalier 	mutex_destroy(&gControllerLock);
458e637ccf5SClemens Zeidler 	put_module(B_ISA_MODULE_NAME);
459e637ccf5SClemens Zeidler }
460