diff --git a/src/add-ons/kernel/drivers/input/ps2_hid/common.c b/src/add-ons/kernel/drivers/input/ps2_hid/common.c index df06b1c964..f32ca2e12d 100644 --- a/src/add-ons/kernel/drivers/input/ps2_hid/common.c +++ b/src/add-ons/kernel/drivers/input/ps2_hid/common.c @@ -6,6 +6,7 @@ * PS/2 hid device driver * Authors (in chronological order): * Stefano Ceccherini (burton666@libero.it) + * Axel Dörfler, axeld@pinc-software.de */ @@ -19,10 +20,8 @@ #define DEVICE_KEYBOARD_NAME "input/keyboard/at/0" int32 api_version = B_CUR_DRIVER_API_VERSION; -isa_module_info *gIsa = NULL; -device_hooks -keyboard_hooks = { +static device_hooks sKeyboardDeviceHooks = { keyboard_open, keyboard_close, keyboard_freecookie, @@ -35,9 +34,7 @@ keyboard_hooks = { NULL }; - -device_hooks -mouse_hooks = { +static device_hooks sMouseDeviceHooks = { mouse_open, mouse_close, mouse_freecookie, @@ -51,6 +48,79 @@ mouse_hooks = { }; +isa_module_info *gIsa = NULL; +sem_id gDeviceOpenSemaphore; + +static int32 sInitialized = 0; +static uint8 sCommandByte = 0; +static bool sKeyboardDetected = false; + +static sem_id sResultSemaphore; +static sem_id sResultOwnerSemaphore; +static uint8 *sResultBuffer; +static int32 sResultBytes; + + +/** Wait until the specified status bits are cleared or set, depending on the + * second parameter. + * This currently busy waits, but should nonetheless be avoided in interrupt + * handlers. + */ + +static status_t +wait_for_status(int32 bits, bool set) +{ + int8 read; + int32 tries = 100; + + TRACE(("wait_write_ctrl(bits = %lx, %s)\n", bits, set ? "set" : "cleared")); + + while (tries-- > 0) { + read = gIsa->read_io_8(PS2_PORT_CTRL); + if (((read & bits) != 0) == set) + return B_OK; + + spin(100); + } + + return B_ERROR; +} + + +static inline status_t +wait_for_bits_cleared(int32 bits) +{ + return wait_for_status(bits, false); +} + + +static inline status_t +wait_for_bits_set(int32 bits) +{ + return wait_for_status(bits, true); +} + + +/** Reads the command byte from the 8042 controller. + * Since the read goes through the data port, this function must not be + * called when the keyboard driver is up and running. + */ + +static status_t +get_command_byte(uint8 *data) +{ + TRACE(("get_command_byte()\n")); + + if (ps2_write_ctrl(PS2_CTRL_READ_CMD) == B_OK) + return ps2_read_data(data); + + return B_ERROR; +} + + +// #pragma mark - + + /** Wait until the control port is ready to be written. This requires that * the "Input buffer full" and "Output buffer full" bits will both be set * to 0. Returns true if the control port is ready to be written, false @@ -58,131 +128,159 @@ mouse_hooks = { * port is still busy. */ -bool -wait_write_ctrl(void) +status_t +ps2_write_ctrl(uint8 data) { - int8 read; - int32 tries = 100; - TRACE(("wait_write_ctrl()\n")); - do { - read = gIsa->read_io_8(PS2_PORT_CTRL); - spin(100); - } while ((read & (PS2_IBUF_FULL | PS2_OBUF_FULL)) && tries-- > 0); + if (wait_for_bits_cleared(PS2_STATUS_INPUT_BUFFER_FULL | PS2_STATUS_OUTPUT_BUFFER_FULL) != B_OK) + return B_ERROR; - return tries > 0; + gIsa->write_io_8(PS2_PORT_CTRL, data); + return B_OK; } -/** Wait until the data port is ready to be written. This requires that - * the "Input buffer full" bit will be set to 0. +/** Wait until the data port is ready to be written, and then writes \a data to it. */ -bool -wait_write_data(void) +status_t +ps2_write_data(uint8 data) { - int8 read; - int32 tries = 100; - TRACE(("wait_write_data()\n")); - do { - read = gIsa->read_io_8(PS2_PORT_CTRL); - spin(100); - } while ((read & PS2_IBUF_FULL) && tries-- > 0); + if (wait_for_bits_cleared(PS2_STATUS_INPUT_BUFFER_FULL) != B_OK) + return B_ERROR; - return tries > 0; + gIsa->write_io_8(PS2_PORT_DATA, data); + return B_OK; } -/** Wait until the data port can be read from. This requires that the - * "Output buffer full" bit will be set to 1. +/** Wait until the data port can be read from, and then transfers the byte read + * to /a data. */ -bool -wait_read_data(void) +status_t +ps2_read_data(uint8 *data) { - int8 read; - int32 tries = 100; - TRACE(("wait_read_data()\n")); - do { - read = gIsa->read_io_8(PS2_PORT_CTRL); - spin(100); - } while (!(read & PS2_OBUF_FULL) && tries-- > 0); + if (wait_for_bits_set(PS2_STATUS_OUTPUT_BUFFER_FULL) != B_OK) + return B_ERROR; - return tries > 0; + *data = gIsa->read_io_8(PS2_PORT_DATA); + TRACE(("ps2_read_data(): read %u\n", *data)); + return B_OK; } -/** Get the ps2 command byte. + +/** Get the PS/2 command byte. This cannot fail, since we're using our buffered + * data, read out in init_driver(). */ -int8 -get_command_byte(void) +uint8 +ps2_get_command_byte(void) { - int8 read = 0; + TRACE(("ps2_get_command_byte(): command byte = %x\n", sCommandByte)); - TRACE(("set_command_byte()\n")); - if (wait_write_ctrl()) { - gIsa->write_io_8(PS2_PORT_CTRL, PS2_CTRL_READ_CMD); - if (wait_read_data()) - read = gIsa->read_io_8(PS2_PORT_DATA); - } - - return read; + return sCommandByte; } /** Set the ps2 command byte. - * Parameters: - * unsigned char, byte to write */ -void -set_command_byte(unsigned char cmd) +status_t +ps2_set_command_byte(uint8 command) { - TRACE(("set_command_byte()\n")); - if (wait_write_ctrl()) { - gIsa->write_io_8(PS2_PORT_CTRL, PS2_CTRL_WRITE_CMD); - if (wait_write_data()) - gIsa->write_io_8(PS2_PORT_DATA, cmd); + TRACE(("set_command_byte(command = %x)\n", command)); + + if (ps2_write_ctrl(PS2_CTRL_WRITE_CMD) == B_OK + && ps2_write_data(command) == B_OK) { + sCommandByte = command; + return B_OK; } + + return B_ERROR; } -/** Reads a single byte from the data port. - * Return value: - * unsigned char, byte read - */ - -uint8 -read_data_byte(void) +bool +ps2_handle_result(uint8 data) { - TRACE(("read_data_byte()\n")); - if (wait_read_data()) { - TRACE(("read_data_byte(): ok\n")); - return gIsa->read_io_8(PS2_PORT_DATA); - } - - TRACE(("read_data_byte(): timeout\n")); - - return PS2_ERROR; + int32 bytesLeft; + + if (sResultBuffer == NULL + || (bytesLeft = atomic_add(&sResultBytes, -1)) <= 0) + return false; + + *(sResultBuffer++) = data; + if (bytesLeft == 1) + release_sem_etc(sResultSemaphore, 1, B_DO_NOT_RESCHEDULE); + return true; } -/** Writes a byte to the mouse device. Uses the control port to indicate - * that the byte is sent to the auxiliary device (mouse), instead of the - * keyboard. - * Parameters: - * unsigned char, byte to write - */ +void +ps2_claim_result(uint8 *buffer, size_t bytes) +{ + acquire_sem(sResultOwnerSemaphore); + + sResultBuffer = buffer; + sResultBytes = bytes; +} + void -write_aux_byte(unsigned char cmd) +ps2_unclaim_result(void) { - TRACE(("write_aux_byte()\n")); - if (wait_write_ctrl()) { - gIsa->write_io_8(PS2_PORT_CTRL, PS2_CTRL_WRITE_AUX); + sResultBytes = 0; + sResultBuffer = NULL; - if (wait_write_data()) - gIsa->write_io_8(PS2_PORT_DATA, cmd); + release_sem(sResultOwnerSemaphore); +} + + +status_t +ps2_wait_for_result(void) +{ + status_t status = acquire_sem_etc(sResultSemaphore, 1, B_RELATIVE_TIMEOUT, 100000); + // 0.1 secs for now + + ps2_unclaim_result(); + return status; +} + + +void +ps2_common_uninitialize(void) +{ + // do we still need the resources? + if (atomic_add(&sInitialized, -1) > 1) + return; + + delete_sem(sResultSemaphore); + delete_sem(sResultOwnerSemaphore); +} + + +status_t +ps2_common_initialize(void) +{ + if (atomic_add(&sInitialized, 1) > 0) { + // we're already initialized + return B_OK; } + + sResultSemaphore = create_sem(0, "ps/2 result"); + if (sResultSemaphore < B_OK) + return sResultSemaphore; + + sResultOwnerSemaphore = create_sem(1, "ps/2 result owner"); + if (sResultOwnerSemaphore < B_OK) { + delete_sem(sResultSemaphore); + return sResultOwnerSemaphore; + } + + sResultBytes = 0; + sResultBuffer = NULL; + + return B_OK; } @@ -197,18 +295,20 @@ init_hardware(void) } -static const char * -kDevices[] = { - DEVICE_KEYBOARD_NAME, - DEVICE_MOUSE_NAME, - NULL -}; - - const char ** publish_devices(void) { - return kDevices; + static char *kDevices[3]; + + kDevices[0] = DEVICE_MOUSE_NAME; + + if (sKeyboardDetected) { + kDevices[1] = DEVICE_KEYBOARD_NAME; + kDevices[2] = NULL; + } else + kDevices[1] = NULL; + + return (const char **)kDevices; } @@ -216,9 +316,9 @@ device_hooks * find_device(const char *name) { if (!strcmp(name, DEVICE_MOUSE_NAME)) - return &mouse_hooks; + return &sMouseDeviceHooks; else if (!strcmp(name, DEVICE_KEYBOARD_NAME)) - return &keyboard_hooks; + return &sKeyboardDeviceHooks; return NULL; } @@ -235,12 +335,31 @@ init_driver(void) return status; } - return B_OK; + // If there is no keyboard or mouse, we don't need to publish ourselves + + if (probe_keyboard() == B_OK) + sKeyboardDetected = true; + else + dprintf("ps2_hid: no keyboard detected!\n"); + + if (!sKeyboardDetected && probe_mouse() != B_OK) { + put_module(B_ISA_MODULE_NAME); + return B_ERROR; + } + + // A semaphore to synchronize open keyboard and mouse devices + + gDeviceOpenSemaphore = create_sem(1, "ps/2 open"); + if (gDeviceOpenSemaphore < B_OK) + return gDeviceOpenSemaphore; + + return get_command_byte(&sCommandByte); } void uninit_driver(void) { + delete_sem(gDeviceOpenSemaphore); put_module(B_ISA_MODULE_NAME); } diff --git a/src/add-ons/kernel/drivers/input/ps2_hid/common.h b/src/add-ons/kernel/drivers/input/ps2_hid/common.h index 38c37c87dd..cdb3de820c 100644 --- a/src/add-ons/kernel/drivers/input/ps2_hid/common.h +++ b/src/add-ons/kernel/drivers/input/ps2_hid/common.h @@ -19,59 +19,67 @@ ///////////////////////////////////////////////////////////////////////// -// definitions +// Interface definitions for the Intel 8042, 8741, or 8742 (PS/2) // I/O addresses -#define PS2_PORT_DATA 0x60 -#define PS2_PORT_CTRL 0x64 +#define PS2_PORT_DATA 0x60 +#define PS2_PORT_CTRL 0x64 // data port bits -#define PS2_OBUF_FULL 0x01 -#define PS2_IBUF_FULL 0x02 -#define PS2_MOUSE_OBUF_FULL 0x20 -#define PS2_TIMEOUT 0x40 +#define PS2_STATUS_OUTPUT_BUFFER_FULL 0x01 +#define PS2_STATUS_INPUT_BUFFER_FULL 0x02 +#define PS2_STATUS_MOUSE_DATA 0x20 +#define PS2_STATUS_TIMEOUT 0x40 // control words -#define PS2_CTRL_READ_CMD 0x20 -#define PS2_CTRL_WRITE_CMD 0x60 -#define PS2_CTRL_WRITE_AUX 0xD4 -#define PS2_CTRL_MOUSE_DISABLE 0xA7 -#define PS2_CTRL_MOUSE_ENABLE 0xA8 -#define PS2_CTRL_MOUSE_TEST 0xA9 +#define PS2_CTRL_READ_CMD 0x20 +#define PS2_CTRL_WRITE_CMD 0x60 +#define PS2_CTRL_WRITE_AUX 0xd4 +#define PS2_CTRL_MOUSE_DISABLE 0xa7 +#define PS2_CTRL_MOUSE_ENABLE 0xa8 +#define PS2_CTRL_MOUSE_TEST 0xa9 +#define PS2_CTRL_KEYBOARD_SELF_TEST 0xaa +#define PS2_CTRL_KEYBOARD_ACTIVATE 0xae +#define PS2_CTRL_KEYBOARD_DEACTIVATE 0xad // command bytes -#define PS2_CMD_DEV_INIT 0x43 +#define PS2_CMD_DEV_INIT 0x43 // command bits -#define PS2_BITS_AUX_INTERRUPT 0x02 -#define PS2_BITS_MOUSE_DISABLED 0x20 +#define PS2_BITS_KEYBOARD_INTERRUPT 0x01 +#define PS2_BITS_AUX_INTERRUPT 0x02 +#define PS2_BITS_KEYBOARD_DISABLED 0x10 +#define PS2_BITS_MOUSE_DISABLED 0x20 +#define PS2_BITS_TRANSLATE_SCANCODES 0x40 // data words -#define PS2_CMD_TEST_PASSED 0xAA -#define PS2_CMD_GET_DEVICE_ID 0xF2 -#define PS2_CMD_SET_SAMPLE_RATE 0xF3 -#define PS2_CMD_ENABLE_MOUSE 0xF4 -#define PS2_CMD_DISABLE_MOUSE 0xF5 -#define PS2_CMD_RESET_MOUSE 0xFF +#define PS2_CMD_TEST_PASSED 0xaa +#define PS2_CMD_GET_DEVICE_ID 0xf2 +#define PS2_CMD_SET_SAMPLE_RATE 0xf3 +#define PS2_CMD_ENABLE_MOUSE 0xf4 +#define PS2_CMD_DISABLE_MOUSE 0xf5 +#define PS2_CMD_RESET_MOUSE 0xff // reply codes -#define PS2_RES_TEST_PASSED 0x55 -#define PS2_RES_ACK 0xFA -#define PS2_RES_RESEND 0xFE -#define PS2_ERROR 0xFC +#define PS2_REPLY_TEST_PASSED 0x55 +#define PS2_REPLY_ACK 0xfa +#define PS2_REPLY_RESEND 0xfe +#define PS2_REPLY_ERROR 0xfc // interrupts -#define INT_PS2_MOUSE 0x0C -#define INT_PS2_KEYBOARD 0x01 +#define INT_PS2_MOUSE 0x0c +#define INT_PS2_KEYBOARD 0x01 // mouse device IDs -#define PS2_DEV_ID_STANDARD 0 -#define PS2_DEV_ID_INTELLIMOUSE 3 +#define PS2_DEV_ID_STANDARD 0 +#define PS2_DEV_ID_INTELLIMOUSE 3 // packet sizes -#define PS2_PACKET_STANDARD 3 -#define PS2_PACKET_INTELLIMOUSE 4 -#define PS2_MAX_PACKET_SIZE 4 // Should be equal to the biggest packet size +#define PS2_PACKET_STANDARD 3 +#define PS2_PACKET_INTELLIMOUSE 4 +#define PS2_MAX_PACKET_SIZE 4 + // Should be equal to the biggest packet size + // debug defines #ifdef DEBUG @@ -82,16 +90,28 @@ // global variables extern isa_module_info *gIsa; +extern sem_id gDeviceOpenSemaphore; -// prototypes -bool wait_write_ctrl(void); -bool wait_write_data(void); -bool wait_read_data(void); -void write_aux_byte(unsigned char cmd); -uint8 read_data_byte(void); +// prototypes from common.c +extern status_t ps2_write_ctrl(uint8 data); +extern status_t ps2_write_data(uint8 data); +extern status_t ps2_read_data(uint8 *data); -int8 get_command_byte(void); -void set_command_byte(unsigned char cmd); +extern status_t ps2_write_aux_byte(uint8 data); +extern uint8 ps2_get_command_byte(void); +extern status_t ps2_set_command_byte(uint8 command); + +extern void ps2_claim_result(uint8 *buffer, size_t bytes); +extern void ps2_unclaim_result(void); +extern status_t ps2_wait_for_result(void); +extern bool ps2_handle_result(uint8 data); + +extern void ps2_common_uninitialize(void); +extern status_t ps2_common_initialize(void); + +// prototypes from keyboard.c & mouse.c +extern status_t probe_keyboard(void); +extern status_t probe_mouse(void); extern status_t keyboard_open(const char *name, uint32 flags, void **cookie); extern status_t keyboard_close(void *cookie); diff --git a/src/add-ons/kernel/drivers/input/ps2_hid/keyboard.c b/src/add-ons/kernel/drivers/input/ps2_hid/keyboard.c index a358123962..90a79462fd 100644 --- a/src/add-ons/kernel/drivers/input/ps2_hid/keyboard.c +++ b/src/add-ons/kernel/drivers/input/ps2_hid/keyboard.c @@ -27,20 +27,113 @@ enum { } leds_status; enum { - PS2_SET_LEDS = 0xed -} commands; + PS2_DATA_SET_LEDS = 0xed, + PS2_ENABLE_KEYBOARD = 0xf4, + + EXTENDED_KEY = 0xe0, +}; static int32 sKeyboardOpenMask; static sem_id sKeyboardSem; static struct packet_buffer *sKeyBuffer; static bool sIsExtended = false; +static bool sInterruptHandlerInstalled = false; -static void +static status_t +keyboard_write_byte(uint8 byte) +{ + uint8 acknowledged = 0; + + TRACE(("keyboard_write_byte(byte = %u)\n", byte)); + + if (sInterruptHandlerInstalled) { + ps2_claim_result(&acknowledged, 1); + + if (ps2_write_data(byte) != B_OK) { + ps2_unclaim_result(); + return B_TIMED_OUT; + } + + ps2_wait_for_result(); + } else { + status_t status = ps2_write_data(byte); + if (status == B_OK) + status = ps2_read_data(&acknowledged); + + if (status != B_OK) + return status; + } + + return acknowledged == PS2_REPLY_ACK ? B_OK : B_ERROR; +} + + +static status_t +keyboard_read_bytes(uint8 *buffer, size_t bufferSize) +{ + uint32 i; + + TRACE(("keyboard_read_bytes(bufferSize = %lu)\n", bufferSize)); + + if (sInterruptHandlerInstalled) { + ps2_claim_result(buffer, bufferSize); + return ps2_wait_for_result(); + } + + for (i = 0; i < bufferSize; i++) { + status_t status = ps2_read_data(&buffer[i]); + if (status != B_OK) + return status; + } + + return B_OK; +} + + +static void +keyboard_empty_data(void) +{ + uint8 data; + + TRACE(("keyboard_empty_data()\n")); + + while (ps2_read_data(&data) == B_OK) { + // empty keyboard output buffer + } +} + + +static status_t +keyboard_command(uint8 command, uint8 *buffer, size_t bufferSize) +{ + status_t status; + uint32 i; + + TRACE(("keyboard_command(command = %u, bufferSize = %lu)\n", command, bufferSize)); + + status = keyboard_write_byte(command); + if (status != B_OK) + return status; + + for (i = 0; i < bufferSize; i++) { + status = keyboard_write_byte(buffer[i]); + if (status != B_OK) + return status; + } + + return B_OK; +} + + +static status_t set_leds(led_info *ledInfo) { - int leds = 0; + uint8 leds = 0; + + TRACE(("ps2_hid: set keyboard LEDs\n")); + if (ledInfo->scroll_lock) leds |= LED_SCROLL; if (ledInfo->num_lock) @@ -48,59 +141,69 @@ set_leds(led_info *ledInfo) if (ledInfo->caps_lock) leds |= LED_CAPS; - wait_write_data(); - gIsa->write_io_8(PS2_PORT_DATA, PS2_SET_LEDS); - wait_write_data(); - gIsa->write_io_8(PS2_PORT_DATA, leds); + return keyboard_command(PS2_DATA_SET_LEDS, &leds, sizeof(leds)); } -static int32 +static int32 handle_keyboard_interrupt(void *data) { - // TODO: Handle braindead "pause" key special case - at_kbd_io keyInfo; - unsigned char read, scancode; - - read = gIsa->read_io_8(PS2_PORT_DATA); + uint8 read = gIsa->read_io_8(PS2_PORT_CTRL); TRACE(("handle_keyboard_interrupt: read = 0x%x\n", read)); - if (read == 0xE0) { - sIsExtended = true; - TRACE(("Extended key\n")); - return B_HANDLED_INTERRUPT; - } + if (read & PS2_STATUS_OUTPUT_BUFFER_FULL) { + at_kbd_io keyInfo; + uint8 scancode; - scancode = read; + read = gIsa->read_io_8(PS2_PORT_DATA); - TRACE(("scancode: %x\n", scancode)); + // someone else might wait for a result from the keyboard controller + if (ps2_handle_result(read)) + return B_INVOKE_SCHEDULER; - // For now, F12 enters the kernel debugger - // ToDo: remove me later :-) - if (scancode == 88) - panic("keyboard requested halt.\n"); + // TODO: Handle braindead "pause" key special case - if (scancode & 0x80) { - keyInfo.is_keydown = false; - scancode -= 0x80; - } else - keyInfo.is_keydown = true; + if (read == EXTENDED_KEY) { + sIsExtended = true; + TRACE(("Extended key\n")); + return B_HANDLED_INTERRUPT; + } - if (sIsExtended) { - scancode |= 0x80; - sIsExtended = false; + scancode = read; + + TRACE(("scancode: %x\n", scancode)); + + // For now, F12 enters the kernel debugger + // ToDo: remove me later :-) + if (scancode == 88) + panic("keyboard requested halt.\n"); + + if (scancode & 0x80) { + keyInfo.is_keydown = false; + scancode -= 0x80; + } else + keyInfo.is_keydown = true; + + if (sIsExtended) { + scancode |= 0x80; + sIsExtended = false; + } + + keyInfo.timestamp = system_time(); + keyInfo.scancode = scancode; + + while (packet_buffer_write(sKeyBuffer, (uint8 *)&keyInfo, sizeof(keyInfo)) == 0) { + // if there is no space left in the buffer, we start dropping old key strokes + packet_buffer_flush(sKeyBuffer, sizeof(keyInfo)); + } + + release_sem_etc(sKeyboardSem, 1, B_DO_NOT_RESCHEDULE); + } else { + // ToDo: the buffer is not yet available, we should come back soon... + // (depending on how often we see the message below... :-) + dprintf("ps2_hid: keyboard: buffer not available!\n"); } - keyInfo.timestamp = system_time(); - keyInfo.scancode = scancode; - - while (packet_buffer_write(sKeyBuffer, (uint8 *)&keyInfo, sizeof(keyInfo)) == 0) { - // if there is no space left in the buffer, we start dropping old key strokes - packet_buffer_flush(sKeyBuffer, sizeof(keyInfo)); - } - - release_sem_etc(sKeyboardSem, 1, B_DO_NOT_RESCHEDULE); - return B_INVOKE_SCHEDULER; } @@ -109,8 +212,11 @@ static status_t read_keyboard_packet(at_kbd_io *userBuffer) { at_kbd_io packet; + status_t status; - status_t status = acquire_sem_etc(sKeyboardSem, 1, B_CAN_INTERRUPT, 0); + TRACE(("read_keyboard_packet()\n")); + + status = acquire_sem_etc(sKeyboardSem, 1, B_CAN_INTERRUPT, 0); if (status < B_OK) return status; @@ -125,58 +231,156 @@ read_keyboard_packet(at_kbd_io *userBuffer) } +static status_t +enable_keyboard(void) +{ + uint32 tries = 3; + + keyboard_empty_data(); + + while (tries-- > 0) { + if (keyboard_command(PS2_ENABLE_KEYBOARD, NULL, 0) == B_OK) + return B_OK; + } + + return B_ERROR; +} + + // #pragma mark - -status_t +status_t +probe_keyboard(void) +{ + uint32 tries; + + // ToDo: for now there just is a keyboard ready to be used... + + keyboard_empty_data(); + + // Keyboard self-test + + if (ps2_write_ctrl(PS2_CTRL_KEYBOARD_SELF_TEST) != B_OK) + return B_ERROR; + + tries = 3; + while (tries-- > 0) { + uint8 acknowledged; + if (ps2_read_data(&acknowledged) == B_OK && acknowledged == PS2_REPLY_ACK) + break; + } + if (tries < 0) + return B_ERROR; + + // Activate keyboard + + if (ps2_write_ctrl(PS2_CTRL_KEYBOARD_ACTIVATE) != B_OK) + return B_ERROR; + + // Enable keyboard + + return enable_keyboard(); +} + + +// #pragma mark - + + +status_t keyboard_open(const char *name, uint32 flags, void **_cookie) { + uint8 commandByte; + status_t status; + TRACE(("keyboard open()\n")); if (atomic_or(&sKeyboardOpenMask, 1) != 0) return B_BUSY; + acquire_sem(gDeviceOpenSemaphore); + + status = ps2_common_initialize(); + if (status != B_OK) + goto err1; + sKeyboardSem = create_sem(0, "keyboard_sem"); if (sKeyboardSem < 0) { - atomic_and(&sKeyboardOpenMask, 0); - return sKeyboardSem; + status = sKeyboardSem; + goto err2; } sKeyBuffer = create_packet_buffer(KEY_BUFFER_SIZE * sizeof(at_kbd_io)); if (sKeyBuffer == NULL) { - panic("could not create keyboard packet buffer!\n"); - atomic_and(&sKeyboardOpenMask, 0); - return B_NO_MEMORY; + status = B_NO_MEMORY; + goto err3; } + keyboard_empty_data(); + + commandByte = ps2_get_command_byte() + | PS2_BITS_KEYBOARD_INTERRUPT | PS2_BITS_TRANSLATE_SCANCODES; + commandByte &= ~PS2_BITS_KEYBOARD_DISABLED; + + status = ps2_set_command_byte(commandByte); + if (status < B_OK) { + TRACE(("keyboard_open(): cannot set command byte\n")); + goto err4; + } + + status = install_io_interrupt_handler(INT_PS2_KEYBOARD, + &handle_keyboard_interrupt, NULL, 0); + if (status < B_OK) + goto err4; + + sInterruptHandlerInstalled = true; + release_sem(gDeviceOpenSemaphore); + *_cookie = NULL; - return install_io_interrupt_handler(INT_PS2_KEYBOARD, &handle_keyboard_interrupt, NULL, 0); + TRACE(("keyboard_open(): done.\n")); + return B_OK; + +err4: + delete_packet_buffer(sKeyBuffer); +err3: + delete_sem(sKeyboardSem); +err2: + ps2_common_uninitialize(); +err1: + atomic_and(&sKeyboardOpenMask, 0); + release_sem(gDeviceOpenSemaphore); + + return status; } -status_t +status_t keyboard_close(void *cookie) { + TRACE(("keyboard_close()\n")); + remove_io_interrupt_handler(INT_PS2_KEYBOARD, &handle_keyboard_interrupt, NULL); + sInterruptHandlerInstalled = false; delete_packet_buffer(sKeyBuffer); delete_sem(sKeyboardSem); + ps2_common_uninitialize(); atomic_and(&sKeyboardOpenMask, 0); return B_OK; } -status_t +status_t keyboard_freecookie(void *cookie) { return B_OK; } -status_t +status_t keyboard_read(void *cookie, off_t pos, void *buffer, size_t *_length) { TRACE(("keyboard read()\n")); @@ -185,7 +389,7 @@ keyboard_read(void *cookie, off_t pos, void *buffer, size_t *_length) } -status_t +status_t keyboard_write(void *cookie, off_t pos, const void *buffer, size_t *_length) { TRACE(("keyboard write()\n")); @@ -194,7 +398,7 @@ keyboard_write(void *cookie, off_t pos, const void *buffer, size_t *_length) } -status_t +status_t keyboard_ioctl(void *cookie, uint32 op, void *buffer, size_t length) { TRACE(("keyboard ioctl()\n")); @@ -210,17 +414,16 @@ keyboard_ioctl(void *cookie, uint32 op, void *buffer, size_t length) return B_BAD_ADDRESS; TRACE(("KB_SET_LEDS\n")); - set_leds(&info); - return B_OK; + return set_leds(&info); } case KB_SET_KEY_REPEATING: case KB_SET_KEY_NONREPEATING: - TRACE(("ioctl 0x%x not implemented yet, returning B_OK\n", op)); + TRACE(("ps2_hid: ioctl 0x%lx not implemented yet, returning B_OK\n", op)); return B_OK; default: - TRACE(("invalid ioctl 0x%x\n", op)); + TRACE(("ps2_hid: invalid ioctl 0x%lx\n", op)); return EINVAL; } } diff --git a/src/add-ons/kernel/drivers/input/ps2_hid/mouse.c b/src/add-ons/kernel/drivers/input/ps2_hid/mouse.c index aed171ee45..1506870c73 100644 --- a/src/add-ons/kernel/drivers/input/ps2_hid/mouse.c +++ b/src/add-ons/kernel/drivers/input/ps2_hid/mouse.c @@ -82,8 +82,24 @@ static int sButtonsState; static int32 sPacketSize; -///////////////////////////////////////////////////////////////////////// -// mouse functions +/** Writes a byte to the mouse device. Uses the control port to indicate + * that the byte is sent to the auxiliary device (mouse), instead of the + * keyboard. + */ + +status_t +ps2_write_aux_byte(uint8 data) +{ + TRACE(("ps2_write_aux_byte(data = %u)\n", data)); + + if (ps2_write_ctrl(PS2_CTRL_WRITE_AUX) == B_OK + && ps2_write_data(data) == B_OK + && ps2_read_data(&data) == B_OK) + return data == PS2_REPLY_ACK ? B_OK : B_TIMED_OUT; + + return B_ERROR; +} + /* static status_t @@ -102,46 +118,24 @@ ps2_reset_mouse() */ -/** Enables or disables mouse reporting for the ps2 port. - */ - -static status_t -ps2_enable_mouse(bool enable) -{ - int32 tries = 2; - uint8 read; - - do { - write_aux_byte(enable ? PS2_CMD_ENABLE_MOUSE : PS2_CMD_DISABLE_MOUSE); - read = read_data_byte(); - if (read == PS2_RES_ACK) - break; - spin(100); - } while (read == PS2_RES_RESEND && tries-- > 0); - - if (read != PS2_RES_ACK) - return B_ERROR; - - return B_OK; -} - - /** Set sampling rate of the ps2 port. */ static status_t ps2_set_sample_rate(uint32 rate) { - status_t status = B_ERROR; + int32 tries = 5; - write_aux_byte(PS2_CMD_SET_SAMPLE_RATE); - if (read_data_byte() == PS2_RES_ACK) { - write_aux_byte(rate); - if (read_data_byte() == PS2_RES_ACK) - status = B_OK; + while (--tries > 0) { + status_t status = ps2_write_aux_byte(PS2_CMD_SET_SAMPLE_RATE); + if (status == B_OK) + status = ps2_write_aux_byte(rate); + + if (status == B_OK) + return B_OK; } - return status; + return B_ERROR; } @@ -187,7 +181,7 @@ ps2_packet_to_movement(uint8 packet[], mouse_movement *pos) pos->wheel_ydelta = (int)wheel_ydelta; pos->wheel_xdelta = (int)wheel_xdelta; - TRACE(("xdelta: %d, ydelta: %d, buttons %x, clicks: %d, timestamp %Ld\n", + TRACE(("xdelta: %d, ydelta: %d, buttons %x, clicks: %ld, timestamp %Ld\n", xDelta, yDelta, buttons, sClickCount, currentTime)); } } @@ -219,8 +213,25 @@ ps2_mouse_read(mouse_movement *userMovement) } -///////////////////////////////////////////////////////////////////////// -// interrupt +/** Enables or disables mouse reporting for the PS/2 port. + */ + +static status_t +set_mouse_enabled(bool enable) +{ + int32 tries = 5; + + while (true) { + if (ps2_write_aux_byte(enable ? + PS2_CMD_ENABLE_MOUSE : PS2_CMD_DISABLE_MOUSE) == B_OK) + return B_OK; + + if (--tries <= 0) + break; + } + + return B_ERROR; +} /** Interrupt handler for the mouse device. Called whenever the I/O @@ -239,7 +250,8 @@ handle_mouse_interrupt(void* data) return B_UNHANDLED_INTERRUPT; } - read = read_data_byte(); + read = gIsa->read_io_8(PS2_PORT_DATA); + TRACE(("mouse interrupt : byte %x\n", read)); while (packet_buffer_write(sMouseBuffer, &read, sizeof(read)) == 0) { // we start dropping packets when the buffer is full @@ -263,6 +275,52 @@ handle_mouse_interrupt(void* data) } +// #pragma mark - + + +status_t +probe_mouse(void) +{ + int8 deviceId = -1; + + // get device id + if (ps2_write_aux_byte(PS2_CMD_GET_DEVICE_ID) == B_OK) + ps2_read_data(&deviceId); + + TRACE(("probe_mouse(): device id: %2x\n", deviceId)); + + if (deviceId == 0) { + int32 tries = 5; + + while (--tries > 0) { + // try to switch to intellimouse mode + if (ps2_set_sample_rate(200) == B_OK + && ps2_set_sample_rate(100) == B_OK + && ps2_set_sample_rate(80) == B_OK) { + // get device id, again + if (ps2_write_aux_byte(PS2_CMD_GET_DEVICE_ID) == B_OK) + ps2_read_data(&deviceId); + break; + } + } + } + + if (deviceId == PS2_DEV_ID_STANDARD) { + sPacketSize = PS2_PACKET_STANDARD; + TRACE(("Standard PS/2 mouse found\n")); + } else if (deviceId == PS2_DEV_ID_INTELLIMOUSE) { + sPacketSize = PS2_PACKET_INTELLIMOUSE; + TRACE(("Extended PS/2 mouse found\n")); + } else { + // Something's wrong. Better quit + dprintf("ps2_hid: No mouse found\n"); + return B_ERROR; + } + + return B_OK; +} + + // #pragma mark - // Device functions @@ -272,48 +330,27 @@ mouse_open(const char *name, uint32 flags, void **_cookie) { status_t status; int8 commandByte; - int8 deviceId = -1; TRACE(("mouse_open()\n")); if (atomic_or(&sOpenMask, 1) != 0) return B_BUSY; - // get device id - write_aux_byte(PS2_CMD_GET_DEVICE_ID); - if (read_data_byte() == PS2_RES_ACK) - deviceId = read_data_byte(); + acquire_sem(gDeviceOpenSemaphore); - TRACE(("init_driver: device id: %2x\n", deviceId)); - if (deviceId == 0) { - // try to switch to intellimouse mode - ps2_set_sample_rate(200); - ps2_set_sample_rate(100); - ps2_set_sample_rate(80); - } + status = probe_mouse(); + if (status != B_OK) + goto err1; - // get device id, again - write_aux_byte(PS2_CMD_GET_DEVICE_ID); - if (read_data_byte() == PS2_RES_ACK) - deviceId = read_data_byte(); - - if (deviceId == PS2_DEV_ID_STANDARD) { - sPacketSize = PS2_PACKET_STANDARD; - TRACE(("Standard ps2 mouse found\n")); - } else if (deviceId == PS2_DEV_ID_INTELLIMOUSE) { - sPacketSize = PS2_PACKET_INTELLIMOUSE; - TRACE(("Extended ps2 mouse found\n")); - } else { - TRACE(("No mouse found\n")); - put_module(B_ISA_MODULE_NAME); - return B_ERROR; // Something's wrong. Better quit - } + status = ps2_common_initialize(); + if (status != B_OK) + goto err1; sMouseBuffer = create_packet_buffer(MOUSE_HISTORY_SIZE * sPacketSize); if (sMouseBuffer == NULL) { TRACE(("can't allocate mouse actions buffer\n")); - put_module(B_ISA_MODULE_NAME); - return B_ERROR; + status = B_NO_MEMORY; + goto err2; } // create the mouse semaphore, used for synchronization between @@ -321,30 +358,50 @@ mouse_open(const char *name, uint32 flags, void **_cookie) sMouseSem = create_sem(0, "ps2_mouse_sem"); if (sMouseSem < 0) { TRACE(("failed creating PS/2 mouse semaphore!\n")); - delete_packet_buffer(sMouseBuffer); - put_module(B_ISA_MODULE_NAME); - return sMouseSem; + status = sMouseSem; + goto err3; } - set_sem_owner(sMouseSem, B_SYSTEM_TEAM); - *_cookie = NULL; - commandByte = get_command_byte(); - commandByte |= PS2_BITS_AUX_INTERRUPT; + commandByte = ps2_get_command_byte() | PS2_BITS_AUX_INTERRUPT; commandByte &= ~PS2_BITS_MOUSE_DISABLED; - set_command_byte(commandByte); - status = ps2_enable_mouse(true); + status = ps2_set_command_byte(commandByte); if (status < B_OK) { - TRACE(("mouse_open(): cannot enable PS/2 mouse\n")); - return B_ERROR; + TRACE(("mouse_open(): sending command byte failed\n")); + goto err4; } - TRACE(("mouse_open(): mouse succesfully enabled\n")); + status = set_mouse_enabled(true); + if (status < B_OK) { + TRACE(("mouse_open(): cannot enable PS/2 mouse\n")); + goto err4; + } - // register interrupt handler - return install_io_interrupt_handler(INT_PS2_MOUSE, handle_mouse_interrupt, NULL, 0); + status = install_io_interrupt_handler(INT_PS2_MOUSE, + handle_mouse_interrupt, NULL, 0); + if (status < B_OK) { + TRACE(("mouse_open(): cannot install interrupt handler\n")); + goto err4; + } + + release_sem(gDeviceOpenSemaphore); + + TRACE(("mouse_open(): mouse succesfully enabled\n")); + return B_OK; + +err4: + delete_sem(sMouseSem); +err3: + delete_packet_buffer(sMouseBuffer); +err2: + ps2_common_uninitialize(); +err1: + atomic_and(&sOpenMask, 0); + release_sem(gDeviceOpenSemaphore); + + return status; } @@ -352,13 +409,15 @@ status_t mouse_close(void *cookie) { TRACE(("mouse_close()\n")); - ps2_enable_mouse(false); + + set_mouse_enabled(false); delete_packet_buffer(sMouseBuffer); delete_sem(sMouseSem); remove_io_interrupt_handler(INT_PS2_MOUSE, handle_mouse_interrupt, NULL); + ps2_common_uninitialize(); atomic_and(&sOpenMask, 0); return B_OK;