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