X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Finput%2Fserio%2Flibps2.c;h=b3e84d3bb7f7cc22f38883d6ee65966c2a345b36;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=c978657068c55b6ade7644b65e9089ae272810bc;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index c97865706..b3e84d3bb 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -27,14 +27,6 @@ MODULE_AUTHOR("Dmitry Torokhov "); MODULE_DESCRIPTION("PS/2 driver library"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(ps2_init); -EXPORT_SYMBOL(ps2_sendbyte); -EXPORT_SYMBOL(ps2_command); -EXPORT_SYMBOL(ps2_schedule_command); -EXPORT_SYMBOL(ps2_handle_ack); -EXPORT_SYMBOL(ps2_handle_response); -EXPORT_SYMBOL(ps2_cmd_aborted); - /* Work structure to schedule execution of a command */ struct ps2work { struct work_struct work; @@ -45,11 +37,11 @@ struct ps2work { /* - * ps2_sendbyte() sends a byte to the mouse, and waits for acknowledge. - * It doesn't handle retransmission, though it could - because when there would - * be need for retransmissions, the mouse has to be replaced anyway. + * ps2_sendbyte() sends a byte to the device and waits for acknowledge. + * It doesn't handle retransmission, though it could - because if there + * is a need for retransmissions device has to be replaced anyway. * - * ps2_sendbyte() can only be called from a process context + * ps2_sendbyte() can only be called from a process context. */ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) @@ -70,6 +62,107 @@ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) return -ps2dev->nak; } +EXPORT_SYMBOL(ps2_sendbyte); + +/* + * ps2_drain() waits for device to transmit requested number of bytes + * and discards them. + */ + +void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) +{ + if (maxbytes > sizeof(ps2dev->cmdbuf)) { + WARN_ON(1); + maxbytes = sizeof(ps2dev->cmdbuf); + } + + mutex_lock(&ps2dev->cmd_mutex); + + serio_pause_rx(ps2dev->serio); + ps2dev->flags = PS2_FLAG_CMD; + ps2dev->cmdcnt = maxbytes; + serio_continue_rx(ps2dev->serio); + + wait_event_timeout(ps2dev->wait, + !(ps2dev->flags & PS2_FLAG_CMD), + msecs_to_jiffies(timeout)); + mutex_unlock(&ps2dev->cmd_mutex); +} +EXPORT_SYMBOL(ps2_drain); + +/* + * ps2_is_keyboard_id() checks received ID byte against the list of + * known keyboard IDs. + */ + +int ps2_is_keyboard_id(char id_byte) +{ + const static char keyboard_ids[] = { + 0xab, /* Regular keyboards */ + 0xac, /* NCD Sun keyboard */ + 0x2b, /* Trust keyboard, translated */ + 0x5d, /* Trust keyboard */ + 0x60, /* NMB SGI keyboard, translated */ + 0x47, /* NMB SGI keyboard */ + }; + + return memchr(keyboard_ids, id_byte, sizeof(keyboard_ids)) != NULL; +} +EXPORT_SYMBOL(ps2_is_keyboard_id); + +/* + * ps2_adjust_timeout() is called after receiving 1st byte of command + * response and tries to reduce remaining timeout to speed up command + * completion. + */ + +static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) +{ + switch (command) { + case PS2_CMD_RESET_BAT: + /* + * Device has sent the first response byte after + * reset command, reset is thus done, so we can + * shorten the timeout. + * The next byte will come soon (keyboard) or not + * at all (mouse). + */ + if (timeout > msecs_to_jiffies(100)) + timeout = msecs_to_jiffies(100); + break; + + case PS2_CMD_GETID: + /* + * Microsoft Natural Elite keyboard responds to + * the GET ID command as it were a mouse, with + * a single byte. Fail the command so atkbd will + * use alternative probe to detect it. + */ + if (ps2dev->cmdbuf[1] == 0xaa) { + serio_pause_rx(ps2dev->serio); + ps2dev->flags = 0; + serio_continue_rx(ps2dev->serio); + timeout = 0; + } + + /* + * If device behind the port is not a keyboard there + * won't be 2nd byte of ID response. + */ + if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) { + serio_pause_rx(ps2dev->serio); + ps2dev->flags = ps2dev->cmdcnt = 0; + serio_continue_rx(ps2dev->serio); + timeout = 0; + } + break; + + default: + break; + } + + return timeout; +} /* * ps2_command() sends a command and its parameters to the mouse, @@ -86,7 +179,17 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) int rc = -1; int i; - down(&ps2dev->cmd_sem); + if (receive > sizeof(ps2dev->cmdbuf)) { + WARN_ON(1); + return -1; + } + + if (send && !param) { + WARN_ON(1); + return -1; + } + + mutex_lock(&ps2dev->cmd_mutex); serio_pause_rx(ps2dev->serio); ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; @@ -101,10 +204,9 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) * ACKing the reset command, and so it can take a long * time before the ACK arrrives. */ - if (command & 0xff) - if (ps2_sendbyte(ps2dev, command & 0xff, - command == PS2_CMD_RESET_BAT ? 1000 : 200)) - goto out; + if (ps2_sendbyte(ps2dev, command & 0xff, + command == PS2_CMD_RESET_BAT ? 1000 : 200)) + goto out; for (i = 0; i < send; i++) if (ps2_sendbyte(ps2dev, param[i], 200)) @@ -120,33 +222,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) if (ps2dev->cmdcnt && timeout > 0) { - if (command == PS2_CMD_RESET_BAT && timeout > msecs_to_jiffies(100)) { - /* - * Device has sent the first response byte - * after a reset command, reset is thus done, - * shorten the timeout. The next byte will come - * soon (keyboard) or not at all (mouse). - */ - timeout = msecs_to_jiffies(100); - } - - if (command == PS2_CMD_GETID && - ps2dev->cmdbuf[receive - 1] != 0xab && /* Regular keyboards */ - ps2dev->cmdbuf[receive - 1] != 0xac && /* NCD Sun keyboard */ - ps2dev->cmdbuf[receive - 1] != 0x2b && /* Trust keyboard, translated */ - ps2dev->cmdbuf[receive - 1] != 0x5d && /* Trust keyboard */ - ps2dev->cmdbuf[receive - 1] != 0x60 && /* NMB SGI keyboard, translated */ - ps2dev->cmdbuf[receive - 1] != 0x47) { /* NMB SGI keyboard */ - /* - * Device behind the port is not a keyboard - * so we don't need to wait for the 2nd byte - * of ID response. - */ - serio_pause_rx(ps2dev->serio); - ps2dev->flags = ps2dev->cmdcnt = 0; - serio_continue_rx(ps2dev->serio); - } - + timeout = ps2_adjust_timeout(ps2dev, command, timeout); wait_event_timeout(ps2dev->wait, !(ps2dev->flags & PS2_FLAG_CMD), timeout); } @@ -160,23 +236,24 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) rc = 0; -out: + out: serio_pause_rx(ps2dev->serio); ps2dev->flags = 0; serio_continue_rx(ps2dev->serio); - up(&ps2dev->cmd_sem); + mutex_unlock(&ps2dev->cmd_mutex); return rc; } +EXPORT_SYMBOL(ps2_command); /* * ps2_execute_scheduled_command() sends a command, previously scheduled by * ps2_schedule_command(), to a PS/2 device (keyboard, mouse, etc.) */ -static void ps2_execute_scheduled_command(void *data) +static void ps2_execute_scheduled_command(struct work_struct *work) { - struct ps2work *ps2work = data; + struct ps2work *ps2work = container_of(work, struct ps2work, work); ps2_command(ps2work->ps2dev, ps2work->param, ps2work->command); kfree(ps2work); @@ -201,7 +278,7 @@ int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int comman ps2work->ps2dev = ps2dev; ps2work->command = command; memcpy(ps2work->param, param, send); - INIT_WORK(&ps2work->work, ps2_execute_scheduled_command, ps2work); + INIT_WORK(&ps2work->work, ps2_execute_scheduled_command); if (!schedule_work(&ps2work->work)) { kfree(ps2work); @@ -210,6 +287,7 @@ int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int comman return 0; } +EXPORT_SYMBOL(ps2_schedule_command); /* * ps2_init() initializes ps2dev structure @@ -217,10 +295,12 @@ int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int comman void ps2_init(struct ps2dev *ps2dev, struct serio *serio) { - init_MUTEX(&ps2dev->cmd_sem); + mutex_init(&ps2dev->cmd_mutex); + lockdep_set_subclass(&ps2dev->cmd_mutex, serio->depth); init_waitqueue_head(&ps2dev->wait); ps2dev->serio = serio; } +EXPORT_SYMBOL(ps2_init); /* * ps2_handle_ack() is supposed to be used in interrupt handler @@ -266,6 +346,7 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) return 1; } +EXPORT_SYMBOL(ps2_handle_ack); /* * ps2_handle_response() is supposed to be used in interrupt handler @@ -291,6 +372,7 @@ int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data) return 1; } +EXPORT_SYMBOL(ps2_handle_response); void ps2_cmd_aborted(struct ps2dev *ps2dev) { @@ -302,4 +384,4 @@ void ps2_cmd_aborted(struct ps2dev *ps2dev) ps2dev->flags = 0; } - +EXPORT_SYMBOL(ps2_cmd_aborted);