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

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;