Commit c5ec0513 authored by Vojtech Pavlik's avatar Vojtech Pavlik

Merge bk://dtor.bkbits.net/input into suse.cz:/data/bk/input

parents 8b2910fd 419bd06b
......@@ -15,6 +15,9 @@ obj-$(CONFIG_PNP) += pnp/
# char/ comes before serial/ etc so that the VT console is the boot-time
# default.
obj-y += char/
# we also need input/serio early so serio bus is initialized by the time
# serial drivers start registering their serio ports
obj-$(CONFIG_SERIO) += input/serio/
obj-y += serial/
obj-$(CONFIG_PARPORT) += parport/
obj-y += base/ block/ misc/ net/ media/
......@@ -37,7 +40,6 @@ obj-$(CONFIG_PARIDE) += block/paride/
obj-$(CONFIG_TC) += tc/
obj-$(CONFIG_USB) += usb/
obj-$(CONFIG_USB_GADGET) += usb/gadget/
obj-$(CONFIG_SERIO) += input/serio/
obj-$(CONFIG_INPUT) += input/
obj-$(CONFIG_GAMEPORT) += input/gameport/
obj-$(CONFIG_I2O) += message/
......
......@@ -232,8 +232,10 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo
&& list->head == list->tail && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
retval = wait_event_interruptible(list->joydev->wait, list->joydev->exist
&& (list->startup < joydev->nabs + joydev->nkey || list->head != list->tail));
retval = wait_event_interruptible(list->joydev->wait,
!list->joydev->exist ||
list->startup < joydev->nabs + joydev->nkey ||
list->head != list->tail);
if (retval)
return retval;
......
......@@ -173,8 +173,7 @@ static unsigned char atkbd_scroll_keys[5][2] = {
#define ATKBD_FLAG_ACK 0 /* Waiting for ACK/NAK */
#define ATKBD_FLAG_CMD 1 /* Waiting for command to finish */
#define ATKBD_FLAG_CMD1 2 /* First byte of command response */
#define ATKBD_FLAG_ID 3 /* First byte is not keyboard ID */
#define ATKBD_FLAG_ENABLED 4 /* Waining for init to finish */
#define ATKBD_FLAG_ENABLED 3 /* Waining for init to finish */
/*
* The atkbd control structure
......@@ -210,10 +209,25 @@ struct atkbd {
unsigned int last;
unsigned long time;
/* Ensures that only one command is executing at a time */
struct semaphore cmd_sem;
/* Used to signal completion from interrupt handler */
wait_queue_head_t wait;
/* Flags */
unsigned long flags;
};
/* Work structure to schedule execution of a command */
struct atkbd_work {
struct work_struct work;
struct atkbd *atkbd;
int command;
unsigned char param[0];
};
static void atkbd_report_key(struct input_dev *dev, struct pt_regs *regs, int code, int value)
{
input_regs(dev, regs);
......@@ -254,37 +268,38 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
atkbd->resend = 0;
#endif
if (test_bit(ATKBD_FLAG_ACK, &atkbd->flags))
if (test_bit(ATKBD_FLAG_ACK, &atkbd->flags)) {
switch (code) {
case ATKBD_RET_ACK:
atkbd->nak = 0;
if (atkbd->cmdcnt) {
set_bit(ATKBD_FLAG_CMD, &atkbd->flags);
set_bit(ATKBD_FLAG_CMD1, &atkbd->flags);
set_bit(ATKBD_FLAG_ID, &atkbd->flags);
}
clear_bit(ATKBD_FLAG_ACK, &atkbd->flags);
goto out;
wake_up_interruptible(&atkbd->wait);
break;
case ATKBD_RET_NAK:
atkbd->nak = 1;
clear_bit(ATKBD_FLAG_ACK, &atkbd->flags);
wake_up_interruptible(&atkbd->wait);
break;
}
goto out;
}
if (test_bit(ATKBD_FLAG_CMD, &atkbd->flags)) {
atkbd->cmdcnt--;
atkbd->cmdbuf[atkbd->cmdcnt] = code;
if (atkbd->cmdcnt)
atkbd->cmdbuf[--atkbd->cmdcnt] = code;
if (atkbd->cmdcnt == 1) {
if (code != 0xab && code != 0xac)
clear_bit(ATKBD_FLAG_ID, &atkbd->flags);
clear_bit(ATKBD_FLAG_CMD1, &atkbd->flags);
}
if (test_and_clear_bit(ATKBD_FLAG_CMD1, &atkbd->flags) && atkbd->cmdcnt)
wake_up_interruptible(&atkbd->wait);
if (!atkbd->cmdcnt)
if (!atkbd->cmdcnt) {
clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
wake_up_interruptible(&atkbd->wait);
}
goto out;
}
......@@ -417,87 +432,147 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
* acknowledge. It doesn't handle resends according to the keyboard
* protocol specs, because if these are needed, the keyboard needs
* replacement anyway, and they only make a mess in the protocol.
*
* atkbd_sendbyte() can only be called from a process context
*/
static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte)
{
int timeout = 200000; /* 200 msec */
#ifdef ATKBD_DEBUG
printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte);
#endif
atkbd->nak = 1;
set_bit(ATKBD_FLAG_ACK, &atkbd->flags);
clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
if (serio_write(atkbd->serio, byte))
return -1;
while (test_bit(ATKBD_FLAG_ACK, &atkbd->flags) && timeout--) udelay(1);
clear_bit(ATKBD_FLAG_ACK, &atkbd->flags);
if (serio_write(atkbd->serio, byte) == 0)
wait_event_interruptible_timeout(atkbd->wait,
!test_bit(ATKBD_FLAG_ACK, &atkbd->flags),
msecs_to_jiffies(200));
clear_bit(ATKBD_FLAG_ACK, &atkbd->flags);
return -atkbd->nak;
}
/*
* atkbd_command() sends a command, and its parameters to the keyboard,
* then waits for the response and puts it in the param array.
*
* atkbd_command() can only be called from a process context
*/
static int atkbd_command(struct atkbd *atkbd, unsigned char *param, int command)
{
int timeout = 500000; /* 500 msec */
int timeout;
int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf;
int rc = -1;
int i;
atkbd->cmdcnt = receive;
timeout = msecs_to_jiffies(command == ATKBD_CMD_RESET_BAT ? 4000 : 500);
if (command == ATKBD_CMD_RESET_BAT)
timeout = 4000000; /* 4 sec */
down(&atkbd->cmd_sem);
clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
if (receive && param)
for (i = 0; i < receive; i++)
atkbd->cmdbuf[(receive - 1) - i] = param[i];
atkbd->cmdcnt = receive;
if (command & 0xff)
if (atkbd_sendbyte(atkbd, command & 0xff))
return -1;
goto out;
for (i = 0; i < send; i++)
if (atkbd_sendbyte(atkbd, param[i]))
return -1;
while (test_bit(ATKBD_FLAG_CMD, &atkbd->flags) && timeout--) {
goto out;
if (!test_bit(ATKBD_FLAG_CMD1, &atkbd->flags)) {
timeout = wait_event_interruptible_timeout(atkbd->wait,
!test_bit(ATKBD_FLAG_CMD1, &atkbd->flags), timeout);
if (command == ATKBD_CMD_RESET_BAT && timeout > 100000)
timeout = 100000;
if (atkbd->cmdcnt && timeout > 0) {
if (command == ATKBD_CMD_RESET_BAT && jiffies_to_msecs(timeout) > 100)
timeout = msecs_to_jiffies(100);
if (command == ATKBD_CMD_GETID && !test_bit(ATKBD_FLAG_ID, &atkbd->flags)) {
if (command == ATKBD_CMD_GETID &&
atkbd->cmdbuf[receive - 1] != 0xab && atkbd->cmdbuf[receive - 1] != 0xac) {
/*
* Device behind the port is not a keyboard
* so we don't need to wait for the 2nd byte
* of ID response.
*/
clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
atkbd->cmdcnt = 0;
break;
}
}
udelay(1);
wait_event_interruptible_timeout(atkbd->wait,
!test_bit(ATKBD_FLAG_CMD, &atkbd->flags), timeout);
}
clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
if (param)
for (i = 0; i < receive; i++)
param[i] = atkbd->cmdbuf[(receive - 1) - i];
if (command == ATKBD_CMD_RESET_BAT && atkbd->cmdcnt == 1)
return 0;
if (atkbd->cmdcnt && (command != ATKBD_CMD_RESET_BAT || atkbd->cmdcnt != 1))
goto out;
if (atkbd->cmdcnt)
rc = 0;
out:
clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
clear_bit(ATKBD_FLAG_CMD1, &atkbd->flags);
up(&atkbd->cmd_sem);
return rc;
}
/*
* atkbd_execute_scheduled_command() sends a command, previously scheduled by
* atkbd_schedule_command(), to the keyboard.
*/
static void atkbd_execute_scheduled_command(void *data)
{
struct atkbd_work *atkbd_work = data;
atkbd_command(atkbd_work->atkbd, atkbd_work->param, atkbd_work->command);
kfree(atkbd_work);
}
/*
* atkbd_schedule_command() allows to schedule delayed execution of a keyboard
* command and can be used to issue a command from an interrupt or softirq
* context.
*/
static int atkbd_schedule_command(struct atkbd *atkbd, unsigned char *param, int command)
{
struct atkbd_work *atkbd_work;
int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf;
if (!test_bit(ATKBD_FLAG_ENABLED, &atkbd->flags))
return -1;
if (!(atkbd_work = kmalloc(sizeof(struct atkbd_work) + max(send, receive), GFP_ATOMIC)))
return -1;
memset(atkbd_work, 0, sizeof(struct atkbd_work));
atkbd_work->atkbd = atkbd;
atkbd_work->command = command;
memcpy(atkbd_work->param, param, send);
INIT_WORK(&atkbd_work->work, atkbd_execute_scheduled_command, atkbd_work);
if (!schedule_work(&atkbd_work->work)) {
kfree(atkbd_work);
return -1;
}
return 0;
}
/*
* Event callback from the input module. Events that change the state of
* the hardware are processed here.
......@@ -524,7 +599,7 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co
param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0)
| (test_bit(LED_NUML, dev->led) ? 2 : 0)
| (test_bit(LED_CAPSL, dev->led) ? 4 : 0);
atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS);
atkbd_schedule_command(atkbd, param, ATKBD_CMD_SETLEDS);
if (atkbd->extra) {
param[0] = 0;
......@@ -533,7 +608,7 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co
| (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0)
| (test_bit(LED_MISC, dev->led) ? 0x10 : 0)
| (test_bit(LED_MUTE, dev->led) ? 0x20 : 0);
atkbd_command(atkbd, param, ATKBD_CMD_EX_SETLEDS);
atkbd_schedule_command(atkbd, param, ATKBD_CMD_EX_SETLEDS);
}
return 0;
......@@ -549,7 +624,7 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co
dev->rep[REP_PERIOD] = period[i];
dev->rep[REP_DELAY] = delay[j];
param[0] = i | (j << 5);
atkbd_command(atkbd, param, ATKBD_CMD_SETREP);
atkbd_schedule_command(atkbd, param, ATKBD_CMD_SETREP);
return 0;
}
......@@ -721,7 +796,11 @@ static void atkbd_cleanup(struct serio *serio)
static void atkbd_disconnect(struct serio *serio)
{
struct atkbd *atkbd = serio->private;
clear_bit(ATKBD_FLAG_ENABLED, &atkbd->flags);
synchronize_kernel();
flush_scheduled_work();
input_unregister_device(&atkbd->dev);
serio_close(serio);
kfree(atkbd);
......@@ -743,6 +822,9 @@ static void atkbd_connect(struct serio *serio, struct serio_driver *drv)
return;
memset(atkbd, 0, sizeof(struct atkbd));
init_MUTEX(&atkbd->cmd_sem);
init_waitqueue_head(&atkbd->wait);
switch (serio->type & SERIO_TYPE) {
case SERIO_8042_XL:
......
......@@ -147,45 +147,64 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
psmouse->nak = 1;
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
wake_up_interruptible(&psmouse->wait);
goto out;
}
if (test_bit(PSMOUSE_FLAG_ACK, &psmouse->flags))
if (test_bit(PSMOUSE_FLAG_ACK, &psmouse->flags)) {
switch (data) {
case PSMOUSE_RET_ACK:
psmouse->nak = 0;
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
goto out;
break;
case PSMOUSE_RET_NAK:
psmouse->nak = 1;
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
goto out;
break;
/*
* Workaround for mice which don't ACK the Get ID command.
* These are valid mouse IDs that we recognize.
*/
case 0x00:
case 0x03:
case 0x04:
if (test_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags)) {
psmouse->nak = 0;
break;
}
/* Fall through */
default:
psmouse->nak = 0; /* Workaround for mice which don't ACK the Get ID command */
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
if (!test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags))
goto out;
}
if (!psmouse->nak && psmouse->cmdcnt) {
set_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
set_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags);
}
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
wake_up_interruptible(&psmouse->wait);
if (test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags)) {
if (data == PSMOUSE_RET_ACK || data == PSMOUSE_RET_NAK)
goto out;
}
psmouse->cmdcnt--;
psmouse->cmdbuf[psmouse->cmdcnt] = data;
if (test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags)) {
if (psmouse->cmdcnt)
psmouse->cmdbuf[--psmouse->cmdcnt] = data;
if (psmouse->cmdcnt == 1) {
if (data != 0xab && data != 0xac)
clear_bit(PSMOUSE_FLAG_ID, &psmouse->flags);
clear_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags);
}
if (test_and_clear_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags) && psmouse->cmdcnt)
wake_up_interruptible(&psmouse->wait);
if (!psmouse->cmdcnt)
if (!psmouse->cmdcnt) {
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
wake_up_interruptible(&psmouse->wait);
}
goto out;
}
if (psmouse->state == PSMOUSE_INITIALIZING)
goto out;
if (psmouse->state == PSMOUSE_ACTIVATED &&
psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n",
......@@ -251,90 +270,95 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
* psmouse_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.
*
* psmouse_sendbyte() can only be called from a process context
*/
static int psmouse_sendbyte(struct psmouse *psmouse, unsigned char byte)
{
int timeout = 200000; /* 200 msec */
psmouse->nak = 1;
set_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
if (serio_write(psmouse->serio, byte))
return -1;
while (test_bit(PSMOUSE_FLAG_ACK, &psmouse->flags) && timeout--) udelay(1);
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
if (serio_write(psmouse->serio, byte) == 0)
wait_event_interruptible_timeout(psmouse->wait,
!test_bit(PSMOUSE_FLAG_ACK, &psmouse->flags),
msecs_to_jiffies(200));
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
return -psmouse->nak;
}
/*
* psmouse_command() sends a command and its parameters to the mouse,
* then waits for the response and puts it in the param array.
*
* psmouse_command() can only be called from a process context
*/
int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command)
{
int timeout = 500000; /* 500 msec */
int timeout;
int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf;
int rc = -1;
int i;
psmouse->cmdcnt = receive;
timeout = msecs_to_jiffies(command == PSMOUSE_CMD_RESET_BAT ? 4000 : 500);
if (command == PSMOUSE_CMD_RESET_BAT)
timeout = 4000000; /* 4 sec */
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
if (command == PSMOUSE_CMD_GETID)
set_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags);
if (receive && param)
for (i = 0; i < receive; i++)
psmouse->cmdbuf[(receive - 1) - i] = param[i];
if (receive) {
set_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
set_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags);
set_bit(PSMOUSE_FLAG_ID, &psmouse->flags);
}
psmouse->cmdcnt = receive;
if (command & 0xff)
if (psmouse_sendbyte(psmouse, command & 0xff)) {
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
return -1;
}
if (psmouse_sendbyte(psmouse, command & 0xff))
goto out;
for (i = 0; i < send; i++)
if (psmouse_sendbyte(psmouse, param[i])) {
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
return -1;
}
while (test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags) && timeout--) {
if (psmouse_sendbyte(psmouse, param[i]))
goto out;
if (!test_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags)) {
timeout = wait_event_interruptible_timeout(psmouse->wait,
!test_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags), timeout);
if (command == PSMOUSE_CMD_RESET_BAT && timeout > 100000)
timeout = 100000;
if (psmouse->cmdcnt && timeout > 0) {
if (command == PSMOUSE_CMD_RESET_BAT && jiffies_to_msecs(timeout) > 100)
timeout = msecs_to_jiffies(100);
if (command == PSMOUSE_CMD_GETID && !test_bit(PSMOUSE_FLAG_ID, &psmouse->flags)) {
if (command == PSMOUSE_CMD_GETID &&
psmouse->cmdbuf[receive - 1] != 0xab && psmouse->cmdbuf[receive - 1] != 0xac) {
/*
* Device behind the port is not a keyboard
* so we don't need to wait for the 2nd byte
* of ID response.
*/
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
psmouse->cmdcnt = 0;
break;
}
}
udelay(1);
wait_event_interruptible_timeout(psmouse->wait,
!test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags), timeout);
}
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
if (param)
for (i = 0; i < receive; i++)
param[i] = psmouse->cmdbuf[(receive - 1) - i];
if (command == PSMOUSE_CMD_RESET_BAT && psmouse->cmdcnt == 1)
return 0;
if (psmouse->cmdcnt && (command != PSMOUSE_CMD_RESET_BAT || psmouse->cmdcnt != 1))
goto out;
if (psmouse->cmdcnt)
return -1;
rc = 0;
return 0;
out:
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
clear_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags);
clear_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags);
return rc;
}
/*
......@@ -625,6 +649,21 @@ static void psmouse_initialize(struct psmouse *psmouse)
psmouse_command(psmouse, param, PSMOUSE_CMD_SETSTREAM);
}
/*
* psmouse_set_state() sets new psmouse state and resets all flags and
* counters while holding serio lock so fighting with interrupt handler
* is not a concern.
*/
static void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
{
serio_pause_rx(psmouse->serio);
psmouse->state = new_state;
psmouse->pktcnt = psmouse->cmdcnt = psmouse->out_of_sync = 0;
psmouse->flags = 0;
serio_continue_rx(psmouse->serio);
}
/*
* psmouse_activate() enables the mouse so that we get motion reports from it.
*/
......@@ -634,9 +673,24 @@ static void psmouse_activate(struct psmouse *psmouse)
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE))
printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys);
psmouse->state = PSMOUSE_ACTIVATED;
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
}
/*
* psmouse_deactivate() puts the mouse into poll mode so that we don't get motion
* reports from it unless we explicitely request it.
*/
static void psmouse_deactivate(struct psmouse *psmouse)
{
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE))
printk(KERN_WARNING "psmouse.c: Failed to deactivate mouse on %s\n", psmouse->serio->phys);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
}
/*
* psmouse_cleanup() resets the mouse into power-on state.
*/
......@@ -657,7 +711,7 @@ static void psmouse_disconnect(struct serio *serio)
struct psmouse *psmouse, *parent;
psmouse = serio->private;
psmouse->state = PSMOUSE_CMD_MODE;
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
parent = serio->parent->private;
......@@ -668,7 +722,7 @@ static void psmouse_disconnect(struct serio *serio)
if (psmouse->disconnect)
psmouse->disconnect(psmouse);
psmouse->state = PSMOUSE_IGNORE;
psmouse_set_state(psmouse, PSMOUSE_IGNORE);
input_unregister_device(&psmouse->dev);
serio_close(serio);
......@@ -687,21 +741,28 @@ static void psmouse_connect(struct serio *serio, struct serio_driver *drv)
(serio->type & SERIO_TYPE) != SERIO_PS_PSTHRU)
return;
if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU)
/*
* If this is a pass-through port deactivate parent so the device
* connected to this port can be successfully identified
*/
if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
parent = serio->parent->private;
psmouse_deactivate(parent);
}
if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL)))
goto out;
memset(psmouse, 0, sizeof(struct psmouse));
init_waitqueue_head(&psmouse->wait);
init_input_dev(&psmouse->dev);
psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
psmouse->state = PSMOUSE_CMD_MODE;
psmouse->serio = serio;
psmouse->dev.private = psmouse;
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
serio->private = psmouse;
if (serio_open(serio, drv)) {
......@@ -741,25 +802,21 @@ static void psmouse_connect(struct serio *serio, struct serio_driver *drv)
printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
psmouse_initialize(psmouse);
if (parent && parent->pt_activate)
parent->pt_activate(parent);
/*
* OK, the device is ready, we just need to activate it (turn the
* stream mode on). But if mouse has a pass-through port we don't
* want to do it yet to not disturb child detection.
* The child will activate this port when it's ready.
*/
if (serio->child) {
/*
* Nothing to be done here, serio core will detect that
* the driver set serio->child and will register it for us.
*/
printk(KERN_INFO "serio: %s port at %s\n", serio->child->name, psmouse->phys);
} else
}
psmouse_activate(psmouse);
out:
......@@ -774,45 +831,46 @@ static int psmouse_reconnect(struct serio *serio)
struct psmouse *psmouse = serio->private;
struct psmouse *parent = NULL;
struct serio_driver *drv = serio->drv;
int rc = -1;
if (!drv || !psmouse) {
printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n");
return -1;
}
psmouse->state = PSMOUSE_CMD_MODE;
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
parent = serio->parent->private;
psmouse_deactivate(parent);
}
psmouse->pktcnt = psmouse->out_of_sync = 0;
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
if (psmouse->reconnect) {
if (psmouse->reconnect(psmouse))
return -1;
goto out;
} else if (psmouse_probe(psmouse) < 0 ||
psmouse->type != psmouse_extensions(psmouse, psmouse_max_proto, 0))
return -1;
goto out;
/* ok, the device type (and capabilities) match the old one,
* we can continue using it, complete intialization
*/
psmouse_initialize(psmouse);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU)
parent = serio->parent->private;
psmouse_initialize(psmouse);
if (parent && parent->pt_activate)
parent->pt_activate(parent);
if (!serio->child)
psmouse_activate(psmouse);
rc = 0;
out:
/* If this is a pass-through port the parent waits to be activated */
if (parent)
psmouse_activate(parent);
return 0;
return rc;
}
......
......@@ -9,6 +9,7 @@
#define PSMOUSE_CMD_GETID 0x02f2
#define PSMOUSE_CMD_SETRATE 0x10f3
#define PSMOUSE_CMD_ENABLE 0x00f4
#define PSMOUSE_CMD_DISABLE 0x00f5
#define PSMOUSE_CMD_RESET_DIS 0x00f6
#define PSMOUSE_CMD_RESET_BAT 0x02ff
......@@ -17,15 +18,17 @@
#define PSMOUSE_RET_ACK 0xfa
#define PSMOUSE_RET_NAK 0xfe
/* psmouse states */
#define PSMOUSE_CMD_MODE 0
#define PSMOUSE_ACTIVATED 1
#define PSMOUSE_IGNORE 2
#define PSMOUSE_FLAG_ACK 0 /* Waiting for ACK/NAK */
#define PSMOUSE_FLAG_CMD 1 /* Waiting for command to finish */
#define PSMOUSE_FLAG_CMD1 2 /* First byte of command response */
#define PSMOUSE_FLAG_ID 3 /* First byte is not keyboard ID */
#define PSMOUSE_FLAG_CMD1 2 /* Waiting for the first byte of command response */
#define PSMOUSE_FLAG_WAITID 3 /* Command execiting is GET ID */
enum psmouse_state {
PSMOUSE_IGNORE,
PSMOUSE_INITIALIZING,
PSMOUSE_CMD_MODE,
PSMOUSE_ACTIVATED,
};
/* psmouse protocol handler return codes */
typedef enum {
......@@ -48,13 +51,16 @@ struct psmouse {
unsigned char model;
unsigned long last;
unsigned long out_of_sync;
unsigned char state;
enum psmouse_state state;
unsigned char nak;
char error;
char devname[64];
char phys[32];
unsigned long flags;
/* Used to signal completion from interrupt handler */
wait_queue_head_t wait;
psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs);
int (*reconnect)(struct psmouse *psmouse);
void (*disconnect)(struct psmouse *psmouse);
......
......@@ -233,17 +233,14 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet
{
struct psmouse *child = ptport->private;
if (child) {
if (child->state == PSMOUSE_ACTIVATED) {
if (child && child->state == PSMOUSE_ACTIVATED) {
serio_interrupt(ptport, packet[1], 0, NULL);
serio_interrupt(ptport, packet[4], 0, NULL);
serio_interrupt(ptport, packet[5], 0, NULL);
if (child->type >= PSMOUSE_GENPS)
serio_interrupt(ptport, packet[2], 0, NULL);
} else if (child->state != PSMOUSE_IGNORE) {
} else
serio_interrupt(ptport, packet[1], 0, NULL);
}
}
}
static void synaptics_pt_activate(struct psmouse *psmouse)
......@@ -472,9 +469,10 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse, struct pt_r
if (unlikely(priv->pkt_type == SYN_NEWABS))
priv->pkt_type = synaptics_detect_pkt_type(psmouse);
if (psmouse->serio->child && psmouse->serio->child->drv && synaptics_is_pt_packet(psmouse->packet))
if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) {
if (psmouse->serio->child)
synaptics_pass_pt_packet(psmouse->serio->child, psmouse->packet);
else
} else
synaptics_process_packet(psmouse);
return PSMOUSE_FULL_PACKET;
......
......@@ -52,8 +52,10 @@ static unsigned tap_time = 200;
module_param(tap_time, uint, 0);
MODULE_PARM_DESC(tap_time, "Tap time for touchpads in absolute mode (msecs)");
struct mousedev_motion {
struct mousedev_hw_data {
int dx, dy, dz;
int x, y;
int abs_event;
unsigned long buttons;
};
......@@ -66,7 +68,7 @@ struct mousedev {
struct list_head list;
struct input_handle handle;
struct mousedev_motion packet;
struct mousedev_hw_data packet;
unsigned int pkt_count;
int old_x[4], old_y[4];
unsigned long touch;
......@@ -76,7 +78,12 @@ enum mousedev_emul {
MOUSEDEV_EMUL_PS2,
MOUSEDEV_EMUL_IMPS,
MOUSEDEV_EMUL_EXPS
} __attribute__ ((packed));
};
struct mousedev_motion {
int dx, dy, dz;
unsigned long buttons;
};
#define PACKET_QUEUE_LEN 16
struct mousedev_list {
......@@ -87,6 +94,7 @@ struct mousedev_list {
struct mousedev_motion packets[PACKET_QUEUE_LEN];
unsigned int head, tail;
spinlock_t packet_lock;
int pos_x, pos_y;
signed char ps2[6];
unsigned char ready, buffer, bufsiz;
......@@ -134,15 +142,19 @@ static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev,
case ABS_X:
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
if (size == 0) size = xres;
mousedev->packet.dx = (value * xres - mousedev->old_x[0]) / size;
mousedev->old_x[0] = mousedev->packet.dx * size;
if (value > dev->absmax[ABS_X]) value = dev->absmax[ABS_X];
if (value < dev->absmin[ABS_X]) value = dev->absmin[ABS_X];
mousedev->packet.x = ((value - dev->absmin[ABS_X]) * xres) / size;
mousedev->packet.abs_event = 1;
break;
case ABS_Y:
size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y];
if (size == 0) size = yres;
mousedev->packet.dy = (value * yres - mousedev->old_y[0]) / size;
mousedev->old_y[0] = mousedev->packet.dy * size;
if (value > dev->absmax[ABS_Y]) value = dev->absmax[ABS_Y];
if (value < dev->absmin[ABS_Y]) value = dev->absmin[ABS_Y];
mousedev->packet.y = yres - ((value - dev->absmin[ABS_Y]) * yres) / size;
mousedev->packet.abs_event = 1;
break;
}
}
......@@ -188,7 +200,7 @@ static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int
}
}
static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_motion *packet)
static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_hw_data *packet)
{
struct mousedev_list *list;
struct mousedev_motion *p;
......@@ -206,6 +218,18 @@ static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_m
}
}
if (packet->abs_event) {
p->dx += packet->x - list->pos_x;
p->dy += packet->y - list->pos_y;
list->pos_x = packet->x;
list->pos_y = packet->y;
}
list->pos_x += packet->dx;
list->pos_x = list->pos_x < 0 ? 0 : (list->pos_x >= xres ? xres : list->pos_x);
list->pos_y += packet->dy;
list->pos_y = list->pos_y < 0 ? 0 : (list->pos_y >= yres ? yres : list->pos_y);
p->dx += packet->dx;
p->dy += packet->dy;
p->dz += packet->dz;
......@@ -224,7 +248,7 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
{
if (!value) {
if (mousedev->touch &&
!time_after(jiffies, mousedev->touch + msecs_to_jiffies(tap_time))) {
time_before(jiffies, mousedev->touch + msecs_to_jiffies(tap_time))) {
/*
* Toggle left button to emulate tap.
* We rely on the fact that mousedev_mix always has 0
......@@ -289,6 +313,7 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig
mousedev_notify_readers(&mousedev_mix, &mousedev->packet);
mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0;
mousedev->packet.abs_event = 0;
}
break;
}
......@@ -374,6 +399,8 @@ static int mousedev_open(struct inode * inode, struct file * file)
memset(list, 0, sizeof(struct mousedev_list));
spin_lock_init(&list->packet_lock);
list->pos_x = xres / 2;
list->pos_y = yres / 2;
list->mousedev = mousedev_table[i];
list_add_tail(&list->node, &mousedev_table[i]->list);
file->private_data = list;
......@@ -524,11 +551,15 @@ static ssize_t mousedev_read(struct file * file, char __user * buffer, size_t co
if (!list->ready && !list->buffer && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
retval = wait_event_interruptible(list->mousedev->wait, list->ready || list->buffer);
retval = wait_event_interruptible(list->mousedev->wait,
!list->mousedev->exist || list->ready || list->buffer);
if (retval)
return retval;
if (!list->mousedev->exist)
return -ENODEV;
if (!list->buffer && list->ready) {
mousedev_packet(list, list->ps2);
list->buffer = list->bufsiz;
......
......@@ -36,6 +36,7 @@
#include <linux/interrupt.h>
#include <linux/serio.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <asm/io.h>
......@@ -58,10 +59,12 @@ MODULE_LICENSE("GPL");
#define CT82C710_IRQ 12
static struct serio *ct82c710_port;
static int ct82c710_data;
static int ct82c710_status;
#define CT82C710_DATA ct82c710_iores.start
#define CT82C710_STATUS (ct82c710_iores.start + 1)
static struct serio *ct82c710_port;
static struct platform_device *ct82c710_device;
static struct resource ct82c710_iores;
/*
* Interrupt handler for the 82C710 mouse port. A character
......@@ -70,7 +73,7 @@ static int ct82c710_status;
static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs)
{
return serio_interrupt(ct82c710_port, inb(ct82c710_data), 0, regs);
return serio_interrupt(ct82c710_port, inb(CT82C710_DATA), 0, regs);
}
/*
......@@ -81,10 +84,10 @@ static int ct82c170_wait(void)
{
int timeout = 60000;
while ((inb(ct82c710_status) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE))
while ((inb(CT82C710_STATUS) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE))
!= (CT82C710_DEV_IDLE | CT82C710_TX_IDLE) && timeout) {
if (inb_p(ct82c710_status) & CT82C710_RX_FULL) inb_p(ct82c710_data);
if (inb_p(CT82C710_STATUS) & CT82C710_RX_FULL) inb_p(CT82C710_DATA);
udelay(1);
timeout--;
......@@ -98,7 +101,7 @@ static void ct82c710_close(struct serio *serio)
if (ct82c170_wait())
printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
outb_p(inb_p(ct82c710_status) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), ct82c710_status);
outb_p(inb_p(CT82C710_STATUS) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), CT82C710_STATUS);
if (ct82c170_wait())
printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
......@@ -113,21 +116,21 @@ static int ct82c710_open(struct serio *serio)
if (request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL))
return -1;
status = inb_p(ct82c710_status);
status = inb_p(CT82C710_STATUS);
status |= (CT82C710_ENABLE | CT82C710_RESET);
outb_p(status, ct82c710_status);
outb_p(status, CT82C710_STATUS);
status &= ~(CT82C710_RESET);
outb_p(status, ct82c710_status);
outb_p(status, CT82C710_STATUS);
status |= CT82C710_INTS_ON;
outb_p(status, ct82c710_status); /* Enable interrupts */
outb_p(status, CT82C710_STATUS); /* Enable interrupts */
while (ct82c170_wait()) {
printk(KERN_ERR "ct82c710: Device busy in open()\n");
status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON);
outb_p(status, ct82c710_status);
outb_p(status, CT82C710_STATUS);
free_irq(CT82C710_IRQ, NULL);
return -1;
}
......@@ -142,7 +145,7 @@ static int ct82c710_open(struct serio *serio)
static int ct82c710_write(struct serio *port, unsigned char c)
{
if (ct82c170_wait()) return -1;
outb_p(c, ct82c710_data);
outb_p(c, CT82C710_DATA);
return 0;
}
......@@ -162,8 +165,9 @@ static int __init ct82c710_probe(void)
return -1; /* No: no 82C710 here */
outb_p(0x0d, 0x390); /* Write index */
ct82c710_data = inb_p(0x391) << 2; /* Get mouse I/O address */
ct82c710_status = ct82c710_data + 1;
ct82c710_iores.start = inb_p(0x391) << 2; /* Get mouse I/O address */
ct82c710_iores.end = ct82c710_iores.start + 1;
ct82c710_iores.flags = IORESOURCE_IO;
outb_p(0x0f, 0x390);
outb_p(0x0f, 0x391); /* Close config mode */
......@@ -181,8 +185,9 @@ static struct serio * __init ct82c710_allocate_port(void)
serio->open = ct82c710_open;
serio->close = ct82c710_close;
serio->write = ct82c710_write;
serio->dev.parent = &ct82c710_device->dev;
strlcpy(serio->name, "C&T 82c710 mouse port", sizeof(serio->name));
snprintf(serio->phys, sizeof(serio->phys), "isa%04x/serio0", ct82c710_data);
snprintf(serio->phys, sizeof(serio->phys), "isa%04lx/serio0", CT82C710_DATA);
}
return serio;
......@@ -193,18 +198,19 @@ int __init ct82c710_init(void)
if (ct82c710_probe())
return -ENODEV;
if (request_region(ct82c710_data, 2, "ct82c710"))
return -EBUSY;
ct82c710_device = platform_device_register_simple("ct82c710", -1, &ct82c710_iores, 1);
if (IS_ERR(ct82c710_device))
return PTR_ERR(ct82c710_device);
if (!(ct82c710_port = ct82c710_allocate_port())) {
release_region(ct82c710_data, 2);
platform_device_unregister(ct82c710_device);
return -ENOMEM;
}
serio_register_port(ct82c710_port);
printk(KERN_INFO "serio: C&T 82c710 mouse port at %#x irq %d\n",
ct82c710_data, CT82C710_IRQ);
printk(KERN_INFO "serio: C&T 82c710 mouse port at %#lx irq %d\n",
CT82C710_DATA, CT82C710_IRQ);
return 0;
}
......@@ -212,7 +218,7 @@ int __init ct82c710_init(void)
void __exit ct82c710_exit(void)
{
serio_unregister_port(ct82c710_port);
release_region(ct82c710_data, 2);
platform_device_unregister(ct82c710_device);
}
module_init(ct82c710_init);
......
......@@ -21,6 +21,7 @@
#include <linux/sysdev.h>
#include <linux/pm.h>
#include <linux/serio.h>
#include <linux/err.h>
#include <asm/io.h>
......@@ -100,9 +101,9 @@ static unsigned char i8042_initial_ctr;
static unsigned char i8042_ctr;
static unsigned char i8042_mux_open;
static unsigned char i8042_mux_present;
static unsigned char i8042_sysdev_initialized;
static struct pm_dev *i8042_pm_dev;
static struct timer_list i8042_timer;
static struct platform_device *i8042_platform_device;
/*
* Shared IRQ's require a device pointer, but this driver doesn't support
......@@ -362,6 +363,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
unsigned long flags;
unsigned char str, data = 0;
unsigned int dfl;
unsigned int aux_idx;
int ret;
mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
......@@ -378,44 +380,67 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
goto out;
}
dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
if (i8042_mux_values[0].exists && (str & I8042_STR_AUXDATA)) {
if (i8042_mux_present && (str & I8042_STR_AUXDATA)) {
static unsigned long last_transmit;
static unsigned char last_str;
dfl = 0;
if (str & I8042_STR_MUXERR) {
dbg("MUX error, status is %02x, data is %02x", str, data);
switch (data) {
default:
/*
* When MUXERR condition is signalled the data register can only contain
* 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately
* it is not always the case. Some KBC just get confused which port the
* data came from and signal error leaving the data intact. They _do not_
* revert to legacy mode (actually I've never seen KBC reverting to legacy
* mode yet, when we see one we'll add proper handling).
* Anyway, we will assume that the data came from the same serio last byte
* was transmitted (if transmission happened not too long ago).
*/
if (time_before(jiffies, last_transmit + HZ/10)) {
str = last_str;
break;
}
/* fall through - report timeout */
case 0xfd:
case 0xfe: dfl = SERIO_TIMEOUT; break;
case 0xff: dfl = SERIO_PARITY; break;
case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break;
case 0xff: dfl = SERIO_PARITY; data = 0xfe; break;
}
}
data = 0xfe;
} else dfl = 0;
aux_idx = (str >> 6) & 3;
dbg("%02x <- i8042 (interrupt, aux%d, %d%s%s)",
data, (str >> 6), irq,
data, aux_idx, irq,
dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : "");
serio_interrupt(i8042_mux_port[(str >> 6) & 3], data, dfl, regs);
if (likely(i8042_mux_values[aux_idx].exists))
serio_interrupt(i8042_mux_port[aux_idx], data, dfl, regs);
last_str = str;
last_transmit = jiffies;
goto irq_ret;
}
dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
dbg("%02x <- i8042 (interrupt, %s, %d%s%s)",
data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", irq,
dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : "");
if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) {
serio_interrupt(i8042_aux_port, data, dfl, regs);
goto irq_ret;
}
if (!i8042_kbd_values.exists)
goto irq_ret;
if (str & I8042_STR_AUXDATA) {
if (likely(i8042_aux_values.exists))
serio_interrupt(i8042_aux_port, data, dfl, regs);
} else {
if (likely(i8042_kbd_values.exists))
serio_interrupt(i8042_kbd_port, data, dfl, regs);
}
irq_ret:
ret = 1;
......@@ -854,7 +879,7 @@ static int i8042_notify_sys(struct notifier_block *this, unsigned long code,
return NOTIFY_DONE;
}
static struct notifier_block i8042_notifier=
static struct notifier_block i8042_notifier =
{
i8042_notify_sys,
NULL,
......@@ -864,25 +889,27 @@ static struct notifier_block i8042_notifier=
/*
* Suspend/resume handlers for the new PM scheme (driver model)
*/
static int i8042_suspend(struct sys_device *dev, u32 state)
static int i8042_suspend(struct device *dev, u32 state, u32 level)
{
return i8042_controller_suspend();
return level == SUSPEND_DISABLE ? i8042_controller_suspend() : 0;
}
static int i8042_resume(struct sys_device *dev)
static int i8042_resume(struct device *dev, u32 level)
{
return i8042_controller_resume();
return level == RESUME_ENABLE ? i8042_controller_resume() : 0;
}
static struct sysdev_class kbc_sysclass = {
set_kset_name("i8042"),
static void i8042_shutdown(struct device *dev)
{
i8042_controller_cleanup();
}
static struct device_driver i8042_driver = {
.name = "i8042",
.bus = &platform_bus_type,
.suspend = i8042_suspend,
.resume = i8042_resume,
};
static struct sys_device device_i8042 = {
.id = 0,
.cls = &kbc_sysclass,
.shutdown = i8042_shutdown,
};
/*
......@@ -913,6 +940,7 @@ static struct serio * __init i8042_allocate_kbd_port(void)
serio->open = i8042_open,
serio->close = i8042_close,
serio->port_data = &i8042_kbd_values,
serio->dev.parent = &i8042_platform_device->dev;
strlcpy(serio->name, "i8042 Kbd Port", sizeof(serio->name));
strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys));
}
......@@ -932,6 +960,7 @@ static struct serio * __init i8042_allocate_aux_port(void)
serio->open = i8042_open;
serio->close = i8042_close;
serio->port_data = &i8042_aux_values,
serio->dev.parent = &i8042_platform_device->dev;
strlcpy(serio->name, "i8042 Aux Port", sizeof(serio->name));
strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
}
......@@ -956,6 +985,7 @@ static struct serio * __init i8042_allocate_mux_port(int index)
serio->open = i8042_open;
serio->close = i8042_close;
serio->port_data = values;
serio->dev.parent = &i8042_platform_device->dev;
snprintf(serio->name, sizeof(serio->name), "i8042 Aux-%d Port", index);
snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, index + 1);
}
......@@ -966,6 +996,7 @@ static struct serio * __init i8042_allocate_mux_port(int index)
int __init i8042_init(void)
{
int i;
int err;
dbg_init();
......@@ -981,6 +1012,16 @@ int __init i8042_init(void)
if (i8042_controller_init())
return -ENODEV;
err = driver_register(&i8042_driver);
if (err)
return err;
i8042_platform_device = platform_device_register_simple("i8042", -1, NULL, 0);
if (IS_ERR(i8042_platform_device)) {
driver_unregister(&i8042_driver);
return PTR_ERR(i8042_platform_device);
}
if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values)) {
if (!i8042_nomux && !i8042_check_mux(&i8042_aux_values))
for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
......@@ -1001,13 +1042,6 @@ int __init i8042_init(void)
mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
if (sysdev_class_register(&kbc_sysclass) == 0) {
if (sysdev_register(&device_i8042) == 0)
i8042_sysdev_initialized = 1;
else
sysdev_class_unregister(&kbc_sysclass);
}
i8042_pm_dev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, i8042_pm_callback);
register_reboot_notifier(&i8042_notifier);
......@@ -1024,11 +1058,6 @@ void __exit i8042_exit(void)
if (i8042_pm_dev)
pm_unregister(i8042_pm_dev);
if (i8042_sysdev_initialized) {
sysdev_unregister(&device_i8042);
sysdev_class_unregister(&kbc_sysclass);
}
i8042_controller_cleanup();
if (i8042_kbd_values.exists)
......@@ -1043,6 +1072,9 @@ void __exit i8042_exit(void)
del_timer_sync(&i8042_timer);
platform_device_unregister(i8042_platform_device);
driver_unregister(&i8042_driver);
i8042_platform_exit();
}
......
......@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/err.h>
#include <asm/io.h>
#include <asm/irq.h>
......@@ -53,6 +54,7 @@ struct maceps2_data {
static struct maceps2_data port_data[2];
static struct serio *maceps2_port[2];
static struct platform_device *maceps2_device;
static int maceps2_write(struct serio *dev, unsigned char val)
{
......@@ -130,6 +132,7 @@ static struct serio * __init maceps2_allocate_port(int idx)
snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx);
snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx);
serio->port_data = &port_data[idx];
serio->dev.parent = &maceps2_device->dev;
}
return serio;
......@@ -138,6 +141,10 @@ static struct serio * __init maceps2_allocate_port(int idx)
static int __init maceps2_init(void)
{
maceps2_device = platform_device_register_simple("maceps2", -1, NULL, 0);
if (IS_ERR(maceps2_device))
return PTR_ERR(maceps2_device);
port_data[0].port = &mace->perif.ps2.keyb;
port_data[0].irq = MACEISA_KEYB_IRQ;
port_data[1].port = &mace->perif.ps2.mouse;
......@@ -148,6 +155,7 @@ static int __init maceps2_init(void)
if (!maceps2_port[0] || !maceps2_port[1]) {
kfree(maceps2_port[0]);
kfree(maceps2_port[1]);
platform_device_unregister(maceps2_device);
return -ENOMEM;
}
......@@ -161,6 +169,7 @@ static void __exit maceps2_exit(void)
{
serio_unregister_port(maceps2_port[0]);
serio_unregister_port(maceps2_port[1]);
platform_device_unregister(maceps2_device);
}
module_init(maceps2_init);
......
......@@ -35,6 +35,7 @@
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <asm/bitops.h>
#include <asm/io.h>
......@@ -49,6 +50,7 @@ MODULE_LICENSE("GPL");
spinlock_t q40kbd_lock = SPIN_LOCK_UNLOCKED;
static struct serio *q40kbd_port;
static struct platform_device *q40kbd_device;
static irqreturn_t q40kbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
......@@ -123,6 +125,7 @@ static struct serio * __init q40kbd_allocate_port(void)
serio->type = SERIO_8042;
serio->open = q40kbd_open;
serio->close = q40kbd_close;
serio->dev.parent = &q40kbd_device->dev;
strlcpy(serio->name, "Q40 Kbd Port", sizeof(serio->name));
strlcpy(serio->phys, "Q40", sizeof(serio->phys));
}
......@@ -135,8 +138,14 @@ static int __init q40kbd_init(void)
if (!MACH_IS_Q40)
return -EIO;
if (!(q40kbd_port = q40kbd_allocate_port()))
q40kbd_device = platform_device_register_simple("q40kbd", -1, NULL, 0);
if (IS_ERR(q40kbd_device))
return PTR_ERR(q40kbd_device);
if (!(q40kbd_port = q40kbd_allocate_port())) {
platform_device_unregister(q40kbd_device);
return -ENOMEM;
}
serio_register_port(q40kbd_port);
printk(KERN_INFO "serio: Q40 kbd registered\n");
......@@ -147,6 +156,7 @@ static int __init q40kbd_init(void)
static void __exit q40kbd_exit(void)
{
serio_unregister_port(q40kbd_port);
platform_device_unregister(q40kbd_device);
}
module_init(q40kbd_init);
......
......@@ -33,6 +33,7 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/err.h>
#include <asm/irq.h>
#include <asm/hardware.h>
......@@ -45,6 +46,7 @@ MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver");
MODULE_LICENSE("GPL");
static struct serio *rpckbd_port;
static struct platform_device *rpckbd_device;
static int rpckbd_write(struct serio *port, unsigned char val)
{
......@@ -119,6 +121,7 @@ static struct serio * __init rpckbd_allocate_port(void)
serio->write = rpckbd_write;
serio->open = rpckbd_open;
serio->close = rpckbd_close;
serio->dev.parent = &rpckbd_device->dev;
strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name));
strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys));
}
......@@ -128,8 +131,14 @@ static struct serio * __init rpckbd_allocate_port(void)
static int __init rpckbd_init(void)
{
if (!(rpckbd_port = rpckbd_allocate_port()))
rpckbd_device = platform_device_register_simple("rpckbd", -1, NULL, 0);
if (IS_ERR(rpckbd_device))
return PTR_ERR(rpckbd_device);
if (!(rpckbd_port = rpckbd_allocate_port())) {
platform_device_unregister(rpckbd_device);
return -ENOMEM;
}
serio_register_port(rpckbd_port);
return 0;
......@@ -138,6 +147,7 @@ static int __init rpckbd_init(void)
static void __exit rpckbd_exit(void)
{
serio_unregister_port(rpckbd_port);
platform_device_unregister(rpckbd_device);
}
module_init(rpckbd_init);
......
......@@ -245,7 +245,6 @@ static ssize_t serio_show_description(struct device *dev, char *buf)
struct serio *serio = to_serio_port(dev);
return sprintf(buf, "%s\n", serio->name);
}
static DEVICE_ATTR(description, S_IRUGO, serio_show_description, NULL);
static ssize_t serio_show_driver(struct device *dev, char *buf)
{
......@@ -256,7 +255,6 @@ static ssize_t serio_rebind_driver(struct device *dev, const char *buf, size_t c
{
struct serio *serio = to_serio_port(dev);
struct device_driver *drv;
struct kobject *k;
int retval;
retval = down_interruptible(&serio_sem);
......@@ -271,10 +269,10 @@ static ssize_t serio_rebind_driver(struct device *dev, const char *buf, size_t c
} else if (!strncmp(buf, "rescan", count)) {
serio_disconnect_port(serio);
serio_connect_port(serio, NULL);
} else if ((k = kset_find_obj(&serio_bus.drivers, buf)) != NULL) {
drv = container_of(k, struct device_driver, kobj);
} else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
serio_disconnect_port(serio);
serio_connect_port(serio, to_serio_driver(drv));
put_driver(drv);
} else {
retval = -EINVAL;
}
......@@ -283,7 +281,37 @@ static ssize_t serio_rebind_driver(struct device *dev, const char *buf, size_t c
return retval;
}
static DEVICE_ATTR(driver, S_IWUSR | S_IRUGO, serio_show_driver, serio_rebind_driver);
static ssize_t serio_show_bind_mode(struct device *dev, char *buf)
{
struct serio *serio = to_serio_port(dev);
return sprintf(buf, "%s\n", serio->manual_bind ? "manual" : "auto");
}
static ssize_t serio_set_bind_mode(struct device *dev, const char *buf, size_t count)
{
struct serio *serio = to_serio_port(dev);
int retval;
retval = count;
if (!strncmp(buf, "manual", count)) {
serio->manual_bind = 1;
} else if (!strncmp(buf, "auto", count)) {
serio->manual_bind = 0;
} else {
retval = -EINVAL;
}
return retval;
}
static struct device_attribute serio_device_attrs[] = {
__ATTR(description, S_IRUGO, serio_show_description, NULL),
__ATTR(driver, S_IWUSR | S_IRUGO, serio_show_driver, serio_rebind_driver),
__ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode),
__ATTR_NULL
};
static void serio_release_port(struct device *dev)
{
......@@ -305,8 +333,6 @@ static void serio_create_port(struct serio *serio)
if (serio->parent)
serio->dev.parent = &serio->parent->dev;
device_register(&serio->dev);
device_create_file(&serio->dev, &dev_attr_description);
device_create_file(&serio->dev, &dev_attr_driver);
}
/*
......@@ -350,7 +376,7 @@ static void serio_connect_port(struct serio *serio, struct serio_driver *drv)
if (drv)
serio_bind_driver(serio, drv);
else
else if (!serio->manual_bind)
serio_find_driver(serio);
/* Ok, now bind children, if any */
......@@ -362,6 +388,7 @@ static void serio_connect_port(struct serio *serio, struct serio_driver *drv)
serio_create_port(serio);
if (!serio->manual_bind) {
/*
* With children we just _prefer_ passed in driver,
* but we will try other options in case preferred
......@@ -370,6 +397,7 @@ static void serio_connect_port(struct serio *serio, struct serio_driver *drv)
if (!drv || !serio_bind_driver(serio, drv))
serio_find_driver(serio);
}
}
}
/*
......@@ -481,7 +509,37 @@ static ssize_t serio_driver_show_description(struct device_driver *drv, char *bu
struct serio_driver *driver = to_serio_driver(drv);
return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)");
}
static DRIVER_ATTR(description, S_IRUGO, serio_driver_show_description, NULL);
static ssize_t serio_driver_show_bind_mode(struct device_driver *drv, char *buf)
{
struct serio_driver *serio_drv = to_serio_driver(drv);
return sprintf(buf, "%s\n", serio_drv->manual_bind ? "manual" : "auto");
}
static ssize_t serio_driver_set_bind_mode(struct device_driver *drv, const char *buf, size_t count)
{
struct serio_driver *serio_drv = to_serio_driver(drv);
int retval;
retval = count;
if (!strncmp(buf, "manual", count)) {
serio_drv->manual_bind = 1;
} else if (!strncmp(buf, "auto", count)) {
serio_drv->manual_bind = 0;
} else {
retval = -EINVAL;
}
return retval;
}
static struct driver_attribute serio_driver_attrs[] = {
__ATTR(description, S_IRUGO, serio_driver_show_description, NULL),
__ATTR(bind_mode, S_IWUSR | S_IRUGO,
serio_driver_show_bind_mode, serio_driver_set_bind_mode),
__ATTR_NULL
};
void serio_register_driver(struct serio_driver *drv)
{
......@@ -493,7 +551,6 @@ void serio_register_driver(struct serio_driver *drv)
drv->driver.bus = &serio_bus;
driver_register(&drv->driver);
driver_create_file(&drv->driver, &driver_attr_description);
if (drv->manual_bind)
goto out;
......@@ -541,15 +598,14 @@ void serio_unregister_driver(struct serio_driver *drv)
/* called from serio_driver->connect/disconnect methods under serio_sem */
int serio_open(struct serio *serio, struct serio_driver *drv)
{
unsigned long flags;
spin_lock_irqsave(&serio->lock, flags);
serio_pause_rx(serio);
serio->drv = drv;
spin_unlock_irqrestore(&serio->lock, flags);
serio_continue_rx(serio);
if (serio->open && serio->open(serio)) {
spin_lock_irqsave(&serio->lock, flags);
serio_pause_rx(serio);
serio->drv = NULL;
spin_unlock_irqrestore(&serio->lock, flags);
serio_continue_rx(serio);
return -1;
}
return 0;
......@@ -558,13 +614,12 @@ int serio_open(struct serio *serio, struct serio_driver *drv)
/* called from serio_driver->connect/disconnect methods under serio_sem */
void serio_close(struct serio *serio)
{
unsigned long flags;
if (serio->close)
serio->close(serio);
spin_lock_irqsave(&serio->lock, flags);
serio_pause_rx(serio);
serio->drv = NULL;
spin_unlock_irqrestore(&serio->lock, flags);
serio_continue_rx(serio);
}
irqreturn_t serio_interrupt(struct serio *serio,
......@@ -599,6 +654,8 @@ static int __init serio_init(void)
return -1;
}
serio_bus.dev_attrs = serio_device_attrs;
serio_bus.drv_attrs = serio_driver_attrs;
bus_register(&serio_bus);
return 0;
......
......@@ -210,7 +210,7 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count,
return -EAGAIN;
retval = wait_event_interruptible(list->tsdev->wait,
(list->head != list->tail) && list->tsdev->exist);
list->head != list->tail || !list->tsdev->exist);
if (retval)
return retval;
......
......@@ -1529,7 +1529,6 @@ static void __init sunzilog_prepare(void)
static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channel)
{
int baud, brg;
struct serio *serio;
if (channel == KEYBOARD_LINE) {
up->flags |= SUNZILOG_FLAG_CONS_KEYB;
......@@ -1546,8 +1545,15 @@ static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channe
up->curregs[R15] = BRKIE;
brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
sunzilog_convert_to_zs(up, up->cflag, 0, brg);
sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS);
__sunzilog_startup(up);
}
#ifdef CONFIG_SERIO
static void __init sunzilog_register_serio(struct uart_sunzilog_port *up, int channel)
{
struct serio *serio;
up->serio = serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
if (serio) {
......@@ -1576,11 +1582,8 @@ static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channe
printk(KERN_WARNING "zs%d: not enough memory for serio port\n",
channel);
}
#endif
sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS);
__sunzilog_startup(up);
}
#endif
static void __init sunzilog_init_hw(void)
{
......@@ -1624,6 +1627,11 @@ static void __init sunzilog_init_hw(void)
}
spin_unlock_irqrestore(&up->port.lock, flags);
#ifdef CONFIG_SERIO
if (i == KEYBOARD_LINE || i == MOUSE_LINE)
sunzilog_register_serio(up, i);
#endif
}
}
......
......@@ -27,6 +27,8 @@ struct serio {
char name[32];
char phys[32];
unsigned int manual_bind;
unsigned short idbus;
unsigned short idvendor;
unsigned short idproduct;
......@@ -35,7 +37,7 @@ struct serio {
unsigned long type;
unsigned long event;
spinlock_t lock;
spinlock_t lock; /* protects critical sections from port's interrupt handler */
int (*write)(struct serio *, unsigned char);
int (*open)(struct serio *);
......@@ -43,7 +45,7 @@ struct serio {
struct serio *parent, *child;
struct serio_driver *drv; /* Accessed from interrupt, writes must be protected by serio_lock */
struct serio_driver *drv; /* accessed from interrupt, must be protected by serio->lock */
struct device dev;
......@@ -55,7 +57,7 @@ struct serio_driver {
void *private;
char *description;
int manual_bind;
unsigned int manual_bind;
void (*write_wakeup)(struct serio *);
irqreturn_t (*interrupt)(struct serio *, unsigned char,
......@@ -81,6 +83,7 @@ void serio_register_port(struct serio *serio);
void serio_register_port_delayed(struct serio *serio);
void serio_unregister_port(struct serio *serio);
void serio_unregister_port_delayed(struct serio *serio);
void serio_register_driver(struct serio_driver *drv);
void serio_unregister_driver(struct serio_driver *drv);
......@@ -104,6 +107,22 @@ static __inline__ void serio_cleanup(struct serio *serio)
serio->drv->cleanup(serio);
}
/*
* Use the following fucntions to protect critical sections in
* driver code from port's interrupt handler
*/
static __inline__ void serio_pause_rx(struct serio *serio)
{
spin_lock_irq(&serio->lock);
}
static __inline__ void serio_continue_rx(struct serio *serio)
{
spin_unlock_irq(&serio->lock);
}
#endif
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment