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:
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user