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/ ...@@ -15,6 +15,9 @@ obj-$(CONFIG_PNP) += pnp/
# char/ comes before serial/ etc so that the VT console is the boot-time # char/ comes before serial/ etc so that the VT console is the boot-time
# default. # default.
obj-y += char/ 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-y += serial/
obj-$(CONFIG_PARPORT) += parport/ obj-$(CONFIG_PARPORT) += parport/
obj-y += base/ block/ misc/ net/ media/ obj-y += base/ block/ misc/ net/ media/
...@@ -37,7 +40,6 @@ obj-$(CONFIG_PARIDE) += block/paride/ ...@@ -37,7 +40,6 @@ obj-$(CONFIG_PARIDE) += block/paride/
obj-$(CONFIG_TC) += tc/ obj-$(CONFIG_TC) += tc/
obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_USB) += usb/
obj-$(CONFIG_USB_GADGET) += usb/gadget/ obj-$(CONFIG_USB_GADGET) += usb/gadget/
obj-$(CONFIG_SERIO) += input/serio/
obj-$(CONFIG_INPUT) += input/ obj-$(CONFIG_INPUT) += input/
obj-$(CONFIG_GAMEPORT) += input/gameport/ obj-$(CONFIG_GAMEPORT) += input/gameport/
obj-$(CONFIG_I2O) += message/ obj-$(CONFIG_I2O) += message/
......
...@@ -232,8 +232,10 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo ...@@ -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)) && list->head == list->tail && (file->f_flags & O_NONBLOCK))
return -EAGAIN; return -EAGAIN;
retval = wait_event_interruptible(list->joydev->wait, list->joydev->exist retval = wait_event_interruptible(list->joydev->wait,
&& (list->startup < joydev->nabs + joydev->nkey || list->head != list->tail)); !list->joydev->exist ||
list->startup < joydev->nabs + joydev->nkey ||
list->head != list->tail);
if (retval) if (retval)
return retval; return retval;
......
...@@ -173,8 +173,7 @@ static unsigned char atkbd_scroll_keys[5][2] = { ...@@ -173,8 +173,7 @@ static unsigned char atkbd_scroll_keys[5][2] = {
#define ATKBD_FLAG_ACK 0 /* Waiting for ACK/NAK */ #define ATKBD_FLAG_ACK 0 /* Waiting for ACK/NAK */
#define ATKBD_FLAG_CMD 1 /* Waiting for command to finish */ #define ATKBD_FLAG_CMD 1 /* Waiting for command to finish */
#define ATKBD_FLAG_CMD1 2 /* First byte of command response */ #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 3 /* Waining for init to finish */
#define ATKBD_FLAG_ENABLED 4 /* Waining for init to finish */
/* /*
* The atkbd control structure * The atkbd control structure
...@@ -210,10 +209,25 @@ struct atkbd { ...@@ -210,10 +209,25 @@ struct atkbd {
unsigned int last; unsigned int last;
unsigned long time; 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 */ /* Flags */
unsigned long 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) static void atkbd_report_key(struct input_dev *dev, struct pt_regs *regs, int code, int value)
{ {
input_regs(dev, regs); input_regs(dev, regs);
...@@ -254,37 +268,38 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, ...@@ -254,37 +268,38 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
atkbd->resend = 0; atkbd->resend = 0;
#endif #endif
if (test_bit(ATKBD_FLAG_ACK, &atkbd->flags)) if (test_bit(ATKBD_FLAG_ACK, &atkbd->flags)) {
switch (code) { switch (code) {
case ATKBD_RET_ACK: case ATKBD_RET_ACK:
atkbd->nak = 0; atkbd->nak = 0;
if (atkbd->cmdcnt) { if (atkbd->cmdcnt) {
set_bit(ATKBD_FLAG_CMD, &atkbd->flags); set_bit(ATKBD_FLAG_CMD, &atkbd->flags);
set_bit(ATKBD_FLAG_CMD1, &atkbd->flags); set_bit(ATKBD_FLAG_CMD1, &atkbd->flags);
set_bit(ATKBD_FLAG_ID, &atkbd->flags);
} }
clear_bit(ATKBD_FLAG_ACK, &atkbd->flags); clear_bit(ATKBD_FLAG_ACK, &atkbd->flags);
goto out; wake_up_interruptible(&atkbd->wait);
break;
case ATKBD_RET_NAK: case ATKBD_RET_NAK:
atkbd->nak = 1; atkbd->nak = 1;
clear_bit(ATKBD_FLAG_ACK, &atkbd->flags); clear_bit(ATKBD_FLAG_ACK, &atkbd->flags);
goto out; wake_up_interruptible(&atkbd->wait);
break;
} }
goto out;
}
if (test_bit(ATKBD_FLAG_CMD, &atkbd->flags)) { if (test_bit(ATKBD_FLAG_CMD, &atkbd->flags)) {
atkbd->cmdcnt--; if (atkbd->cmdcnt)
atkbd->cmdbuf[atkbd->cmdcnt] = code; atkbd->cmdbuf[--atkbd->cmdcnt] = code;
if (atkbd->cmdcnt == 1) { if (test_and_clear_bit(ATKBD_FLAG_CMD1, &atkbd->flags) && atkbd->cmdcnt)
if (code != 0xab && code != 0xac) wake_up_interruptible(&atkbd->wait);
clear_bit(ATKBD_FLAG_ID, &atkbd->flags);
clear_bit(ATKBD_FLAG_CMD1, &atkbd->flags);
}
if (!atkbd->cmdcnt) if (!atkbd->cmdcnt) {
clear_bit(ATKBD_FLAG_CMD, &atkbd->flags); clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
wake_up_interruptible(&atkbd->wait);
}
goto out; goto out;
} }
...@@ -417,87 +432,147 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, ...@@ -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 * acknowledge. It doesn't handle resends according to the keyboard
* protocol specs, because if these are needed, the keyboard needs * protocol specs, because if these are needed, the keyboard needs
* replacement anyway, and they only make a mess in the protocol. * 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) static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte)
{ {
int timeout = 200000; /* 200 msec */
#ifdef ATKBD_DEBUG #ifdef ATKBD_DEBUG
printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte); printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte);
#endif #endif
atkbd->nak = 1;
set_bit(ATKBD_FLAG_ACK, &atkbd->flags); 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; return -atkbd->nak;
} }
/* /*
* atkbd_command() sends a command, and its parameters to the keyboard, * atkbd_command() sends a command, and its parameters to the keyboard,
* then waits for the response and puts it in the param array. * 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) 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 send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf; int receive = (command >> 8) & 0xf;
int rc = -1;
int i; int i;
atkbd->cmdcnt = receive; timeout = msecs_to_jiffies(command == ATKBD_CMD_RESET_BAT ? 4000 : 500);
if (command == ATKBD_CMD_RESET_BAT) down(&atkbd->cmd_sem);
timeout = 4000000; /* 4 sec */ clear_bit(ATKBD_FLAG_CMD, &atkbd->flags);
if (receive && param) if (receive && param)
for (i = 0; i < receive; i++) for (i = 0; i < receive; i++)
atkbd->cmdbuf[(receive - 1) - i] = param[i]; atkbd->cmdbuf[(receive - 1) - i] = param[i];
atkbd->cmdcnt = receive;
if (command & 0xff) if (command & 0xff)
if (atkbd_sendbyte(atkbd, command & 0xff)) if (atkbd_sendbyte(atkbd, command & 0xff))
return -1; goto out;
for (i = 0; i < send; i++) for (i = 0; i < send; i++)
if (atkbd_sendbyte(atkbd, param[i])) if (atkbd_sendbyte(atkbd, param[i]))
return -1; goto out;
while (test_bit(ATKBD_FLAG_CMD, &atkbd->flags) && timeout--) { timeout = wait_event_interruptible_timeout(atkbd->wait,
!test_bit(ATKBD_FLAG_CMD1, &atkbd->flags), timeout);
if (!test_bit(ATKBD_FLAG_CMD1, &atkbd->flags)) { if (atkbd->cmdcnt && timeout > 0) {
if (command == ATKBD_CMD_RESET_BAT && jiffies_to_msecs(timeout) > 100)
if (command == ATKBD_CMD_RESET_BAT && timeout > 100000) timeout = msecs_to_jiffies(100);
timeout = 100000;
if (command == ATKBD_CMD_GETID && !test_bit(ATKBD_FLAG_ID, &atkbd->flags)) { if (command == ATKBD_CMD_GETID &&
clear_bit(ATKBD_FLAG_CMD, &atkbd->flags); atkbd->cmdbuf[receive - 1] != 0xab && atkbd->cmdbuf[receive - 1] != 0xac) {
atkbd->cmdcnt = 0; /*
break; * 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;
} }
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) if (param)
for (i = 0; i < receive; i++) for (i = 0; i < receive; i++)
param[i] = atkbd->cmdbuf[(receive - 1) - i]; param[i] = atkbd->cmdbuf[(receive - 1) - i];
if (command == ATKBD_CMD_RESET_BAT && atkbd->cmdcnt == 1) if (atkbd->cmdcnt && (command != ATKBD_CMD_RESET_BAT || atkbd->cmdcnt != 1))
return 0; goto out;
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->cmdcnt) if (!(atkbd_work = kmalloc(sizeof(struct atkbd_work) + max(send, receive), GFP_ATOMIC)))
return -1; 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; return 0;
} }
/* /*
* Event callback from the input module. Events that change the state of * Event callback from the input module. Events that change the state of
* the hardware are processed here. * the hardware are processed here.
...@@ -524,7 +599,7 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co ...@@ -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) param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0)
| (test_bit(LED_NUML, dev->led) ? 2 : 0) | (test_bit(LED_NUML, dev->led) ? 2 : 0)
| (test_bit(LED_CAPSL, dev->led) ? 4 : 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) { if (atkbd->extra) {
param[0] = 0; param[0] = 0;
...@@ -533,7 +608,7 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co ...@@ -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_SUSPEND, dev->led) ? 0x04 : 0)
| (test_bit(LED_MISC, dev->led) ? 0x10 : 0) | (test_bit(LED_MISC, dev->led) ? 0x10 : 0)
| (test_bit(LED_MUTE, dev->led) ? 0x20 : 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; return 0;
...@@ -549,7 +624,7 @@ static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int co ...@@ -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_PERIOD] = period[i];
dev->rep[REP_DELAY] = delay[j]; dev->rep[REP_DELAY] = delay[j];
param[0] = i | (j << 5); param[0] = i | (j << 5);
atkbd_command(atkbd, param, ATKBD_CMD_SETREP); atkbd_schedule_command(atkbd, param, ATKBD_CMD_SETREP);
return 0; return 0;
} }
...@@ -721,7 +796,11 @@ static void atkbd_cleanup(struct serio *serio) ...@@ -721,7 +796,11 @@ static void atkbd_cleanup(struct serio *serio)
static void atkbd_disconnect(struct serio *serio) static void atkbd_disconnect(struct serio *serio)
{ {
struct atkbd *atkbd = serio->private; struct atkbd *atkbd = serio->private;
clear_bit(ATKBD_FLAG_ENABLED, &atkbd->flags); clear_bit(ATKBD_FLAG_ENABLED, &atkbd->flags);
synchronize_kernel();
flush_scheduled_work();
input_unregister_device(&atkbd->dev); input_unregister_device(&atkbd->dev);
serio_close(serio); serio_close(serio);
kfree(atkbd); kfree(atkbd);
...@@ -743,6 +822,9 @@ static void atkbd_connect(struct serio *serio, struct serio_driver *drv) ...@@ -743,6 +822,9 @@ static void atkbd_connect(struct serio *serio, struct serio_driver *drv)
return; return;
memset(atkbd, 0, sizeof(struct atkbd)); memset(atkbd, 0, sizeof(struct atkbd));
init_MUTEX(&atkbd->cmd_sem);
init_waitqueue_head(&atkbd->wait);
switch (serio->type & SERIO_TYPE) { switch (serio->type & SERIO_TYPE) {
case SERIO_8042_XL: case SERIO_8042_XL:
......
...@@ -147,45 +147,64 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, ...@@ -147,45 +147,64 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
psmouse->nak = 1; psmouse->nak = 1;
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags); clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
wake_up_interruptible(&psmouse->wait);
goto out; goto out;
} }
if (test_bit(PSMOUSE_FLAG_ACK, &psmouse->flags)) if (test_bit(PSMOUSE_FLAG_ACK, &psmouse->flags)) {
switch (data) { switch (data) {
case PSMOUSE_RET_ACK: case PSMOUSE_RET_ACK:
psmouse->nak = 0; psmouse->nak = 0;
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags);
goto out;
break; break;
case PSMOUSE_RET_NAK: case PSMOUSE_RET_NAK:
psmouse->nak = 1; psmouse->nak = 1;
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags); break;
goto out;
/*
* 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: default:
psmouse->nak = 0; /* Workaround for mice which don't ACK the Get ID command */ goto out;
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--; if (test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags)) {
psmouse->cmdbuf[psmouse->cmdcnt] = data; if (psmouse->cmdcnt)
psmouse->cmdbuf[--psmouse->cmdcnt] = data;
if (psmouse->cmdcnt == 1) { if (test_and_clear_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags) && psmouse->cmdcnt)
if (data != 0xab && data != 0xac) wake_up_interruptible(&psmouse->wait);
clear_bit(PSMOUSE_FLAG_ID, &psmouse->flags);
clear_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags);
}
if (!psmouse->cmdcnt) if (!psmouse->cmdcnt) {
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
wake_up_interruptible(&psmouse->wait);
}
goto out; goto out;
} }
if (psmouse->state == PSMOUSE_INITIALIZING)
goto out;
if (psmouse->state == PSMOUSE_ACTIVATED && if (psmouse->state == PSMOUSE_ACTIVATED &&
psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) { psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n", 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, ...@@ -251,90 +270,95 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
* psmouse_sendbyte() sends a byte to the mouse, and waits for acknowledge. * psmouse_sendbyte() sends a byte to the mouse, and waits for acknowledge.
* It doesn't handle retransmission, though it could - because when there would * It doesn't handle retransmission, though it could - because when there would
* be need for retransmissions, the mouse has to be replaced anyway. * 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) 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); 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; return -psmouse->nak;
} }
/* /*
* psmouse_command() sends a command and its parameters to the mouse, * psmouse_command() sends a command and its parameters to the mouse,
* then waits for the response and puts it in the param array. * 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 psmouse_command(struct psmouse *psmouse, unsigned char *param, int command)
{ {
int timeout = 500000; /* 500 msec */ int timeout;
int send = (command >> 12) & 0xf; int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf; int receive = (command >> 8) & 0xf;
int rc = -1;
int i; int i;
psmouse->cmdcnt = receive; timeout = msecs_to_jiffies(command == PSMOUSE_CMD_RESET_BAT ? 4000 : 500);
if (command == PSMOUSE_CMD_RESET_BAT) clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
timeout = 4000000; /* 4 sec */ if (command == PSMOUSE_CMD_GETID)
set_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags);
if (receive && param) if (receive && param)
for (i = 0; i < receive; i++) for (i = 0; i < receive; i++)
psmouse->cmdbuf[(receive - 1) - i] = param[i]; psmouse->cmdbuf[(receive - 1) - i] = param[i];
if (receive) { psmouse->cmdcnt = receive;
set_bit(PSMOUSE_FLAG_CMD, &psmouse->flags);
set_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags);
set_bit(PSMOUSE_FLAG_ID, &psmouse->flags);
}
if (command & 0xff) if (command & 0xff)
if (psmouse_sendbyte(psmouse, command & 0xff)) { if (psmouse_sendbyte(psmouse, command & 0xff))
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); goto out;
return -1;
}
for (i = 0; i < send; i++) for (i = 0; i < send; i++)
if (psmouse_sendbyte(psmouse, param[i])) { if (psmouse_sendbyte(psmouse, param[i]))
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); goto out;
return -1;
}
while (test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags) && timeout--) { timeout = wait_event_interruptible_timeout(psmouse->wait,
!test_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags), timeout);
if (!test_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags)) { if (psmouse->cmdcnt && timeout > 0) {
if (command == PSMOUSE_CMD_RESET_BAT && jiffies_to_msecs(timeout) > 100)
if (command == PSMOUSE_CMD_RESET_BAT && timeout > 100000) timeout = msecs_to_jiffies(100);
timeout = 100000;
if (command == PSMOUSE_CMD_GETID && !test_bit(PSMOUSE_FLAG_ID, &psmouse->flags)) { if (command == PSMOUSE_CMD_GETID &&
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); psmouse->cmdbuf[receive - 1] != 0xab && psmouse->cmdbuf[receive - 1] != 0xac) {
psmouse->cmdcnt = 0; /*
break; * 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;
} }
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) if (param)
for (i = 0; i < receive; i++) for (i = 0; i < receive; i++)
param[i] = psmouse->cmdbuf[(receive - 1) - i]; param[i] = psmouse->cmdbuf[(receive - 1) - i];
if (command == PSMOUSE_CMD_RESET_BAT && psmouse->cmdcnt == 1) if (psmouse->cmdcnt && (command != PSMOUSE_CMD_RESET_BAT || psmouse->cmdcnt != 1))
return 0; goto out;
if (psmouse->cmdcnt) rc = 0;
return -1;
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) ...@@ -625,6 +649,21 @@ static void psmouse_initialize(struct psmouse *psmouse)
psmouse_command(psmouse, param, PSMOUSE_CMD_SETSTREAM); 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. * psmouse_activate() enables the mouse so that we get motion reports from it.
*/ */
...@@ -634,9 +673,24 @@ static void psmouse_activate(struct psmouse *psmouse) ...@@ -634,9 +673,24 @@ static void psmouse_activate(struct psmouse *psmouse)
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE)) if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE))
printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys); 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. * psmouse_cleanup() resets the mouse into power-on state.
*/ */
...@@ -657,7 +711,7 @@ static void psmouse_disconnect(struct serio *serio) ...@@ -657,7 +711,7 @@ static void psmouse_disconnect(struct serio *serio)
struct psmouse *psmouse, *parent; struct psmouse *psmouse, *parent;
psmouse = serio->private; 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) { if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
parent = serio->parent->private; parent = serio->parent->private;
...@@ -668,7 +722,7 @@ static void psmouse_disconnect(struct serio *serio) ...@@ -668,7 +722,7 @@ static void psmouse_disconnect(struct serio *serio)
if (psmouse->disconnect) if (psmouse->disconnect)
psmouse->disconnect(psmouse); psmouse->disconnect(psmouse);
psmouse->state = PSMOUSE_IGNORE; psmouse_set_state(psmouse, PSMOUSE_IGNORE);
input_unregister_device(&psmouse->dev); input_unregister_device(&psmouse->dev);
serio_close(serio); serio_close(serio);
...@@ -687,21 +741,28 @@ static void psmouse_connect(struct serio *serio, struct serio_driver *drv) ...@@ -687,21 +741,28 @@ static void psmouse_connect(struct serio *serio, struct serio_driver *drv)
(serio->type & SERIO_TYPE) != SERIO_PS_PSTHRU) (serio->type & SERIO_TYPE) != SERIO_PS_PSTHRU)
return; 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; parent = serio->parent->private;
psmouse_deactivate(parent);
}
if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL))) if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL)))
goto out; goto out;
memset(psmouse, 0, sizeof(struct psmouse)); memset(psmouse, 0, sizeof(struct psmouse));
init_waitqueue_head(&psmouse->wait);
init_input_dev(&psmouse->dev); init_input_dev(&psmouse->dev);
psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); 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.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
psmouse->state = PSMOUSE_CMD_MODE;
psmouse->serio = serio; psmouse->serio = serio;
psmouse->dev.private = psmouse; psmouse->dev.private = psmouse;
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
serio->private = psmouse; serio->private = psmouse;
if (serio_open(serio, drv)) { if (serio_open(serio, drv)) {
...@@ -741,26 +802,22 @@ static void psmouse_connect(struct serio *serio, struct serio_driver *drv) ...@@ -741,26 +802,22 @@ static void psmouse_connect(struct serio *serio, struct serio_driver *drv)
printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys); printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
psmouse_initialize(psmouse); psmouse_initialize(psmouse);
if (parent && parent->pt_activate) if (parent && parent->pt_activate)
parent->pt_activate(parent); 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) { if (serio->child) {
/* /*
* Nothing to be done here, serio core will detect that * Nothing to be done here, serio core will detect that
* the driver set serio->child and will register it for us. * 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); printk(KERN_INFO "serio: %s port at %s\n", serio->child->name, psmouse->phys);
} else }
psmouse_activate(psmouse);
psmouse_activate(psmouse);
out: out:
/* If this is a pass-through port the parent awaits to be activated */ /* If this is a pass-through port the parent awaits to be activated */
...@@ -774,45 +831,46 @@ static int psmouse_reconnect(struct serio *serio) ...@@ -774,45 +831,46 @@ static int psmouse_reconnect(struct serio *serio)
struct psmouse *psmouse = serio->private; struct psmouse *psmouse = serio->private;
struct psmouse *parent = NULL; struct psmouse *parent = NULL;
struct serio_driver *drv = serio->drv; struct serio_driver *drv = serio->drv;
int rc = -1;
if (!drv || !psmouse) { if (!drv || !psmouse) {
printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n"); printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n");
return -1; return -1;
} }
psmouse->state = PSMOUSE_CMD_MODE; if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
parent = serio->parent->private;
clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags); psmouse_deactivate(parent);
clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); }
psmouse->pktcnt = psmouse->out_of_sync = 0; psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
if (psmouse->reconnect) { if (psmouse->reconnect) {
if (psmouse->reconnect(psmouse)) if (psmouse->reconnect(psmouse))
return -1; goto out;
} else if (psmouse_probe(psmouse) < 0 || } else if (psmouse_probe(psmouse) < 0 ||
psmouse->type != psmouse_extensions(psmouse, psmouse_max_proto, 0)) psmouse->type != psmouse_extensions(psmouse, psmouse_max_proto, 0))
return -1; goto out;
/* ok, the device type (and capabilities) match the old one, /* ok, the device type (and capabilities) match the old one,
* we can continue using it, complete intialization * 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) psmouse_initialize(psmouse);
parent = serio->parent->private;
if (parent && parent->pt_activate) if (parent && parent->pt_activate)
parent->pt_activate(parent); parent->pt_activate(parent);
if (!serio->child) psmouse_activate(psmouse);
psmouse_activate(psmouse); rc = 0;
out:
/* If this is a pass-through port the parent waits to be activated */ /* If this is a pass-through port the parent waits to be activated */
if (parent) if (parent)
psmouse_activate(parent); psmouse_activate(parent);
return 0; return rc;
} }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#define PSMOUSE_CMD_GETID 0x02f2 #define PSMOUSE_CMD_GETID 0x02f2
#define PSMOUSE_CMD_SETRATE 0x10f3 #define PSMOUSE_CMD_SETRATE 0x10f3
#define PSMOUSE_CMD_ENABLE 0x00f4 #define PSMOUSE_CMD_ENABLE 0x00f4
#define PSMOUSE_CMD_DISABLE 0x00f5
#define PSMOUSE_CMD_RESET_DIS 0x00f6 #define PSMOUSE_CMD_RESET_DIS 0x00f6
#define PSMOUSE_CMD_RESET_BAT 0x02ff #define PSMOUSE_CMD_RESET_BAT 0x02ff
...@@ -17,15 +18,17 @@ ...@@ -17,15 +18,17 @@
#define PSMOUSE_RET_ACK 0xfa #define PSMOUSE_RET_ACK 0xfa
#define PSMOUSE_RET_NAK 0xfe #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_ACK 0 /* Waiting for ACK/NAK */
#define PSMOUSE_FLAG_CMD 1 /* Waiting for command to finish */ #define PSMOUSE_FLAG_CMD 1 /* Waiting for command to finish */
#define PSMOUSE_FLAG_CMD1 2 /* First byte of command response */ #define PSMOUSE_FLAG_CMD1 2 /* Waiting for the first byte of command response */
#define PSMOUSE_FLAG_ID 3 /* First byte is not keyboard ID */ #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 */ /* psmouse protocol handler return codes */
typedef enum { typedef enum {
...@@ -48,13 +51,16 @@ struct psmouse { ...@@ -48,13 +51,16 @@ struct psmouse {
unsigned char model; unsigned char model;
unsigned long last; unsigned long last;
unsigned long out_of_sync; unsigned long out_of_sync;
unsigned char state; enum psmouse_state state;
unsigned char nak; unsigned char nak;
char error; char error;
char devname[64]; char devname[64];
char phys[32]; char phys[32];
unsigned long flags; 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); psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs);
int (*reconnect)(struct psmouse *psmouse); int (*reconnect)(struct psmouse *psmouse);
void (*disconnect)(struct psmouse *psmouse); void (*disconnect)(struct psmouse *psmouse);
......
...@@ -233,17 +233,14 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet ...@@ -233,17 +233,14 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet
{ {
struct psmouse *child = ptport->private; struct psmouse *child = ptport->private;
if (child) { if (child && child->state == PSMOUSE_ACTIVATED) {
if (child->state == PSMOUSE_ACTIVATED) { serio_interrupt(ptport, packet[1], 0, NULL);
serio_interrupt(ptport, packet[1], 0, NULL); serio_interrupt(ptport, packet[4], 0, NULL);
serio_interrupt(ptport, packet[4], 0, NULL); serio_interrupt(ptport, packet[5], 0, NULL);
serio_interrupt(ptport, packet[5], 0, NULL); if (child->type >= PSMOUSE_GENPS)
if (child->type >= PSMOUSE_GENPS) serio_interrupt(ptport, packet[2], 0, NULL);
serio_interrupt(ptport, packet[2], 0, NULL); } else
} else if (child->state != PSMOUSE_IGNORE) { serio_interrupt(ptport, packet[1], 0, NULL);
serio_interrupt(ptport, packet[1], 0, NULL);
}
}
} }
static void synaptics_pt_activate(struct psmouse *psmouse) 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 ...@@ -472,9 +469,10 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse, struct pt_r
if (unlikely(priv->pkt_type == SYN_NEWABS)) if (unlikely(priv->pkt_type == SYN_NEWABS))
priv->pkt_type = synaptics_detect_pkt_type(psmouse); 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)) {
synaptics_pass_pt_packet(psmouse->serio->child, psmouse->packet); if (psmouse->serio->child)
else synaptics_pass_pt_packet(psmouse->serio->child, psmouse->packet);
} else
synaptics_process_packet(psmouse); synaptics_process_packet(psmouse);
return PSMOUSE_FULL_PACKET; return PSMOUSE_FULL_PACKET;
......
...@@ -52,8 +52,10 @@ static unsigned tap_time = 200; ...@@ -52,8 +52,10 @@ static unsigned tap_time = 200;
module_param(tap_time, uint, 0); module_param(tap_time, uint, 0);
MODULE_PARM_DESC(tap_time, "Tap time for touchpads in absolute mode (msecs)"); 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 dx, dy, dz;
int x, y;
int abs_event;
unsigned long buttons; unsigned long buttons;
}; };
...@@ -66,7 +68,7 @@ struct mousedev { ...@@ -66,7 +68,7 @@ struct mousedev {
struct list_head list; struct list_head list;
struct input_handle handle; struct input_handle handle;
struct mousedev_motion packet; struct mousedev_hw_data packet;
unsigned int pkt_count; unsigned int pkt_count;
int old_x[4], old_y[4]; int old_x[4], old_y[4];
unsigned long touch; unsigned long touch;
...@@ -76,7 +78,12 @@ enum mousedev_emul { ...@@ -76,7 +78,12 @@ enum mousedev_emul {
MOUSEDEV_EMUL_PS2, MOUSEDEV_EMUL_PS2,
MOUSEDEV_EMUL_IMPS, MOUSEDEV_EMUL_IMPS,
MOUSEDEV_EMUL_EXPS MOUSEDEV_EMUL_EXPS
} __attribute__ ((packed)); };
struct mousedev_motion {
int dx, dy, dz;
unsigned long buttons;
};
#define PACKET_QUEUE_LEN 16 #define PACKET_QUEUE_LEN 16
struct mousedev_list { struct mousedev_list {
...@@ -87,6 +94,7 @@ struct mousedev_list { ...@@ -87,6 +94,7 @@ struct mousedev_list {
struct mousedev_motion packets[PACKET_QUEUE_LEN]; struct mousedev_motion packets[PACKET_QUEUE_LEN];
unsigned int head, tail; unsigned int head, tail;
spinlock_t packet_lock; spinlock_t packet_lock;
int pos_x, pos_y;
signed char ps2[6]; signed char ps2[6];
unsigned char ready, buffer, bufsiz; unsigned char ready, buffer, bufsiz;
...@@ -134,15 +142,19 @@ static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev, ...@@ -134,15 +142,19 @@ static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev,
case ABS_X: case ABS_X:
size = dev->absmax[ABS_X] - dev->absmin[ABS_X]; size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
if (size == 0) size = xres; if (size == 0) size = xres;
mousedev->packet.dx = (value * xres - mousedev->old_x[0]) / size; if (value > dev->absmax[ABS_X]) value = dev->absmax[ABS_X];
mousedev->old_x[0] = mousedev->packet.dx * size; 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; break;
case ABS_Y: case ABS_Y:
size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y]; size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y];
if (size == 0) size = yres; if (size == 0) size = yres;
mousedev->packet.dy = (value * yres - mousedev->old_y[0]) / size; if (value > dev->absmax[ABS_Y]) value = dev->absmax[ABS_Y];
mousedev->old_y[0] = mousedev->packet.dy * size; 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; break;
} }
} }
...@@ -188,7 +200,7 @@ static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int ...@@ -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_list *list;
struct mousedev_motion *p; struct mousedev_motion *p;
...@@ -206,6 +218,18 @@ static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_m ...@@ -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->dx += packet->dx;
p->dy += packet->dy; p->dy += packet->dy;
p->dz += packet->dz; p->dz += packet->dz;
...@@ -224,7 +248,7 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value) ...@@ -224,7 +248,7 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
{ {
if (!value) { if (!value) {
if (mousedev->touch && 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. * Toggle left button to emulate tap.
* We rely on the fact that mousedev_mix always has 0 * 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 ...@@ -289,6 +313,7 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig
mousedev_notify_readers(&mousedev_mix, &mousedev->packet); mousedev_notify_readers(&mousedev_mix, &mousedev->packet);
mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0; mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0;
mousedev->packet.abs_event = 0;
} }
break; break;
} }
...@@ -374,6 +399,8 @@ static int mousedev_open(struct inode * inode, struct file * file) ...@@ -374,6 +399,8 @@ static int mousedev_open(struct inode * inode, struct file * file)
memset(list, 0, sizeof(struct mousedev_list)); memset(list, 0, sizeof(struct mousedev_list));
spin_lock_init(&list->packet_lock); spin_lock_init(&list->packet_lock);
list->pos_x = xres / 2;
list->pos_y = yres / 2;
list->mousedev = mousedev_table[i]; list->mousedev = mousedev_table[i];
list_add_tail(&list->node, &mousedev_table[i]->list); list_add_tail(&list->node, &mousedev_table[i]->list);
file->private_data = list; file->private_data = list;
...@@ -524,11 +551,15 @@ static ssize_t mousedev_read(struct file * file, char __user * buffer, size_t co ...@@ -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)) if (!list->ready && !list->buffer && (file->f_flags & O_NONBLOCK))
return -EAGAIN; 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) if (retval)
return retval; return retval;
if (!list->mousedev->exist)
return -ENODEV;
if (!list->buffer && list->ready) { if (!list->buffer && list->ready) {
mousedev_packet(list, list->ps2); mousedev_packet(list, list->ps2);
list->buffer = list->bufsiz; list->buffer = list->bufsiz;
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/serio.h> #include <linux/serio.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/err.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -58,10 +59,12 @@ MODULE_LICENSE("GPL"); ...@@ -58,10 +59,12 @@ MODULE_LICENSE("GPL");
#define CT82C710_IRQ 12 #define CT82C710_IRQ 12
static struct serio *ct82c710_port; #define CT82C710_DATA ct82c710_iores.start
static int ct82c710_data; #define CT82C710_STATUS (ct82c710_iores.start + 1)
static int ct82c710_status;
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 * Interrupt handler for the 82C710 mouse port. A character
...@@ -70,7 +73,7 @@ static int ct82c710_status; ...@@ -70,7 +73,7 @@ static int ct82c710_status;
static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs) 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) ...@@ -81,10 +84,10 @@ static int ct82c170_wait(void)
{ {
int timeout = 60000; 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) { != (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); udelay(1);
timeout--; timeout--;
...@@ -98,7 +101,7 @@ static void ct82c710_close(struct serio *serio) ...@@ -98,7 +101,7 @@ static void ct82c710_close(struct serio *serio)
if (ct82c170_wait()) if (ct82c170_wait())
printk(KERN_WARNING "ct82c710.c: Device busy in close()\n"); 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()) if (ct82c170_wait())
printk(KERN_WARNING "ct82c710.c: Device busy in close()\n"); printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
...@@ -113,21 +116,21 @@ static int ct82c710_open(struct serio *serio) ...@@ -113,21 +116,21 @@ static int ct82c710_open(struct serio *serio)
if (request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL)) if (request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL))
return -1; return -1;
status = inb_p(ct82c710_status); status = inb_p(CT82C710_STATUS);
status |= (CT82C710_ENABLE | CT82C710_RESET); status |= (CT82C710_ENABLE | CT82C710_RESET);
outb_p(status, ct82c710_status); outb_p(status, CT82C710_STATUS);
status &= ~(CT82C710_RESET); status &= ~(CT82C710_RESET);
outb_p(status, ct82c710_status); outb_p(status, CT82C710_STATUS);
status |= CT82C710_INTS_ON; status |= CT82C710_INTS_ON;
outb_p(status, ct82c710_status); /* Enable interrupts */ outb_p(status, CT82C710_STATUS); /* Enable interrupts */
while (ct82c170_wait()) { while (ct82c170_wait()) {
printk(KERN_ERR "ct82c710: Device busy in open()\n"); printk(KERN_ERR "ct82c710: Device busy in open()\n");
status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON); status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON);
outb_p(status, ct82c710_status); outb_p(status, CT82C710_STATUS);
free_irq(CT82C710_IRQ, NULL); free_irq(CT82C710_IRQ, NULL);
return -1; return -1;
} }
...@@ -142,7 +145,7 @@ static int ct82c710_open(struct serio *serio) ...@@ -142,7 +145,7 @@ static int ct82c710_open(struct serio *serio)
static int ct82c710_write(struct serio *port, unsigned char c) static int ct82c710_write(struct serio *port, unsigned char c)
{ {
if (ct82c170_wait()) return -1; if (ct82c170_wait()) return -1;
outb_p(c, ct82c710_data); outb_p(c, CT82C710_DATA);
return 0; return 0;
} }
...@@ -162,8 +165,9 @@ static int __init ct82c710_probe(void) ...@@ -162,8 +165,9 @@ static int __init ct82c710_probe(void)
return -1; /* No: no 82C710 here */ return -1; /* No: no 82C710 here */
outb_p(0x0d, 0x390); /* Write index */ outb_p(0x0d, 0x390); /* Write index */
ct82c710_data = inb_p(0x391) << 2; /* Get mouse I/O address */ ct82c710_iores.start = inb_p(0x391) << 2; /* Get mouse I/O address */
ct82c710_status = ct82c710_data + 1; ct82c710_iores.end = ct82c710_iores.start + 1;
ct82c710_iores.flags = IORESOURCE_IO;
outb_p(0x0f, 0x390); outb_p(0x0f, 0x390);
outb_p(0x0f, 0x391); /* Close config mode */ outb_p(0x0f, 0x391); /* Close config mode */
...@@ -181,8 +185,9 @@ static struct serio * __init ct82c710_allocate_port(void) ...@@ -181,8 +185,9 @@ static struct serio * __init ct82c710_allocate_port(void)
serio->open = ct82c710_open; serio->open = ct82c710_open;
serio->close = ct82c710_close; serio->close = ct82c710_close;
serio->write = ct82c710_write; serio->write = ct82c710_write;
serio->dev.parent = &ct82c710_device->dev;
strlcpy(serio->name, "C&T 82c710 mouse port", sizeof(serio->name)); 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; return serio;
...@@ -193,18 +198,19 @@ int __init ct82c710_init(void) ...@@ -193,18 +198,19 @@ int __init ct82c710_init(void)
if (ct82c710_probe()) if (ct82c710_probe())
return -ENODEV; return -ENODEV;
if (request_region(ct82c710_data, 2, "ct82c710")) ct82c710_device = platform_device_register_simple("ct82c710", -1, &ct82c710_iores, 1);
return -EBUSY; if (IS_ERR(ct82c710_device))
return PTR_ERR(ct82c710_device);
if (!(ct82c710_port = ct82c710_allocate_port())) { if (!(ct82c710_port = ct82c710_allocate_port())) {
release_region(ct82c710_data, 2); platform_device_unregister(ct82c710_device);
return -ENOMEM; return -ENOMEM;
} }
serio_register_port(ct82c710_port); serio_register_port(ct82c710_port);
printk(KERN_INFO "serio: C&T 82c710 mouse port at %#x irq %d\n", printk(KERN_INFO "serio: C&T 82c710 mouse port at %#lx irq %d\n",
ct82c710_data, CT82C710_IRQ); CT82C710_DATA, CT82C710_IRQ);
return 0; return 0;
} }
...@@ -212,7 +218,7 @@ int __init ct82c710_init(void) ...@@ -212,7 +218,7 @@ int __init ct82c710_init(void)
void __exit ct82c710_exit(void) void __exit ct82c710_exit(void)
{ {
serio_unregister_port(ct82c710_port); serio_unregister_port(ct82c710_port);
release_region(ct82c710_data, 2); platform_device_unregister(ct82c710_device);
} }
module_init(ct82c710_init); module_init(ct82c710_init);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/sysdev.h> #include <linux/sysdev.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/serio.h> #include <linux/serio.h>
#include <linux/err.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -100,9 +101,9 @@ static unsigned char i8042_initial_ctr; ...@@ -100,9 +101,9 @@ static unsigned char i8042_initial_ctr;
static unsigned char i8042_ctr; static unsigned char i8042_ctr;
static unsigned char i8042_mux_open; static unsigned char i8042_mux_open;
static unsigned char i8042_mux_present; static unsigned char i8042_mux_present;
static unsigned char i8042_sysdev_initialized;
static struct pm_dev *i8042_pm_dev; static struct pm_dev *i8042_pm_dev;
static struct timer_list i8042_timer; 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 * 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) ...@@ -362,6 +363,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
unsigned long flags; unsigned long flags;
unsigned char str, data = 0; unsigned char str, data = 0;
unsigned int dfl; unsigned int dfl;
unsigned int aux_idx;
int ret; int ret;
mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); 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) ...@@ -378,44 +380,67 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
goto out; goto out;
} }
dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | if (i8042_mux_present && (str & I8042_STR_AUXDATA)) {
((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0); static unsigned long last_transmit;
static unsigned char last_str;
if (i8042_mux_values[0].exists && (str & I8042_STR_AUXDATA)) {
dfl = 0;
if (str & I8042_STR_MUXERR) { if (str & I8042_STR_MUXERR) {
dbg("MUX error, status is %02x, data is %02x", str, data);
switch (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 0xfd:
case 0xfe: dfl = SERIO_TIMEOUT; break; case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break;
case 0xff: dfl = SERIO_PARITY; 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)", dbg("%02x <- i8042 (interrupt, aux%d, %d%s%s)",
data, (str >> 6), irq, data, aux_idx, irq,
dfl & SERIO_PARITY ? ", bad parity" : "", dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : ""); 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; 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)", dbg("%02x <- i8042 (interrupt, %s, %d%s%s)",
data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", irq, data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", irq,
dfl & SERIO_PARITY ? ", bad parity" : "", dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : ""); 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;
serio_interrupt(i8042_kbd_port, data, dfl, regs); 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: irq_ret:
ret = 1; ret = 1;
...@@ -854,7 +879,7 @@ static int i8042_notify_sys(struct notifier_block *this, unsigned long code, ...@@ -854,7 +879,7 @@ static int i8042_notify_sys(struct notifier_block *this, unsigned long code,
return NOTIFY_DONE; return NOTIFY_DONE;
} }
static struct notifier_block i8042_notifier= static struct notifier_block i8042_notifier =
{ {
i8042_notify_sys, i8042_notify_sys,
NULL, NULL,
...@@ -864,25 +889,27 @@ static struct notifier_block i8042_notifier= ...@@ -864,25 +889,27 @@ static struct notifier_block i8042_notifier=
/* /*
* Suspend/resume handlers for the new PM scheme (driver model) * 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 = { static void i8042_shutdown(struct device *dev)
set_kset_name("i8042"), {
.suspend = i8042_suspend, i8042_controller_cleanup();
.resume = i8042_resume, }
};
static struct sys_device device_i8042 = { static struct device_driver i8042_driver = {
.id = 0, .name = "i8042",
.cls = &kbc_sysclass, .bus = &platform_bus_type,
.suspend = i8042_suspend,
.resume = i8042_resume,
.shutdown = i8042_shutdown,
}; };
/* /*
...@@ -913,6 +940,7 @@ static struct serio * __init i8042_allocate_kbd_port(void) ...@@ -913,6 +940,7 @@ static struct serio * __init i8042_allocate_kbd_port(void)
serio->open = i8042_open, serio->open = i8042_open,
serio->close = i8042_close, serio->close = i8042_close,
serio->port_data = &i8042_kbd_values, serio->port_data = &i8042_kbd_values,
serio->dev.parent = &i8042_platform_device->dev;
strlcpy(serio->name, "i8042 Kbd Port", sizeof(serio->name)); strlcpy(serio->name, "i8042 Kbd Port", sizeof(serio->name));
strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys)); strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys));
} }
...@@ -932,6 +960,7 @@ static struct serio * __init i8042_allocate_aux_port(void) ...@@ -932,6 +960,7 @@ static struct serio * __init i8042_allocate_aux_port(void)
serio->open = i8042_open; serio->open = i8042_open;
serio->close = i8042_close; serio->close = i8042_close;
serio->port_data = &i8042_aux_values, serio->port_data = &i8042_aux_values,
serio->dev.parent = &i8042_platform_device->dev;
strlcpy(serio->name, "i8042 Aux Port", sizeof(serio->name)); strlcpy(serio->name, "i8042 Aux Port", sizeof(serio->name));
strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys)); strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
} }
...@@ -956,6 +985,7 @@ static struct serio * __init i8042_allocate_mux_port(int index) ...@@ -956,6 +985,7 @@ static struct serio * __init i8042_allocate_mux_port(int index)
serio->open = i8042_open; serio->open = i8042_open;
serio->close = i8042_close; serio->close = i8042_close;
serio->port_data = values; serio->port_data = values;
serio->dev.parent = &i8042_platform_device->dev;
snprintf(serio->name, sizeof(serio->name), "i8042 Aux-%d Port", index); snprintf(serio->name, sizeof(serio->name), "i8042 Aux-%d Port", index);
snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, index + 1); 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) ...@@ -966,6 +996,7 @@ static struct serio * __init i8042_allocate_mux_port(int index)
int __init i8042_init(void) int __init i8042_init(void)
{ {
int i; int i;
int err;
dbg_init(); dbg_init();
...@@ -981,6 +1012,16 @@ int __init i8042_init(void) ...@@ -981,6 +1012,16 @@ int __init i8042_init(void)
if (i8042_controller_init()) if (i8042_controller_init())
return -ENODEV; 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_noaux && !i8042_check_aux(&i8042_aux_values)) {
if (!i8042_nomux && !i8042_check_mux(&i8042_aux_values)) if (!i8042_nomux && !i8042_check_mux(&i8042_aux_values))
for (i = 0; i < I8042_NUM_MUX_PORTS; i++) { for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
...@@ -1001,13 +1042,6 @@ int __init i8042_init(void) ...@@ -1001,13 +1042,6 @@ int __init i8042_init(void)
mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); 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); i8042_pm_dev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, i8042_pm_callback);
register_reboot_notifier(&i8042_notifier); register_reboot_notifier(&i8042_notifier);
...@@ -1024,11 +1058,6 @@ void __exit i8042_exit(void) ...@@ -1024,11 +1058,6 @@ void __exit i8042_exit(void)
if (i8042_pm_dev) if (i8042_pm_dev)
pm_unregister(i8042_pm_dev); pm_unregister(i8042_pm_dev);
if (i8042_sysdev_initialized) {
sysdev_unregister(&device_i8042);
sysdev_class_unregister(&kbc_sysclass);
}
i8042_controller_cleanup(); i8042_controller_cleanup();
if (i8042_kbd_values.exists) if (i8042_kbd_values.exists)
...@@ -1043,6 +1072,9 @@ void __exit i8042_exit(void) ...@@ -1043,6 +1072,9 @@ void __exit i8042_exit(void)
del_timer_sync(&i8042_timer); del_timer_sync(&i8042_timer);
platform_device_unregister(i8042_platform_device);
driver_unregister(&i8042_driver);
i8042_platform_exit(); i8042_platform_exit();
} }
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/err.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -53,6 +54,7 @@ struct maceps2_data { ...@@ -53,6 +54,7 @@ struct maceps2_data {
static struct maceps2_data port_data[2]; static struct maceps2_data port_data[2];
static struct serio *maceps2_port[2]; static struct serio *maceps2_port[2];
static struct platform_device *maceps2_device;
static int maceps2_write(struct serio *dev, unsigned char val) static int maceps2_write(struct serio *dev, unsigned char val)
{ {
...@@ -123,13 +125,14 @@ static struct serio * __init maceps2_allocate_port(int idx) ...@@ -123,13 +125,14 @@ static struct serio * __init maceps2_allocate_port(int idx)
serio = kmalloc(sizeof(struct serio), GFP_KERNEL); serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
if (serio) { if (serio) {
memset(serio, 0, sizeof(struct serio)); memset(serio, 0, sizeof(struct serio));
serio->type = SERIO_8042; serio->type = SERIO_8042;
serio->write = maceps2_write; serio->write = maceps2_write;
serio->open = maceps2_open; serio->open = maceps2_open;
serio->close = maceps2_close; serio->close = maceps2_close;
snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx); snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx);
snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx); snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx);
serio->port_data = &port_data[idx]; serio->port_data = &port_data[idx];
serio->dev.parent = &maceps2_device->dev;
} }
return serio; return serio;
...@@ -138,6 +141,10 @@ static struct serio * __init maceps2_allocate_port(int idx) ...@@ -138,6 +141,10 @@ static struct serio * __init maceps2_allocate_port(int idx)
static int __init maceps2_init(void) 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].port = &mace->perif.ps2.keyb;
port_data[0].irq = MACEISA_KEYB_IRQ; port_data[0].irq = MACEISA_KEYB_IRQ;
port_data[1].port = &mace->perif.ps2.mouse; port_data[1].port = &mace->perif.ps2.mouse;
...@@ -148,6 +155,7 @@ static int __init maceps2_init(void) ...@@ -148,6 +155,7 @@ static int __init maceps2_init(void)
if (!maceps2_port[0] || !maceps2_port[1]) { if (!maceps2_port[0] || !maceps2_port[1]) {
kfree(maceps2_port[0]); kfree(maceps2_port[0]);
kfree(maceps2_port[1]); kfree(maceps2_port[1]);
platform_device_unregister(maceps2_device);
return -ENOMEM; return -ENOMEM;
} }
...@@ -161,6 +169,7 @@ static void __exit maceps2_exit(void) ...@@ -161,6 +169,7 @@ static void __exit maceps2_exit(void)
{ {
serio_unregister_port(maceps2_port[0]); serio_unregister_port(maceps2_port[0]);
serio_unregister_port(maceps2_port[1]); serio_unregister_port(maceps2_port[1]);
platform_device_unregister(maceps2_device);
} }
module_init(maceps2_init); module_init(maceps2_init);
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/serio.h> #include <linux/serio.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/err.h>
#include <asm/bitops.h> #include <asm/bitops.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -49,6 +50,7 @@ MODULE_LICENSE("GPL"); ...@@ -49,6 +50,7 @@ MODULE_LICENSE("GPL");
spinlock_t q40kbd_lock = SPIN_LOCK_UNLOCKED; spinlock_t q40kbd_lock = SPIN_LOCK_UNLOCKED;
static struct serio *q40kbd_port; 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) static irqreturn_t q40kbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{ {
...@@ -120,9 +122,10 @@ static struct serio * __init q40kbd_allocate_port(void) ...@@ -120,9 +122,10 @@ static struct serio * __init q40kbd_allocate_port(void)
serio = kmalloc(sizeof(struct serio), GFP_KERNEL); serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
if (serio) { if (serio) {
memset(serio, 0, sizeof(struct serio)); memset(serio, 0, sizeof(struct serio));
serio->type = SERIO_8042; serio->type = SERIO_8042;
serio->open = q40kbd_open; serio->open = q40kbd_open;
serio->close = q40kbd_close; serio->close = q40kbd_close;
serio->dev.parent = &q40kbd_device->dev;
strlcpy(serio->name, "Q40 Kbd Port", sizeof(serio->name)); strlcpy(serio->name, "Q40 Kbd Port", sizeof(serio->name));
strlcpy(serio->phys, "Q40", sizeof(serio->phys)); strlcpy(serio->phys, "Q40", sizeof(serio->phys));
} }
...@@ -135,8 +138,14 @@ static int __init q40kbd_init(void) ...@@ -135,8 +138,14 @@ static int __init q40kbd_init(void)
if (!MACH_IS_Q40) if (!MACH_IS_Q40)
return -EIO; 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; return -ENOMEM;
}
serio_register_port(q40kbd_port); serio_register_port(q40kbd_port);
printk(KERN_INFO "serio: Q40 kbd registered\n"); printk(KERN_INFO "serio: Q40 kbd registered\n");
...@@ -147,6 +156,7 @@ static int __init q40kbd_init(void) ...@@ -147,6 +156,7 @@ static int __init q40kbd_init(void)
static void __exit q40kbd_exit(void) static void __exit q40kbd_exit(void)
{ {
serio_unregister_port(q40kbd_port); serio_unregister_port(q40kbd_port);
platform_device_unregister(q40kbd_device);
} }
module_init(q40kbd_init); module_init(q40kbd_init);
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/serio.h> #include <linux/serio.h>
#include <linux/err.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/hardware.h> #include <asm/hardware.h>
...@@ -45,6 +46,7 @@ MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver"); ...@@ -45,6 +46,7 @@ MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static struct serio *rpckbd_port; static struct serio *rpckbd_port;
static struct platform_device *rpckbd_device;
static int rpckbd_write(struct serio *port, unsigned char val) static int rpckbd_write(struct serio *port, unsigned char val)
{ {
...@@ -115,10 +117,11 @@ static struct serio * __init rpckbd_allocate_port(void) ...@@ -115,10 +117,11 @@ static struct serio * __init rpckbd_allocate_port(void)
serio = kmalloc(sizeof(struct serio), GFP_KERNEL); serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
if (serio) { if (serio) {
memset(serio, 0, sizeof(struct serio)); memset(serio, 0, sizeof(struct serio));
serio->type = SERIO_8042; serio->type = SERIO_8042;
serio->write = rpckbd_write; serio->write = rpckbd_write;
serio->open = rpckbd_open; serio->open = rpckbd_open;
serio->close = rpckbd_close; serio->close = rpckbd_close;
serio->dev.parent = &rpckbd_device->dev;
strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name)); strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name));
strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys)); strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys));
} }
...@@ -128,8 +131,14 @@ static struct serio * __init rpckbd_allocate_port(void) ...@@ -128,8 +131,14 @@ static struct serio * __init rpckbd_allocate_port(void)
static int __init rpckbd_init(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; return -ENOMEM;
}
serio_register_port(rpckbd_port); serio_register_port(rpckbd_port);
return 0; return 0;
...@@ -138,6 +147,7 @@ static int __init rpckbd_init(void) ...@@ -138,6 +147,7 @@ static int __init rpckbd_init(void)
static void __exit rpckbd_exit(void) static void __exit rpckbd_exit(void)
{ {
serio_unregister_port(rpckbd_port); serio_unregister_port(rpckbd_port);
platform_device_unregister(rpckbd_device);
} }
module_init(rpckbd_init); module_init(rpckbd_init);
......
...@@ -245,7 +245,6 @@ static ssize_t serio_show_description(struct device *dev, char *buf) ...@@ -245,7 +245,6 @@ static ssize_t serio_show_description(struct device *dev, char *buf)
struct serio *serio = to_serio_port(dev); struct serio *serio = to_serio_port(dev);
return sprintf(buf, "%s\n", serio->name); 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) 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 ...@@ -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 serio *serio = to_serio_port(dev);
struct device_driver *drv; struct device_driver *drv;
struct kobject *k;
int retval; int retval;
retval = down_interruptible(&serio_sem); retval = down_interruptible(&serio_sem);
...@@ -271,10 +269,10 @@ static ssize_t serio_rebind_driver(struct device *dev, const char *buf, size_t c ...@@ -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)) { } else if (!strncmp(buf, "rescan", count)) {
serio_disconnect_port(serio); serio_disconnect_port(serio);
serio_connect_port(serio, NULL); serio_connect_port(serio, NULL);
} else if ((k = kset_find_obj(&serio_bus.drivers, buf)) != NULL) { } else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
drv = container_of(k, struct device_driver, kobj);
serio_disconnect_port(serio); serio_disconnect_port(serio);
serio_connect_port(serio, to_serio_driver(drv)); serio_connect_port(serio, to_serio_driver(drv));
put_driver(drv);
} else { } else {
retval = -EINVAL; retval = -EINVAL;
} }
...@@ -283,7 +281,37 @@ static ssize_t serio_rebind_driver(struct device *dev, const char *buf, size_t c ...@@ -283,7 +281,37 @@ static ssize_t serio_rebind_driver(struct device *dev, const char *buf, size_t c
return retval; 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) static void serio_release_port(struct device *dev)
{ {
...@@ -305,8 +333,6 @@ static void serio_create_port(struct serio *serio) ...@@ -305,8 +333,6 @@ static void serio_create_port(struct serio *serio)
if (serio->parent) if (serio->parent)
serio->dev.parent = &serio->parent->dev; serio->dev.parent = &serio->parent->dev;
device_register(&serio->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) ...@@ -350,7 +376,7 @@ static void serio_connect_port(struct serio *serio, struct serio_driver *drv)
if (drv) if (drv)
serio_bind_driver(serio, drv); serio_bind_driver(serio, drv);
else else if (!serio->manual_bind)
serio_find_driver(serio); serio_find_driver(serio);
/* Ok, now bind children, if any */ /* Ok, now bind children, if any */
...@@ -362,13 +388,15 @@ static void serio_connect_port(struct serio *serio, struct serio_driver *drv) ...@@ -362,13 +388,15 @@ static void serio_connect_port(struct serio *serio, struct serio_driver *drv)
serio_create_port(serio); 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 * With children we just _prefer_ passed in driver,
* is not the one * but we will try other options in case preferred
*/ * is not the one
if (!drv || !serio_bind_driver(serio, drv)) */
serio_find_driver(serio); 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 ...@@ -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); struct serio_driver *driver = to_serio_driver(drv);
return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)"); 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) void serio_register_driver(struct serio_driver *drv)
{ {
...@@ -493,7 +551,6 @@ 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; drv->driver.bus = &serio_bus;
driver_register(&drv->driver); driver_register(&drv->driver);
driver_create_file(&drv->driver, &driver_attr_description);
if (drv->manual_bind) if (drv->manual_bind)
goto out; goto out;
...@@ -541,15 +598,14 @@ void serio_unregister_driver(struct serio_driver *drv) ...@@ -541,15 +598,14 @@ void serio_unregister_driver(struct serio_driver *drv)
/* called from serio_driver->connect/disconnect methods under serio_sem */ /* called from serio_driver->connect/disconnect methods under serio_sem */
int serio_open(struct serio *serio, struct serio_driver *drv) int serio_open(struct serio *serio, struct serio_driver *drv)
{ {
unsigned long flags; serio_pause_rx(serio);
spin_lock_irqsave(&serio->lock, flags);
serio->drv = drv; serio->drv = drv;
spin_unlock_irqrestore(&serio->lock, flags); serio_continue_rx(serio);
if (serio->open && serio->open(serio)) { if (serio->open && serio->open(serio)) {
spin_lock_irqsave(&serio->lock, flags); serio_pause_rx(serio);
serio->drv = NULL; serio->drv = NULL;
spin_unlock_irqrestore(&serio->lock, flags); serio_continue_rx(serio);
return -1; return -1;
} }
return 0; return 0;
...@@ -558,13 +614,12 @@ int serio_open(struct serio *serio, struct serio_driver *drv) ...@@ -558,13 +614,12 @@ int serio_open(struct serio *serio, struct serio_driver *drv)
/* called from serio_driver->connect/disconnect methods under serio_sem */ /* called from serio_driver->connect/disconnect methods under serio_sem */
void serio_close(struct serio *serio) void serio_close(struct serio *serio)
{ {
unsigned long flags;
if (serio->close) if (serio->close)
serio->close(serio); serio->close(serio);
spin_lock_irqsave(&serio->lock, flags);
serio_pause_rx(serio);
serio->drv = NULL; serio->drv = NULL;
spin_unlock_irqrestore(&serio->lock, flags); serio_continue_rx(serio);
} }
irqreturn_t serio_interrupt(struct serio *serio, irqreturn_t serio_interrupt(struct serio *serio,
...@@ -599,6 +654,8 @@ static int __init serio_init(void) ...@@ -599,6 +654,8 @@ static int __init serio_init(void)
return -1; return -1;
} }
serio_bus.dev_attrs = serio_device_attrs;
serio_bus.drv_attrs = serio_driver_attrs;
bus_register(&serio_bus); bus_register(&serio_bus);
return 0; return 0;
......
...@@ -19,18 +19,18 @@ ...@@ -19,18 +19,18 @@
/* /*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* *
* Should you need to contact me, the author, you can do so either by * Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <jsimmons@infradead.org>. * e-mail - mail your message to <jsimmons@infradead.org>.
*/ */
...@@ -210,7 +210,7 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, ...@@ -210,7 +210,7 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count,
return -EAGAIN; return -EAGAIN;
retval = wait_event_interruptible(list->tsdev->wait, retval = wait_event_interruptible(list->tsdev->wait,
(list->head != list->tail) && list->tsdev->exist); list->head != list->tail || !list->tsdev->exist);
if (retval) if (retval)
return retval; return retval;
......
...@@ -1529,7 +1529,6 @@ static void __init sunzilog_prepare(void) ...@@ -1529,7 +1529,6 @@ static void __init sunzilog_prepare(void)
static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channel) static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channel)
{ {
int baud, brg; int baud, brg;
struct serio *serio;
if (channel == KEYBOARD_LINE) { if (channel == KEYBOARD_LINE) {
up->flags |= SUNZILOG_FLAG_CONS_KEYB; up->flags |= SUNZILOG_FLAG_CONS_KEYB;
...@@ -1546,8 +1545,15 @@ static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channe ...@@ -1546,8 +1545,15 @@ static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channe
up->curregs[R15] = BRKIE; up->curregs[R15] = BRKIE;
brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
sunzilog_convert_to_zs(up, up->cflag, 0, brg); sunzilog_convert_to_zs(up, up->cflag, 0, brg);
sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS);
__sunzilog_startup(up);
}
#ifdef CONFIG_SERIO #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); up->serio = serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
if (serio) { if (serio) {
...@@ -1576,11 +1582,8 @@ static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channe ...@@ -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", printk(KERN_WARNING "zs%d: not enough memory for serio port\n",
channel); channel);
} }
#endif
sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS);
__sunzilog_startup(up);
} }
#endif
static void __init sunzilog_init_hw(void) static void __init sunzilog_init_hw(void)
{ {
...@@ -1624,6 +1627,11 @@ 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); 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 { ...@@ -27,6 +27,8 @@ struct serio {
char name[32]; char name[32];
char phys[32]; char phys[32];
unsigned int manual_bind;
unsigned short idbus; unsigned short idbus;
unsigned short idvendor; unsigned short idvendor;
unsigned short idproduct; unsigned short idproduct;
...@@ -35,7 +37,7 @@ struct serio { ...@@ -35,7 +37,7 @@ struct serio {
unsigned long type; unsigned long type;
unsigned long event; unsigned long event;
spinlock_t lock; spinlock_t lock; /* protects critical sections from port's interrupt handler */
int (*write)(struct serio *, unsigned char); int (*write)(struct serio *, unsigned char);
int (*open)(struct serio *); int (*open)(struct serio *);
...@@ -43,7 +45,7 @@ struct serio { ...@@ -43,7 +45,7 @@ struct serio {
struct serio *parent, *child; 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; struct device dev;
...@@ -55,7 +57,7 @@ struct serio_driver { ...@@ -55,7 +57,7 @@ struct serio_driver {
void *private; void *private;
char *description; char *description;
int manual_bind; unsigned int manual_bind;
void (*write_wakeup)(struct serio *); void (*write_wakeup)(struct serio *);
irqreturn_t (*interrupt)(struct serio *, unsigned char, irqreturn_t (*interrupt)(struct serio *, unsigned char,
...@@ -81,6 +83,7 @@ void serio_register_port(struct serio *serio); ...@@ -81,6 +83,7 @@ void serio_register_port(struct serio *serio);
void serio_register_port_delayed(struct serio *serio); void serio_register_port_delayed(struct serio *serio);
void serio_unregister_port(struct serio *serio); void serio_unregister_port(struct serio *serio);
void serio_unregister_port_delayed(struct serio *serio); void serio_unregister_port_delayed(struct serio *serio);
void serio_register_driver(struct serio_driver *drv); void serio_register_driver(struct serio_driver *drv);
void serio_unregister_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) ...@@ -104,6 +107,22 @@ static __inline__ void serio_cleanup(struct serio *serio)
serio->drv->cleanup(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 #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