aboutsummaryrefslogtreecommitdiff
path: root/drivers/input/serio/libps2.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/serio/libps2.c')
-rw-r--r--drivers/input/serio/libps2.c112
1 files changed, 50 insertions, 62 deletions
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
index b819239d74d..75516996db2 100644
--- a/drivers/input/serio/libps2.c
+++ b/drivers/input/serio/libps2.c
@@ -13,11 +13,11 @@
#include <linux/delay.h>
#include <linux/module.h>
-#include <linux/slab.h>
+#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/serio.h>
-#include <linux/init.h>
+#include <linux/i8042.h>
#include <linux/libps2.h>
#define DRIVER_DESC "PS/2 driver library"
@@ -26,15 +26,6 @@ MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
MODULE_DESCRIPTION("PS/2 driver library");
MODULE_LICENSE("GPL");
-/* Work structure to schedule execution of a command */
-struct ps2work {
- struct work_struct work;
- struct ps2dev *ps2dev;
- int command;
- unsigned char param[0];
-};
-
-
/*
* ps2_sendbyte() sends a byte to the device and waits for acknowledge.
* It doesn't handle retransmission, though it could - because if there
@@ -63,6 +54,24 @@ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout)
}
EXPORT_SYMBOL(ps2_sendbyte);
+void ps2_begin_command(struct ps2dev *ps2dev)
+{
+ mutex_lock(&ps2dev->cmd_mutex);
+
+ if (i8042_check_port_owner(ps2dev->serio))
+ i8042_lock_chip();
+}
+EXPORT_SYMBOL(ps2_begin_command);
+
+void ps2_end_command(struct ps2dev *ps2dev)
+{
+ if (i8042_check_port_owner(ps2dev->serio))
+ i8042_unlock_chip();
+
+ mutex_unlock(&ps2dev->cmd_mutex);
+}
+EXPORT_SYMBOL(ps2_end_command);
+
/*
* ps2_drain() waits for device to transmit requested number of bytes
* and discards them.
@@ -75,7 +84,7 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout)
maxbytes = sizeof(ps2dev->cmdbuf);
}
- mutex_lock(&ps2dev->cmd_mutex);
+ ps2_begin_command(ps2dev);
serio_pause_rx(ps2dev->serio);
ps2dev->flags = PS2_FLAG_CMD;
@@ -85,7 +94,8 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout)
wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_CMD),
msecs_to_jiffies(timeout));
- mutex_unlock(&ps2dev->cmd_mutex);
+
+ ps2_end_command(ps2dev);
}
EXPORT_SYMBOL(ps2_drain);
@@ -170,7 +180,7 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout)
* ps2_command() can only be called from a process context
*/
-int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
+int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
{
int timeout;
int send = (command >> 12) & 0xf;
@@ -188,8 +198,6 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
return -1;
}
- mutex_lock(&ps2dev->cmd_mutex);
-
serio_pause_rx(ps2dev->serio);
ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
ps2dev->cmdcnt = receive;
@@ -201,7 +209,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
/*
* Some devices (Synaptics) peform the reset before
* ACKing the reset command, and so it can take a long
- * time before the ACK arrrives.
+ * time before the ACK arrives.
*/
if (ps2_sendbyte(ps2dev, command & 0xff,
command == PS2_CMD_RESET_BAT ? 1000 : 200))
@@ -219,7 +227,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
timeout = wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_CMD1), timeout);
- if (ps2dev->cmdcnt && timeout > 0) {
+ if (ps2dev->cmdcnt && !(ps2dev->flags & PS2_FLAG_CMD1)) {
timeout = ps2_adjust_timeout(ps2dev, command, timeout);
wait_event_timeout(ps2dev->wait,
@@ -240,53 +248,21 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
ps2dev->flags = 0;
serio_continue_rx(ps2dev->serio);
- 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(struct work_struct *work)
-{
- struct ps2work *ps2work = container_of(work, struct ps2work, work);
-
- ps2_command(ps2work->ps2dev, ps2work->param, ps2work->command);
- kfree(ps2work);
-}
-
-/*
- * ps2_schedule_command() allows to schedule delayed execution of a PS/2
- * command and can be used to issue a command from an interrupt or softirq
- * context.
- */
+EXPORT_SYMBOL(__ps2_command);
-int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command)
+int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
{
- struct ps2work *ps2work;
- int send = (command >> 12) & 0xf;
- int receive = (command >> 8) & 0xf;
-
- if (!(ps2work = kmalloc(sizeof(struct ps2work) + max(send, receive), GFP_ATOMIC)))
- return -1;
+ int rc;
- memset(ps2work, 0, sizeof(struct ps2work));
- ps2work->ps2dev = ps2dev;
- ps2work->command = command;
- memcpy(ps2work->param, param, send);
- INIT_WORK(&ps2work->work, ps2_execute_scheduled_command);
+ ps2_begin_command(ps2dev);
+ rc = __ps2_command(ps2dev, param, command);
+ ps2_end_command(ps2dev);
- if (!schedule_work(&ps2work->work)) {
- kfree(ps2work);
- return -1;
- }
-
- return 0;
+ return rc;
}
-EXPORT_SYMBOL(ps2_schedule_command);
+EXPORT_SYMBOL(ps2_command);
/*
* ps2_init() initializes ps2dev structure
@@ -314,9 +290,17 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
break;
case PS2_RET_NAK:
- ps2dev->nak = 1;
+ ps2dev->flags |= PS2_FLAG_NAK;
+ ps2dev->nak = PS2_RET_NAK;
break;
+ case PS2_RET_ERR:
+ if (ps2dev->flags & PS2_FLAG_NAK) {
+ ps2dev->flags &= ~PS2_FLAG_NAK;
+ ps2dev->nak = PS2_RET_ERR;
+ break;
+ }
+
/*
* Workaround for mice which don't ACK the Get ID command.
* These are valid mouse IDs that we recognize.
@@ -334,8 +318,11 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
}
- if (!ps2dev->nak && ps2dev->cmdcnt)
- ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1;
+ if (!ps2dev->nak) {
+ ps2dev->flags &= ~PS2_FLAG_NAK;
+ if (ps2dev->cmdcnt)
+ ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1;
+ }
ps2dev->flags &= ~PS2_FLAG_ACK;
wake_up(&ps2dev->wait);
@@ -381,6 +368,7 @@ void ps2_cmd_aborted(struct ps2dev *ps2dev)
if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD))
wake_up(&ps2dev->wait);
- ps2dev->flags = 0;
+ /* reset all flags except last nack */
+ ps2dev->flags &= PS2_FLAG_NAK;
}
EXPORT_SYMBOL(ps2_cmd_aborted);