2 * Copyright 2004-2009 Haiku, Inc.
3 * Distributed under the terms of the MIT License.
5 * Authors (in chronological order):
6 * Stefano Ceccherini (burton666@libero.it)
7 * Axel Dörfler, axeld@pinc-software.de
8 * Marcus Overhagen <marcus@overhagen.de>
11 /*! PS/2 bus manager */
16 #include "ps2_common.h"
17 #include "ps2_service.h"
21 isa_module_info
*gIsa
= NULL
;
22 bool gActiveMultiplexingEnabled
= false;
23 sem_id gControllerSem
;
25 static int32 sIgnoreInterrupts
= 0;
31 return gIsa
->read_io_8(PS2_PORT_CTRL
);
38 return gIsa
->read_io_8(PS2_PORT_DATA
);
43 ps2_write_ctrl(uint8 ctrl
)
45 TRACE("ps2: ps2_write_ctrl 0x%02x\n", ctrl
);
47 gIsa
->write_io_8(PS2_PORT_CTRL
, ctrl
);
52 ps2_write_data(uint8 data
)
54 TRACE("ps2: ps2_write_data 0x%02x\n", data
);
56 gIsa
->write_io_8(PS2_PORT_DATA
, data
);
64 for (i
= 0; i
< PS2_CTRL_WAIT_TIMEOUT
/ 50; i
++) {
65 if (ps2_read_ctrl() & PS2_STATUS_OUTPUT_BUFFER_FULL
)
77 for (i
= 0; i
< PS2_CTRL_WAIT_TIMEOUT
/ 50; i
++) {
78 if (!(ps2_read_ctrl() & PS2_STATUS_INPUT_BUFFER_FULL
))
94 acquire_sem(gControllerSem
);
95 atomic_add(&sIgnoreInterrupts
, 1);
97 for (i
= 0; i
< 64; i
++) {
100 ctrl
= ps2_read_ctrl();
101 if (!(ctrl
& PS2_STATUS_OUTPUT_BUFFER_FULL
))
103 data
= ps2_read_data();
104 TRACE("ps2: ps2_flush: ctrl 0x%02x, data 0x%02x (%s)\n", ctrl
, data
, (ctrl
& PS2_STATUS_AUX_DATA
) ? "aux" : "keyb");
108 atomic_add(&sIgnoreInterrupts
, -1);
109 release_sem(gControllerSem
);
118 res
= ps2_command(PS2_CTRL_SELF_TEST
, NULL
, 0, &in
, 1);
119 if (res
!= B_OK
|| in
!= 0x55) {
120 INFO("ps2: controller self test failed, status 0x%08" B_PRIx32
", data "
121 "0x%02x\n", res
, in
);
129 ps2_setup_command_byte(bool interruptsEnabled
)
134 res
= ps2_command(PS2_CTRL_READ_CMD
, NULL
, 0, &cmdbyte
, 1);
135 TRACE("ps2: get command byte: res 0x%08" B_PRIx32
", cmdbyte 0x%02x\n",
140 cmdbyte
|= PS2_BITS_TRANSLATE_SCANCODES
;
141 cmdbyte
&= ~(PS2_BITS_KEYBOARD_DISABLED
| PS2_BITS_MOUSE_DISABLED
);
143 if (interruptsEnabled
)
144 cmdbyte
|= PS2_BITS_KEYBOARD_INTERRUPT
| PS2_BITS_AUX_INTERRUPT
;
146 cmdbyte
&= ~(PS2_BITS_KEYBOARD_INTERRUPT
| PS2_BITS_AUX_INTERRUPT
);
148 res
= ps2_command(PS2_CTRL_WRITE_CMD
, &cmdbyte
, 1, NULL
, 0);
149 TRACE("ps2: set command byte: res 0x%08" B_PRIx32
", cmdbyte 0x%02x\n",
157 ps2_setup_active_multiplexing(bool *enabled
)
163 res
= ps2_command(0xd3, &out
, 1, &in
, 1);
166 // Step 1, if controller is good, in does match out.
167 // This test failes with MS Virtual PC.
172 res
= ps2_command(0xd3, &out
, 1, &in
, 1);
175 // Step 2, if controller is good, in does match out.
180 res
= ps2_command(0xd3, &out
, 1, &in
, 1);
183 // Step 3, if the controller doesn't support active multiplexing,
184 // then in data does match out data (0xa4), else it's version number.
188 // With some broken USB legacy emulation, it's 0xac, and with
189 // MS Virtual PC, it's 0xa6. Since current active multiplexing
190 // specification version is 1.1 (0x11), we validate the data.
192 TRACE("ps2: active multiplexing v%d.%d detected, but ignored!\n", (in
>> 4), in
& 0xf);
196 INFO("ps2: active multiplexing v%d.%d enabled\n", (in
>> 4), in
& 0xf);
201 TRACE("ps2: active multiplexing not supported\n");
205 // Some controllers get upset by the d3 command and will continue data
206 // loopback, thus we need to send a harmless command (enable keyboard
208 // This fixes bug report #1175
209 res
= ps2_command(0xae, NULL
, 0, NULL
, 0);
211 INFO("ps2: active multiplexing d3 workaround failed, status 0x%08"
217 TRACE("ps2: testing for active multiplexing failed\n");
219 // this should revert the controller into legacy mode,
220 // just in case it has switched to multiplexed mode
221 return ps2_selftest();
226 ps2_command(uint8 cmd
, const uint8
*out
, int outCount
, uint8
*in
, int inCount
)
231 acquire_sem(gControllerSem
);
232 atomic_add(&sIgnoreInterrupts
, 1);
235 TRACE("ps2: ps2_command cmd 0x%02x, out %d, in %d\n", cmd
, outCount
, inCount
);
236 for (i
= 0; i
< outCount
; i
++)
237 TRACE("ps2: ps2_command out 0x%02x\n", out
[i
]);
240 res
= ps2_wait_write();
244 for (i
= 0; res
== B_OK
&& i
< outCount
; i
++) {
245 res
= ps2_wait_write();
247 ps2_write_data(out
[i
]);
249 TRACE("ps2: ps2_command out byte %d failed\n", i
);
252 for (i
= 0; res
== B_OK
&& i
< inCount
; i
++) {
253 res
= ps2_wait_read();
255 in
[i
] = ps2_read_data();
257 TRACE("ps2: ps2_command in byte %d failed\n", i
);
261 for (i
= 0; i
< inCount
; i
++)
262 TRACE("ps2: ps2_command in 0x%02x\n", in
[i
]);
263 TRACE("ps2: ps2_command result 0x%08" B_PRIx32
"\n", res
);
266 atomic_add(&sIgnoreInterrupts
, -1);
267 release_sem(gControllerSem
);
277 ps2_interrupt(void* cookie
)
284 ctrl
= ps2_read_ctrl();
285 if (!(ctrl
& PS2_STATUS_OUTPUT_BUFFER_FULL
)) {
286 TRACE("ps2: ps2_interrupt unhandled, OBF bit unset, ctrl 0x%02x (%s)\n",
287 ctrl
, (ctrl
& PS2_STATUS_AUX_DATA
) ? "aux" : "keyb");
288 return B_UNHANDLED_INTERRUPT
;
291 if (atomic_get(&sIgnoreInterrupts
)) {
292 TRACE("ps2: ps2_interrupt ignoring, ctrl 0x%02x (%s)\n", ctrl
,
293 (ctrl
& PS2_STATUS_AUX_DATA
) ? "aux" : "keyb");
294 return B_HANDLED_INTERRUPT
;
297 data
= ps2_read_data();
299 if ((ctrl
& PS2_STATUS_AUX_DATA
) != 0) {
301 if (gActiveMultiplexingEnabled
) {
303 error
= (ctrl
& 0x04) != 0;
304 TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (mouse %d)\n",
308 error
= (ctrl
& 0xC0) != 0;
309 TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (aux)\n", ctrl
,
312 dev
= &ps2_device
[PS2_DEVICE_MOUSE
+ idx
];
314 TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (keyb)\n", ctrl
,
317 dev
= &ps2_device
[PS2_DEVICE_KEYB
];
318 error
= (ctrl
& 0xC0) != 0;
321 dev
->history
[1] = dev
->history
[0];
322 dev
->history
[0].time
= system_time();
323 dev
->history
[0].data
= data
;
324 dev
->history
[0].error
= error
;
326 return ps2_dev_handle_int(dev
);
330 // #pragma mark - driver interface
338 TRACE("ps2: init\n");
340 status
= get_module(B_ISA_MODULE_NAME
, (module_info
**)&gIsa
);
344 gControllerSem
= create_sem(1, "ps/2 keyb ctrl");
348 status
= ps2_dev_init();
352 status
= ps2_service_init();
356 status
= install_io_interrupt_handler(INT_PS2_KEYBOARD
, &ps2_interrupt
,
361 status
= install_io_interrupt_handler(INT_PS2_MOUSE
, &ps2_interrupt
, NULL
,
366 // While this might have fixed bug #1185, we can't do this unconditionally
367 // as it obviously messes up many controllers which couldn't reboot anymore
371 // Setup the command byte with disabled keyboard and AUX interrupts
372 // to prevent interrupts storm on some KBCs during active multiplexing
373 // activation procedure. Fixes #7635.
374 status
= ps2_setup_command_byte(false);
376 INFO("ps2: initial setup of command byte failed\n");
381 status
= ps2_setup_active_multiplexing(&gActiveMultiplexingEnabled
);
383 INFO("ps2: setting up active multiplexing failed\n");
387 status
= ps2_setup_command_byte(true);
389 INFO("ps2: setting up command byte with enabled interrupts failed\n");
393 if (gActiveMultiplexingEnabled
) {
394 if (ps2_dev_command_timeout(&ps2_device
[PS2_DEVICE_MOUSE
],
395 PS2_CMD_MOUSE_SET_SCALE11
, NULL
, 0, NULL
, 0, 100000)
397 INFO("ps2: accessing multiplexed mouse port 0 timed out, ignoring it!\n");
399 ps2_service_notify_device_added(&ps2_device
[PS2_DEVICE_MOUSE
]);
401 ps2_service_notify_device_added(&ps2_device
[PS2_DEVICE_MOUSE
+ 1]);
402 ps2_service_notify_device_added(&ps2_device
[PS2_DEVICE_MOUSE
+ 2]);
403 ps2_service_notify_device_added(&ps2_device
[PS2_DEVICE_MOUSE
+ 3]);
404 ps2_service_notify_device_added(&ps2_device
[PS2_DEVICE_KEYB
]);
406 ps2_service_notify_device_added(&ps2_device
[PS2_DEVICE_MOUSE
]);
407 ps2_service_notify_device_added(&ps2_device
[PS2_DEVICE_KEYB
]);
410 TRACE("ps2: init done!\n");
414 remove_io_interrupt_handler(INT_PS2_MOUSE
, &ps2_interrupt
, NULL
);
416 remove_io_interrupt_handler(INT_PS2_KEYBOARD
, &ps2_interrupt
, NULL
);
422 delete_sem(gControllerSem
);
423 put_module(B_ISA_MODULE_NAME
);
424 TRACE("ps2: init failed!\n");
432 TRACE("ps2: uninit\n");
433 remove_io_interrupt_handler(INT_PS2_MOUSE
, &ps2_interrupt
, NULL
);
434 remove_io_interrupt_handler(INT_PS2_KEYBOARD
, &ps2_interrupt
, NULL
);
437 delete_sem(gControllerSem
);
438 put_module(B_ISA_MODULE_NAME
);