Many changes, it now does work okay on Haiku (tested with consoled only (both,

keyboard and mouse part), since for some reason, the app_server refuses to work
with the input_server for me now):
- keyboards are now actually detected, the keyboard device is only published if
  there actually is a keyboard
- keyboard_close() no longer calls put_module() for the ISA bus manager; it's
  sharing that resource with the mouse driver.
- rewrote the keyboard command handshake; now, the acknowledge byte is always
  considered, and it's now also safe to call keyboard commands with the interrupt
  handler installed (bytes read are now safely routed through the handler).
- keyboard_open() now also sets the PS/2 command byte to enable keyboard
  interrupts (this may actually have been the source of all evil - ie. keyboard
  not working after having touched the mouse)
- many commands now try again several times if they failed
- ps2_get_command_byte() is now safe to be called when the keyboard driver
  is up and running, too (it's now locally buffered)
- more and better error checks
- possibly more I forgot about...
- general cleanup.

Note, there is still some functionality missing from the keyboard driver:
- ctrl/alt/del handling
- key repeat (delay, time, non-repeating)

Repeat stuff might better be moved to the input_server add-on, anyway, for
more flexibility and better performance.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@12498 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2005-04-28 19:04:06 +00:00
parent b9cc06b57c
commit 80bd55147b
4 changed files with 685 additions and 284 deletions
+220 -101
View File
@@ -6,6 +6,7 @@
* PS/2 hid device driver * PS/2 hid device driver
* Authors (in chronological order): * Authors (in chronological order):
* Stefano Ceccherini ([email protected]) * Stefano Ceccherini ([email protected])
* Axel Dörfler, [email protected]
*/ */
@@ -19,10 +20,8 @@
#define DEVICE_KEYBOARD_NAME "input/keyboard/at/0" #define DEVICE_KEYBOARD_NAME "input/keyboard/at/0"
int32 api_version = B_CUR_DRIVER_API_VERSION; int32 api_version = B_CUR_DRIVER_API_VERSION;
isa_module_info *gIsa = NULL;
device_hooks static device_hooks sKeyboardDeviceHooks = {
keyboard_hooks = {
keyboard_open, keyboard_open,
keyboard_close, keyboard_close,
keyboard_freecookie, keyboard_freecookie,
@@ -35,9 +34,7 @@ keyboard_hooks = {
NULL NULL
}; };
static device_hooks sMouseDeviceHooks = {
device_hooks
mouse_hooks = {
mouse_open, mouse_open,
mouse_close, mouse_close,
mouse_freecookie, 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 /** 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 * 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 * to 0. Returns true if the control port is ready to be written, false
@@ -58,131 +128,159 @@ mouse_hooks = {
* port is still busy. * port is still busy.
*/ */
bool status_t
wait_write_ctrl(void) ps2_write_ctrl(uint8 data)
{ {
int8 read; if (wait_for_bits_cleared(PS2_STATUS_INPUT_BUFFER_FULL | PS2_STATUS_OUTPUT_BUFFER_FULL) != B_OK)
int32 tries = 100; return B_ERROR;
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);
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 /** Wait until the data port is ready to be written, and then writes \a data to it.
* the "Input buffer full" bit will be set to 0.
*/ */
bool status_t
wait_write_data(void) ps2_write_data(uint8 data)
{ {
int8 read; if (wait_for_bits_cleared(PS2_STATUS_INPUT_BUFFER_FULL) != B_OK)
int32 tries = 100; return B_ERROR;
TRACE(("wait_write_data()\n"));
do {
read = gIsa->read_io_8(PS2_PORT_CTRL);
spin(100);
} while ((read & PS2_IBUF_FULL) && tries-- > 0);
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 /** Wait until the data port can be read from, and then transfers the byte read
* "Output buffer full" bit will be set to 1. * to /a data.
*/ */
bool status_t
wait_read_data(void) ps2_read_data(uint8 *data)
{ {
int8 read; if (wait_for_bits_set(PS2_STATUS_OUTPUT_BUFFER_FULL) != B_OK)
int32 tries = 100; return B_ERROR;
TRACE(("wait_read_data()\n"));
do {
read = gIsa->read_io_8(PS2_PORT_CTRL);
spin(100);
} while (!(read & PS2_OBUF_FULL) && tries-- > 0);
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 uint8
get_command_byte(void) ps2_get_command_byte(void)
{ {
int8 read = 0; TRACE(("ps2_get_command_byte(): command byte = %x\n", sCommandByte));
TRACE(("set_command_byte()\n")); return sCommandByte;
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;
} }
/** Set the ps2 command byte. /** Set the ps2 command byte.
* Parameters:
* unsigned char, byte to write
*/ */
void status_t
set_command_byte(unsigned char cmd) ps2_set_command_byte(uint8 command)
{ {
TRACE(("set_command_byte()\n")); TRACE(("set_command_byte(command = %x)\n", command));
if (wait_write_ctrl()) {
gIsa->write_io_8(PS2_PORT_CTRL, PS2_CTRL_WRITE_CMD); if (ps2_write_ctrl(PS2_CTRL_WRITE_CMD) == B_OK
if (wait_write_data()) && ps2_write_data(command) == B_OK) {
gIsa->write_io_8(PS2_PORT_DATA, cmd); sCommandByte = command;
return B_OK;
} }
return B_ERROR;
} }
/** Reads a single byte from the data port. bool
* Return value: ps2_handle_result(uint8 data)
* unsigned char, byte read
*/
uint8
read_data_byte(void)
{ {
TRACE(("read_data_byte()\n")); int32 bytesLeft;
if (wait_read_data()) {
TRACE(("read_data_byte(): ok\n")); if (sResultBuffer == NULL
return gIsa->read_io_8(PS2_PORT_DATA); || (bytesLeft = atomic_add(&sResultBytes, -1)) <= 0)
} return false;
TRACE(("read_data_byte(): timeout\n")); *(sResultBuffer++) = data;
if (bytesLeft == 1)
return PS2_ERROR; release_sem_etc(sResultSemaphore, 1, B_DO_NOT_RESCHEDULE);
return true;
} }
/** Writes a byte to the mouse device. Uses the control port to indicate void
* that the byte is sent to the auxiliary device (mouse), instead of the ps2_claim_result(uint8 *buffer, size_t bytes)
* keyboard. {
* Parameters: acquire_sem(sResultOwnerSemaphore);
* unsigned char, byte to write
*/ sResultBuffer = buffer;
sResultBytes = bytes;
}
void void
write_aux_byte(unsigned char cmd) ps2_unclaim_result(void)
{ {
TRACE(("write_aux_byte()\n")); sResultBytes = 0;
if (wait_write_ctrl()) { sResultBuffer = NULL;
gIsa->write_io_8(PS2_PORT_CTRL, PS2_CTRL_WRITE_AUX);
if (wait_write_data()) release_sem(sResultOwnerSemaphore);
gIsa->write_io_8(PS2_PORT_DATA, cmd); }
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 ** const char **
publish_devices(void) 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) find_device(const char *name)
{ {
if (!strcmp(name, DEVICE_MOUSE_NAME)) if (!strcmp(name, DEVICE_MOUSE_NAME))
return &mouse_hooks; return &sMouseDeviceHooks;
else if (!strcmp(name, DEVICE_KEYBOARD_NAME)) else if (!strcmp(name, DEVICE_KEYBOARD_NAME))
return &keyboard_hooks; return &sKeyboardDeviceHooks;
return NULL; return NULL;
} }
@@ -235,12 +335,31 @@ init_driver(void)
return status; 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 void
uninit_driver(void) uninit_driver(void)
{ {
delete_sem(gDeviceOpenSemaphore);
put_module(B_ISA_MODULE_NAME); put_module(B_ISA_MODULE_NAME);
} }
@@ -19,59 +19,67 @@
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
// definitions // Interface definitions for the Intel 8042, 8741, or 8742 (PS/2)
// I/O addresses // I/O addresses
#define PS2_PORT_DATA 0x60 #define PS2_PORT_DATA 0x60
#define PS2_PORT_CTRL 0x64 #define PS2_PORT_CTRL 0x64
// data port bits // data port bits
#define PS2_OBUF_FULL 0x01 #define PS2_STATUS_OUTPUT_BUFFER_FULL 0x01
#define PS2_IBUF_FULL 0x02 #define PS2_STATUS_INPUT_BUFFER_FULL 0x02
#define PS2_MOUSE_OBUF_FULL 0x20 #define PS2_STATUS_MOUSE_DATA 0x20
#define PS2_TIMEOUT 0x40 #define PS2_STATUS_TIMEOUT 0x40
// control words // control words
#define PS2_CTRL_READ_CMD 0x20 #define PS2_CTRL_READ_CMD 0x20
#define PS2_CTRL_WRITE_CMD 0x60 #define PS2_CTRL_WRITE_CMD 0x60
#define PS2_CTRL_WRITE_AUX 0xD4 #define PS2_CTRL_WRITE_AUX 0xd4
#define PS2_CTRL_MOUSE_DISABLE 0xA7 #define PS2_CTRL_MOUSE_DISABLE 0xa7
#define PS2_CTRL_MOUSE_ENABLE 0xA8 #define PS2_CTRL_MOUSE_ENABLE 0xa8
#define PS2_CTRL_MOUSE_TEST 0xA9 #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 // command bytes
#define PS2_CMD_DEV_INIT 0x43 #define PS2_CMD_DEV_INIT 0x43
// command bits // command bits
#define PS2_BITS_AUX_INTERRUPT 0x02 #define PS2_BITS_KEYBOARD_INTERRUPT 0x01
#define PS2_BITS_MOUSE_DISABLED 0x20 #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 // data words
#define PS2_CMD_TEST_PASSED 0xAA #define PS2_CMD_TEST_PASSED 0xaa
#define PS2_CMD_GET_DEVICE_ID 0xF2 #define PS2_CMD_GET_DEVICE_ID 0xf2
#define PS2_CMD_SET_SAMPLE_RATE 0xF3 #define PS2_CMD_SET_SAMPLE_RATE 0xf3
#define PS2_CMD_ENABLE_MOUSE 0xF4 #define PS2_CMD_ENABLE_MOUSE 0xf4
#define PS2_CMD_DISABLE_MOUSE 0xF5 #define PS2_CMD_DISABLE_MOUSE 0xf5
#define PS2_CMD_RESET_MOUSE 0xFF #define PS2_CMD_RESET_MOUSE 0xff
// reply codes // reply codes
#define PS2_RES_TEST_PASSED 0x55 #define PS2_REPLY_TEST_PASSED 0x55
#define PS2_RES_ACK 0xFA #define PS2_REPLY_ACK 0xfa
#define PS2_RES_RESEND 0xFE #define PS2_REPLY_RESEND 0xfe
#define PS2_ERROR 0xFC #define PS2_REPLY_ERROR 0xfc
// interrupts // interrupts
#define INT_PS2_MOUSE 0x0C #define INT_PS2_MOUSE 0x0c
#define INT_PS2_KEYBOARD 0x01 #define INT_PS2_KEYBOARD 0x01
// mouse device IDs // mouse device IDs
#define PS2_DEV_ID_STANDARD 0 #define PS2_DEV_ID_STANDARD 0
#define PS2_DEV_ID_INTELLIMOUSE 3 #define PS2_DEV_ID_INTELLIMOUSE 3
// packet sizes // packet sizes
#define PS2_PACKET_STANDARD 3 #define PS2_PACKET_STANDARD 3
#define PS2_PACKET_INTELLIMOUSE 4 #define PS2_PACKET_INTELLIMOUSE 4
#define PS2_MAX_PACKET_SIZE 4 // Should be equal to the biggest packet size #define PS2_MAX_PACKET_SIZE 4
// Should be equal to the biggest packet size
// debug defines // debug defines
#ifdef DEBUG #ifdef DEBUG
@@ -82,16 +90,28 @@
// global variables // global variables
extern isa_module_info *gIsa; extern isa_module_info *gIsa;
extern sem_id gDeviceOpenSemaphore;
// prototypes // prototypes from common.c
bool wait_write_ctrl(void); extern status_t ps2_write_ctrl(uint8 data);
bool wait_write_data(void); extern status_t ps2_write_data(uint8 data);
bool wait_read_data(void); extern status_t ps2_read_data(uint8 *data);
void write_aux_byte(unsigned char cmd);
uint8 read_data_byte(void);
int8 get_command_byte(void); extern status_t ps2_write_aux_byte(uint8 data);
void set_command_byte(unsigned char cmd); 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_open(const char *name, uint32 flags, void **cookie);
extern status_t keyboard_close(void *cookie); extern status_t keyboard_close(void *cookie);
@@ -27,20 +27,113 @@ enum {
} leds_status; } leds_status;
enum { enum {
PS2_SET_LEDS = 0xed PS2_DATA_SET_LEDS = 0xed,
} commands; PS2_ENABLE_KEYBOARD = 0xf4,
EXTENDED_KEY = 0xe0,
};
static int32 sKeyboardOpenMask; static int32 sKeyboardOpenMask;
static sem_id sKeyboardSem; static sem_id sKeyboardSem;
static struct packet_buffer *sKeyBuffer; static struct packet_buffer *sKeyBuffer;
static bool sIsExtended = false; 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) set_leds(led_info *ledInfo)
{ {
int leds = 0; uint8 leds = 0;
TRACE(("ps2_hid: set keyboard LEDs\n"));
if (ledInfo->scroll_lock) if (ledInfo->scroll_lock)
leds |= LED_SCROLL; leds |= LED_SCROLL;
if (ledInfo->num_lock) if (ledInfo->num_lock)
@@ -48,59 +141,69 @@ set_leds(led_info *ledInfo)
if (ledInfo->caps_lock) if (ledInfo->caps_lock)
leds |= LED_CAPS; leds |= LED_CAPS;
wait_write_data(); return keyboard_command(PS2_DATA_SET_LEDS, &leds, sizeof(leds));
gIsa->write_io_8(PS2_PORT_DATA, PS2_SET_LEDS);
wait_write_data();
gIsa->write_io_8(PS2_PORT_DATA, leds);
} }
static int32 static int32
handle_keyboard_interrupt(void *data) handle_keyboard_interrupt(void *data)
{ {
// TODO: Handle braindead "pause" key special case uint8 read = gIsa->read_io_8(PS2_PORT_CTRL);
at_kbd_io keyInfo;
unsigned char read, scancode;
read = gIsa->read_io_8(PS2_PORT_DATA);
TRACE(("handle_keyboard_interrupt: read = 0x%x\n", read)); TRACE(("handle_keyboard_interrupt: read = 0x%x\n", read));
if (read == 0xE0) { if (read & PS2_STATUS_OUTPUT_BUFFER_FULL) {
sIsExtended = true; at_kbd_io keyInfo;
TRACE(("Extended key\n")); uint8 scancode;
return B_HANDLED_INTERRUPT;
}
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: Handle braindead "pause" key special case
// ToDo: remove me later :-)
if (scancode == 88)
panic("keyboard requested halt.\n");
if (scancode & 0x80) { if (read == EXTENDED_KEY) {
keyInfo.is_keydown = false; sIsExtended = true;
scancode -= 0x80; TRACE(("Extended key\n"));
} else return B_HANDLED_INTERRUPT;
keyInfo.is_keydown = true; }
if (sIsExtended) { scancode = read;
scancode |= 0x80;
sIsExtended = false; 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; return B_INVOKE_SCHEDULER;
} }
@@ -109,8 +212,11 @@ static status_t
read_keyboard_packet(at_kbd_io *userBuffer) read_keyboard_packet(at_kbd_io *userBuffer)
{ {
at_kbd_io packet; 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) if (status < B_OK)
return status; 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 - // #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) keyboard_open(const char *name, uint32 flags, void **_cookie)
{ {
uint8 commandByte;
status_t status;
TRACE(("keyboard open()\n")); TRACE(("keyboard open()\n"));
if (atomic_or(&sKeyboardOpenMask, 1) != 0) if (atomic_or(&sKeyboardOpenMask, 1) != 0)
return B_BUSY; return B_BUSY;
acquire_sem(gDeviceOpenSemaphore);
status = ps2_common_initialize();
if (status != B_OK)
goto err1;
sKeyboardSem = create_sem(0, "keyboard_sem"); sKeyboardSem = create_sem(0, "keyboard_sem");
if (sKeyboardSem < 0) { if (sKeyboardSem < 0) {
atomic_and(&sKeyboardOpenMask, 0); status = sKeyboardSem;
return sKeyboardSem; goto err2;
} }
sKeyBuffer = create_packet_buffer(KEY_BUFFER_SIZE * sizeof(at_kbd_io)); sKeyBuffer = create_packet_buffer(KEY_BUFFER_SIZE * sizeof(at_kbd_io));
if (sKeyBuffer == NULL) { if (sKeyBuffer == NULL) {
panic("could not create keyboard packet buffer!\n"); status = B_NO_MEMORY;
atomic_and(&sKeyboardOpenMask, 0); goto err3;
return B_NO_MEMORY;
} }
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; *_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) keyboard_close(void *cookie)
{ {
TRACE(("keyboard_close()\n"));
remove_io_interrupt_handler(INT_PS2_KEYBOARD, &handle_keyboard_interrupt, NULL); remove_io_interrupt_handler(INT_PS2_KEYBOARD, &handle_keyboard_interrupt, NULL);
sInterruptHandlerInstalled = false;
delete_packet_buffer(sKeyBuffer); delete_packet_buffer(sKeyBuffer);
delete_sem(sKeyboardSem); delete_sem(sKeyboardSem);
ps2_common_uninitialize();
atomic_and(&sKeyboardOpenMask, 0); atomic_and(&sKeyboardOpenMask, 0);
return B_OK; return B_OK;
} }
status_t status_t
keyboard_freecookie(void *cookie) keyboard_freecookie(void *cookie)
{ {
return B_OK; return B_OK;
} }
status_t status_t
keyboard_read(void *cookie, off_t pos, void *buffer, size_t *_length) keyboard_read(void *cookie, off_t pos, void *buffer, size_t *_length)
{ {
TRACE(("keyboard read()\n")); 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) keyboard_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
{ {
TRACE(("keyboard write()\n")); 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) keyboard_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
{ {
TRACE(("keyboard ioctl()\n")); TRACE(("keyboard ioctl()\n"));
@@ -210,17 +414,16 @@ keyboard_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
return B_BAD_ADDRESS; return B_BAD_ADDRESS;
TRACE(("KB_SET_LEDS\n")); TRACE(("KB_SET_LEDS\n"));
set_leds(&info); return set_leds(&info);
return B_OK;
} }
case KB_SET_KEY_REPEATING: case KB_SET_KEY_REPEATING:
case KB_SET_KEY_NONREPEATING: 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; return B_OK;
default: default:
TRACE(("invalid ioctl 0x%x\n", op)); TRACE(("ps2_hid: invalid ioctl 0x%lx\n", op));
return EINVAL; return EINVAL;
} }
} }
+141 -82
View File
@@ -82,8 +82,24 @@ static int sButtonsState;
static int32 sPacketSize; static int32 sPacketSize;
///////////////////////////////////////////////////////////////////////// /** Writes a byte to the mouse device. Uses the control port to indicate
// mouse functions * 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 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. /** Set sampling rate of the ps2 port.
*/ */
static status_t static status_t
ps2_set_sample_rate(uint32 rate) ps2_set_sample_rate(uint32 rate)
{ {
status_t status = B_ERROR; int32 tries = 5;
write_aux_byte(PS2_CMD_SET_SAMPLE_RATE); while (--tries > 0) {
if (read_data_byte() == PS2_RES_ACK) { status_t status = ps2_write_aux_byte(PS2_CMD_SET_SAMPLE_RATE);
write_aux_byte(rate); if (status == B_OK)
if (read_data_byte() == PS2_RES_ACK) status = ps2_write_aux_byte(rate);
status = B_OK;
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_ydelta = (int)wheel_ydelta;
pos->wheel_xdelta = (int)wheel_xdelta; 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)); xDelta, yDelta, buttons, sClickCount, currentTime));
} }
} }
@@ -219,8 +213,25 @@ ps2_mouse_read(mouse_movement *userMovement)
} }
///////////////////////////////////////////////////////////////////////// /** Enables or disables mouse reporting for the PS/2 port.
// interrupt */
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 /** Interrupt handler for the mouse device. Called whenever the I/O
@@ -239,7 +250,8 @@ handle_mouse_interrupt(void* data)
return B_UNHANDLED_INTERRUPT; return B_UNHANDLED_INTERRUPT;
} }
read = read_data_byte(); read = gIsa->read_io_8(PS2_PORT_DATA);
TRACE(("mouse interrupt : byte %x\n", read)); TRACE(("mouse interrupt : byte %x\n", read));
while (packet_buffer_write(sMouseBuffer, &read, sizeof(read)) == 0) { while (packet_buffer_write(sMouseBuffer, &read, sizeof(read)) == 0) {
// we start dropping packets when the buffer is full // 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 - // #pragma mark -
// Device functions // Device functions
@@ -272,48 +330,27 @@ mouse_open(const char *name, uint32 flags, void **_cookie)
{ {
status_t status; status_t status;
int8 commandByte; int8 commandByte;
int8 deviceId = -1;
TRACE(("mouse_open()\n")); TRACE(("mouse_open()\n"));
if (atomic_or(&sOpenMask, 1) != 0) if (atomic_or(&sOpenMask, 1) != 0)
return B_BUSY; return B_BUSY;
// get device id acquire_sem(gDeviceOpenSemaphore);
write_aux_byte(PS2_CMD_GET_DEVICE_ID);
if (read_data_byte() == PS2_RES_ACK)
deviceId = read_data_byte();
TRACE(("init_driver: device id: %2x\n", deviceId)); status = probe_mouse();
if (deviceId == 0) { if (status != B_OK)
// try to switch to intellimouse mode goto err1;
ps2_set_sample_rate(200);
ps2_set_sample_rate(100);
ps2_set_sample_rate(80);
}
// get device id, again status = ps2_common_initialize();
write_aux_byte(PS2_CMD_GET_DEVICE_ID); if (status != B_OK)
if (read_data_byte() == PS2_RES_ACK) goto err1;
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
}
sMouseBuffer = create_packet_buffer(MOUSE_HISTORY_SIZE * sPacketSize); sMouseBuffer = create_packet_buffer(MOUSE_HISTORY_SIZE * sPacketSize);
if (sMouseBuffer == NULL) { if (sMouseBuffer == NULL) {
TRACE(("can't allocate mouse actions buffer\n")); TRACE(("can't allocate mouse actions buffer\n"));
put_module(B_ISA_MODULE_NAME); status = B_NO_MEMORY;
return B_ERROR; goto err2;
} }
// create the mouse semaphore, used for synchronization between // 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"); sMouseSem = create_sem(0, "ps2_mouse_sem");
if (sMouseSem < 0) { if (sMouseSem < 0) {
TRACE(("failed creating PS/2 mouse semaphore!\n")); TRACE(("failed creating PS/2 mouse semaphore!\n"));
delete_packet_buffer(sMouseBuffer); status = sMouseSem;
put_module(B_ISA_MODULE_NAME); goto err3;
return sMouseSem;
} }
set_sem_owner(sMouseSem, B_SYSTEM_TEAM);
*_cookie = NULL; *_cookie = NULL;
commandByte = get_command_byte(); commandByte = ps2_get_command_byte() | PS2_BITS_AUX_INTERRUPT;
commandByte |= PS2_BITS_AUX_INTERRUPT;
commandByte &= ~PS2_BITS_MOUSE_DISABLED; commandByte &= ~PS2_BITS_MOUSE_DISABLED;
set_command_byte(commandByte);
status = ps2_enable_mouse(true); status = ps2_set_command_byte(commandByte);
if (status < B_OK) { if (status < B_OK) {
TRACE(("mouse_open(): cannot enable PS/2 mouse\n")); TRACE(("mouse_open(): sending command byte failed\n"));
return B_ERROR; 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 status = install_io_interrupt_handler(INT_PS2_MOUSE,
return install_io_interrupt_handler(INT_PS2_MOUSE, handle_mouse_interrupt, NULL, 0); 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) mouse_close(void *cookie)
{ {
TRACE(("mouse_close()\n")); TRACE(("mouse_close()\n"));
ps2_enable_mouse(false);
set_mouse_enabled(false);
delete_packet_buffer(sMouseBuffer); delete_packet_buffer(sMouseBuffer);
delete_sem(sMouseSem); delete_sem(sMouseSem);
remove_io_interrupt_handler(INT_PS2_MOUSE, handle_mouse_interrupt, NULL); remove_io_interrupt_handler(INT_PS2_MOUSE, handle_mouse_interrupt, NULL);
ps2_common_uninitialize();
atomic_and(&sOpenMask, 0); atomic_and(&sOpenMask, 0);
return B_OK; return B_OK;