vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / bus_managers / ps2 / ps2_common.cpp
blobd0560f272b26ae7d4f1acd0559cfa7d051d728b7
1 /*
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>
9 */
11 /*! PS/2 bus manager */
14 #include <string.h>
16 #include "ps2_common.h"
17 #include "ps2_service.h"
18 #include "ps2_dev.h"
21 isa_module_info *gIsa = NULL;
22 bool gActiveMultiplexingEnabled = false;
23 sem_id gControllerSem;
25 static int32 sIgnoreInterrupts = 0;
28 uint8
29 ps2_read_ctrl(void)
31 return gIsa->read_io_8(PS2_PORT_CTRL);
35 uint8
36 ps2_read_data(void)
38 return gIsa->read_io_8(PS2_PORT_DATA);
42 void
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);
51 void
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);
60 status_t
61 ps2_wait_read(void)
63 int i;
64 for (i = 0; i < PS2_CTRL_WAIT_TIMEOUT / 50; i++) {
65 if (ps2_read_ctrl() & PS2_STATUS_OUTPUT_BUFFER_FULL)
66 return B_OK;
67 snooze(50);
69 return B_ERROR;
73 status_t
74 ps2_wait_write(void)
76 int i;
77 for (i = 0; i < PS2_CTRL_WAIT_TIMEOUT / 50; i++) {
78 if (!(ps2_read_ctrl() & PS2_STATUS_INPUT_BUFFER_FULL))
79 return B_OK;
80 snooze(50);
82 return B_ERROR;
86 // #pragma mark -
89 void
90 ps2_flush(void)
92 int i;
94 acquire_sem(gControllerSem);
95 atomic_add(&sIgnoreInterrupts, 1);
97 for (i = 0; i < 64; i++) {
98 uint8 ctrl;
99 uint8 data;
100 ctrl = ps2_read_ctrl();
101 if (!(ctrl & PS2_STATUS_OUTPUT_BUFFER_FULL))
102 break;
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");
105 snooze(100);
108 atomic_add(&sIgnoreInterrupts, -1);
109 release_sem(gControllerSem);
113 static status_t
114 ps2_selftest()
116 status_t res;
117 uint8 in;
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);
122 return B_ERROR;
124 return B_OK;
128 static status_t
129 ps2_setup_command_byte(bool interruptsEnabled)
131 status_t res;
132 uint8 cmdbyte;
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",
136 res, cmdbyte);
137 if (res != B_OK)
138 cmdbyte = 0x47;
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;
145 else
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",
150 res, cmdbyte);
152 return res;
156 static status_t
157 ps2_setup_active_multiplexing(bool *enabled)
159 status_t res;
160 uint8 in, out;
162 out = 0xf0;
163 res = ps2_command(0xd3, &out, 1, &in, 1);
164 if (res)
165 goto fail;
166 // Step 1, if controller is good, in does match out.
167 // This test failes with MS Virtual PC.
168 if (in != out)
169 goto no_support;
171 out = 0x56;
172 res = ps2_command(0xd3, &out, 1, &in, 1);
173 if (res)
174 goto fail;
175 // Step 2, if controller is good, in does match out.
176 if (in != out)
177 goto no_support;
179 out = 0xa4;
180 res = ps2_command(0xd3, &out, 1, &in, 1);
181 if (res)
182 goto fail;
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.
185 if (in == out)
186 goto no_support;
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.
191 if (in > 0x9f) {
192 TRACE("ps2: active multiplexing v%d.%d detected, but ignored!\n", (in >> 4), in & 0xf);
193 goto no_support;
196 INFO("ps2: active multiplexing v%d.%d enabled\n", (in >> 4), in & 0xf);
197 *enabled = true;
198 goto done;
200 no_support:
201 TRACE("ps2: active multiplexing not supported\n");
202 *enabled = false;
204 done:
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
207 // interface) next.
208 // This fixes bug report #1175
209 res = ps2_command(0xae, NULL, 0, NULL, 0);
210 if (res != B_OK) {
211 INFO("ps2: active multiplexing d3 workaround failed, status 0x%08"
212 B_PRIx32 "\n", res);
214 return B_OK;
216 fail:
217 TRACE("ps2: testing for active multiplexing failed\n");
218 *enabled = false;
219 // this should revert the controller into legacy mode,
220 // just in case it has switched to multiplexed mode
221 return ps2_selftest();
225 status_t
226 ps2_command(uint8 cmd, const uint8 *out, int outCount, uint8 *in, int inCount)
228 status_t res;
229 int i;
231 acquire_sem(gControllerSem);
232 atomic_add(&sIgnoreInterrupts, 1);
234 #ifdef TRACE_PS2
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]);
238 #endif
240 res = ps2_wait_write();
241 if (res == B_OK)
242 ps2_write_ctrl(cmd);
244 for (i = 0; res == B_OK && i < outCount; i++) {
245 res = ps2_wait_write();
246 if (res == B_OK)
247 ps2_write_data(out[i]);
248 else
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();
254 if (res == B_OK)
255 in[i] = ps2_read_data();
256 else
257 TRACE("ps2: ps2_command in byte %d failed\n", i);
260 #ifdef TRACE_PS2
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);
264 #endif
266 atomic_add(&sIgnoreInterrupts, -1);
267 release_sem(gControllerSem);
269 return res;
273 // #pragma mark -
276 static int32
277 ps2_interrupt(void* cookie)
279 uint8 ctrl;
280 uint8 data;
281 bool error;
282 ps2_dev *dev;
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) {
300 uint8 idx;
301 if (gActiveMultiplexingEnabled) {
302 idx = ctrl >> 6;
303 error = (ctrl & 0x04) != 0;
304 TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (mouse %d)\n",
305 ctrl, data, idx);
306 } else {
307 idx = 0;
308 error = (ctrl & 0xC0) != 0;
309 TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (aux)\n", ctrl,
310 data);
312 dev = &ps2_device[PS2_DEVICE_MOUSE + idx];
313 } else {
314 TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (keyb)\n", ctrl,
315 data);
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
333 status_t
334 ps2_init(void)
336 status_t status;
338 TRACE("ps2: init\n");
340 status = get_module(B_ISA_MODULE_NAME, (module_info **)&gIsa);
341 if (status < B_OK)
342 return status;
344 gControllerSem = create_sem(1, "ps/2 keyb ctrl");
346 ps2_flush();
348 status = ps2_dev_init();
349 if (status < B_OK)
350 goto err1;
352 status = ps2_service_init();
353 if (status < B_OK)
354 goto err2;
356 status = install_io_interrupt_handler(INT_PS2_KEYBOARD, &ps2_interrupt,
357 NULL, 0);
358 if (status)
359 goto err3;
361 status = install_io_interrupt_handler(INT_PS2_MOUSE, &ps2_interrupt, NULL,
363 if (status)
364 goto err4;
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
368 // after that
369 //ps2_selftest();
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);
375 if (status) {
376 INFO("ps2: initial setup of command byte failed\n");
377 goto err5;
380 ps2_flush();
381 status = ps2_setup_active_multiplexing(&gActiveMultiplexingEnabled);
382 if (status) {
383 INFO("ps2: setting up active multiplexing failed\n");
384 goto err5;
387 status = ps2_setup_command_byte(true);
388 if (status) {
389 INFO("ps2: setting up command byte with enabled interrupts failed\n");
390 goto err5;
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)
396 == B_TIMED_OUT) {
397 INFO("ps2: accessing multiplexed mouse port 0 timed out, ignoring it!\n");
398 } else {
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]);
405 } else {
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");
411 return B_OK;
413 err5:
414 remove_io_interrupt_handler(INT_PS2_MOUSE, &ps2_interrupt, NULL);
415 err4:
416 remove_io_interrupt_handler(INT_PS2_KEYBOARD, &ps2_interrupt, NULL);
417 err3:
418 ps2_service_exit();
419 err2:
420 ps2_dev_exit();
421 err1:
422 delete_sem(gControllerSem);
423 put_module(B_ISA_MODULE_NAME);
424 TRACE("ps2: init failed!\n");
425 return B_ERROR;
429 void
430 ps2_uninit(void)
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);
435 ps2_service_exit();
436 ps2_dev_exit();
437 delete_sem(gControllerSem);
438 put_module(B_ISA_MODULE_NAME);