From 3dd01a831133e3260b9c77a09c234b012e9507da Mon Sep 17 00:00:00 2001 From: Vojtech Pavlik Date: Sat, 14 Jan 2006 00:24:06 -0500 Subject: Input: i8042 - add Sony Vaio FSC-115b to MUX blacklist Signed-off-by: Vojtech Pavlik Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-x86ia64io.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 2d2f9fb3ade..a4c6f352272 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -173,6 +173,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"), }, }, + { + .ident = "Sony Vaio FS-115b", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"), + }, + }, { } }; -- cgit v1.2.3 From 940824b0ac6661ff4f3b36e7bce17f681d0cbc23 Mon Sep 17 00:00:00 2001 From: Vojtech Pavlik Date: Sat, 14 Jan 2006 00:25:39 -0500 Subject: Input: HID - add support for Cherry Cymotion keyboard The Cherry Cymotion is a special Linux keyboard made by Cherry, with only one little problem: it doesn't work with Linux. This patch (originally by hexten.net, cleaned up by me) makes it work including all the special keys. Signed-off-by: Vojtech Pavlik Signed-off-by: Dmitry Torokhov --- drivers/usb/input/hid-core.c | 22 ++++++++++++++++++++++ drivers/usb/input/hid-input.c | 8 ++++++++ drivers/usb/input/hid.h | 1 + 3 files changed, 31 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 5f52979af1c..1a2d20303ed 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -1450,6 +1450,9 @@ void hid_init_reports(struct hid_device *hid) #define USB_VENDOR_ID_APPLE 0x05ac #define USB_DEVICE_ID_APPLE_POWERMOUSE 0x0304 +#define USB_VENDOR_ID_CHERRY 0x046a +#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 + /* * Alphabetically sorted blacklist by quirk type. */ @@ -1580,6 +1583,8 @@ static const struct hid_blacklist { { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION }, + { 0, 0 } }; @@ -1626,6 +1631,20 @@ static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) usb_buffer_free(dev, hid->bufsize, hid->ctrlbuf, hid->ctrlbuf_dma); } +/* + * Cherry Cymotion keyboard have an invalid HID report descriptor, + * that needs fixing before we can parse it. + */ + +static void hid_fixup_cymotion_descriptor(char *rdesc, int rsize) +{ + if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) { + info("Fixing up Cherry Cymotion report descriptor"); + rdesc[11] = rdesc[16] = 0xff; + rdesc[12] = rdesc[17] = 0x03; + } +} + static struct hid_device *usb_hid_configure(struct usb_interface *intf) { struct usb_host_interface *interface = intf->cur_altsetting; @@ -1673,6 +1692,9 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) return NULL; } + if ((quirks & HID_QUIRK_CYMOTION)) + hid_fixup_cymotion_descriptor(rdesc, rsize); + #ifdef DEBUG_DATA printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); for (n = 0; n < rsize; n++) diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c index 192a03b2897..9e5a80bbd16 100644 --- a/drivers/usb/input/hid-input.c +++ b/drivers/usb/input/hid-input.c @@ -289,11 +289,19 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x226: map_key_clear(KEY_STOP); break; case 0x227: map_key_clear(KEY_REFRESH); break; case 0x22a: map_key_clear(KEY_BOOKMARKS); break; + case 0x233: map_key_clear(KEY_SCROLLUP); break; + case 0x234: map_key_clear(KEY_SCROLLDOWN); break; case 0x238: map_rel(REL_HWHEEL); break; case 0x279: map_key_clear(KEY_REDO); break; case 0x289: map_key_clear(KEY_REPLY); break; case 0x28b: map_key_clear(KEY_FORWARDMAIL); break; case 0x28c: map_key_clear(KEY_SEND); break; + + /* Reported on a Cherry Cymotion keyboard */ + case 0x301: map_key_clear(KEY_PROG1); break; + case 0x302: map_key_clear(KEY_PROG2); break; + case 0x303: map_key_clear(KEY_PROG3); break; + default: goto ignore; } break; diff --git a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h index ee48a227610..335316c7233 100644 --- a/drivers/usb/input/hid.h +++ b/drivers/usb/input/hid.h @@ -246,6 +246,7 @@ struct hid_item { #define HID_QUIRK_2WHEEL_MOUSE_HACK_5 0x100 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x200 #define HID_QUIRK_2WHEEL_POWERMOUSE 0x400 +#define HID_QUIRK_CYMOTION 0x800 /* * This is the global environment of the parser. This information is -- cgit v1.2.3 From b65d0d1bacfdbb4a134a7ebd16f280d5d42241cb Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 14 Jan 2006 00:26:15 -0500 Subject: Input: HID - fix an oops in PID initialization code Signed-off-by: Dmitry Torokhov --- drivers/usb/input/pid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/input/pid.c b/drivers/usb/input/pid.c index 19e015d171a..d9d9f656b8c 100644 --- a/drivers/usb/input/pid.c +++ b/drivers/usb/input/pid.c @@ -259,7 +259,7 @@ static int hid_pid_upload_effect(struct input_dev *dev, int hid_pid_init(struct hid_device *hid) { struct hid_ff_pid *private; - struct hid_input *hidinput = list_entry(&hid->inputs, struct hid_input, list); + struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); struct input_dev *input_dev = hidinput->input; private = hid->ff_private = kzalloc(sizeof(struct hid_ff_pid), GFP_KERNEL); -- cgit v1.2.3 From f0d5c6f419d3a10443f66d6835855837eae4ac4b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 14 Jan 2006 00:27:37 -0500 Subject: Input: psmouse - attempt to re-synchronize mouse every 5 seconds This should help driver to deal vith KVMs that reset mice when switching between boxes. Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 38 +++++ drivers/input/mouse/logips2pp.c | 2 +- drivers/input/mouse/psmouse-base.c | 316 +++++++++++++++++++++++++++++-------- drivers/input/mouse/psmouse.h | 9 +- drivers/input/mouse/synaptics.c | 2 + 5 files changed, 301 insertions(+), 66 deletions(-) (limited to 'drivers') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 24474335dfd..2141501e9f2 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -348,6 +348,40 @@ static int alps_tap_mode(struct psmouse *psmouse, int enable) return 0; } +/* + * alps_poll() - poll the touchpad for current motion packet. + * Used in resync. + */ +static int alps_poll(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + unsigned char buf[6]; + int poll_failed; + + if (priv->i->flags & ALPS_PASS) + alps_passthrough_mode(psmouse, 1); + + poll_failed = ps2_command(&psmouse->ps2dev, buf, + PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0; + + if (priv->i->flags & ALPS_PASS) + alps_passthrough_mode(psmouse, 0); + + if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0) + return -1; + + if ((psmouse->badbyte & 0xc8) == 0x08) { +/* + * Poll the track stick ... + */ + if (ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (3 << 8))) + return -1; + } + + memcpy(psmouse->packet, buf, sizeof(buf)); + return 0; +} + static int alps_reconnect(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; @@ -451,10 +485,14 @@ int alps_init(struct psmouse *psmouse) input_register_device(priv->dev2); psmouse->protocol_handler = alps_process_byte; + psmouse->poll = alps_poll; psmouse->disconnect = alps_disconnect; psmouse->reconnect = alps_reconnect; psmouse->pktsize = 6; + /* We are having trouble resyncing ALPS touchpads so disable it for now */ + psmouse->resync_time = 0; + return 0; init_fail: diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index 025a71de540..c88520d3d13 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -117,7 +117,7 @@ static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned cha if (psmouse_sliced_command(psmouse, command)) return -1; - if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL)) + if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300)) return -1; return 0; diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 4d5ecc04c5b..7665fd9ce55 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -54,10 +54,14 @@ static unsigned int psmouse_smartscroll = 1; module_param_named(smartscroll, psmouse_smartscroll, bool, 0644); MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled."); -static unsigned int psmouse_resetafter; +static unsigned int psmouse_resetafter = 5; module_param_named(resetafter, psmouse_resetafter, uint, 0644); MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never)."); +static unsigned int psmouse_resync_time = 5; +module_param_named(resync_time, psmouse_resync_time, uint, 0644); +MODULE_PARM_DESC(resync_time, "How long can mouse stay idle before forcing resync (in seconds, 0 = never)."); + PSMOUSE_DEFINE_ATTR(protocol, S_IWUSR | S_IRUGO, NULL, psmouse_attr_show_protocol, psmouse_attr_set_protocol); @@ -70,12 +74,16 @@ PSMOUSE_DEFINE_ATTR(resolution, S_IWUSR | S_IRUGO, PSMOUSE_DEFINE_ATTR(resetafter, S_IWUSR | S_IRUGO, (void *) offsetof(struct psmouse, resetafter), psmouse_show_int_attr, psmouse_set_int_attr); +PSMOUSE_DEFINE_ATTR(resync_time, S_IWUSR | S_IRUGO, + (void *) offsetof(struct psmouse, resync_time), + psmouse_show_int_attr, psmouse_set_int_attr); static struct attribute *psmouse_attributes[] = { &psmouse_attr_protocol.dattr.attr, &psmouse_attr_rate.dattr.attr, &psmouse_attr_resolution.dattr.attr, &psmouse_attr_resetafter.dattr.attr, + &psmouse_attr_resync_time.dattr.attr, NULL }; @@ -98,6 +106,8 @@ __obsolete_setup("psmouse_rate="); */ static DECLARE_MUTEX(psmouse_sem); +static struct workqueue_struct *kpsmoused_wq; + struct psmouse_protocol { enum psmouse_type type; char *name; @@ -178,15 +188,79 @@ static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse, struct pt_reg } /* - * psmouse_interrupt() handles incoming characters, either gathering them into - * packets or passing them to the command routine as command output. + * __psmouse_set_state() sets new psmouse state and resets all flags. + */ + +static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) +{ + psmouse->state = new_state; + psmouse->pktcnt = psmouse->out_of_sync = 0; + psmouse->ps2dev.flags = 0; + psmouse->last = jiffies; +} + + +/* + * 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->ps2dev.serio); + __psmouse_set_state(psmouse, new_state); + serio_continue_rx(psmouse->ps2dev.serio); +} + +/* + * psmouse_handle_byte() processes one byte of the input data stream + * by calling corresponding protocol handler. + */ + +static int psmouse_handle_byte(struct psmouse *psmouse, struct pt_regs *regs) +{ + psmouse_ret_t rc = psmouse->protocol_handler(psmouse, regs); + + switch (rc) { + case PSMOUSE_BAD_DATA: + if (psmouse->state == PSMOUSE_ACTIVATED) { + printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n", + psmouse->name, psmouse->phys, psmouse->pktcnt); + if (++psmouse->out_of_sync == psmouse->resetafter) { + __psmouse_set_state(psmouse, PSMOUSE_IGNORE); + printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n"); + serio_reconnect(psmouse->ps2dev.serio); + return -1; + } + } + psmouse->pktcnt = 0; + break; + + case PSMOUSE_FULL_PACKET: + psmouse->pktcnt = 0; + if (psmouse->out_of_sync) { + psmouse->out_of_sync = 0; + printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n", + psmouse->name, psmouse->phys); + } + break; + + case PSMOUSE_GOOD_DATA: + break; + } + return 0; +} + +/* + * psmouse_interrupt() handles incoming characters, either passing them + * for normal processing or gathering them as command response. */ static irqreturn_t psmouse_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs) { struct psmouse *psmouse = serio_get_drvdata(serio); - psmouse_ret_t rc; if (psmouse->state == PSMOUSE_IGNORE) goto out; @@ -208,67 +282,58 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, if (ps2_handle_response(&psmouse->ps2dev, data)) goto out; - if (psmouse->state == PSMOUSE_INITIALIZING) + if (psmouse->state <= PSMOUSE_RESYNCING) 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", + printk(KERN_INFO "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n", psmouse->name, psmouse->phys, psmouse->pktcnt); - psmouse->pktcnt = 0; + psmouse->badbyte = psmouse->packet[0]; + __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); + queue_work(kpsmoused_wq, &psmouse->resync_work); + goto out; } - psmouse->last = jiffies; psmouse->packet[psmouse->pktcnt++] = data; - - if (psmouse->packet[0] == PSMOUSE_RET_BAT) { +/* + * Check if this is a new device announcement (0xAA 0x00) + */ + if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) { if (psmouse->pktcnt == 1) goto out; - if (psmouse->pktcnt == 2) { - if (psmouse->packet[1] == PSMOUSE_RET_ID) { - psmouse->state = PSMOUSE_IGNORE; - serio_reconnect(serio); - goto out; - } - if (psmouse->type == PSMOUSE_SYNAPTICS) { - /* neither 0xAA nor 0x00 are valid first bytes - * for a packet in absolute mode - */ - psmouse->pktcnt = 0; - goto out; - } + if (psmouse->packet[1] == PSMOUSE_RET_ID) { + __psmouse_set_state(psmouse, PSMOUSE_IGNORE); + serio_reconnect(serio); + goto out; } - } - - rc = psmouse->protocol_handler(psmouse, regs); +/* + * Not a new device, try processing first byte normally + */ + psmouse->pktcnt = 1; + if (psmouse_handle_byte(psmouse, regs)) + goto out; - switch (rc) { - case PSMOUSE_BAD_DATA: - printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n", - psmouse->name, psmouse->phys, psmouse->pktcnt); - psmouse->pktcnt = 0; + psmouse->packet[psmouse->pktcnt++] = data; + } - if (++psmouse->out_of_sync == psmouse->resetafter) { - psmouse->state = PSMOUSE_IGNORE; - printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n"); - serio_reconnect(psmouse->ps2dev.serio); - } - break; +/* + * See if we need to force resync because mouse was idle for too long + */ + if (psmouse->state == PSMOUSE_ACTIVATED && + psmouse->pktcnt == 1 && psmouse->resync_time && + time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) { + psmouse->badbyte = psmouse->packet[0]; + __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); + queue_work(kpsmoused_wq, &psmouse->resync_work); + goto out; + } - case PSMOUSE_FULL_PACKET: - psmouse->pktcnt = 0; - if (psmouse->out_of_sync) { - psmouse->out_of_sync = 0; - printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n", - psmouse->name, psmouse->phys); - } - break; + psmouse->last = jiffies; + psmouse_handle_byte(psmouse, regs); - case PSMOUSE_GOOD_DATA: - break; - } -out: + out: return IRQ_HANDLED; } @@ -751,21 +816,6 @@ static void psmouse_initialize(struct psmouse *psmouse) } } -/* - * 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->ps2dev.serio); - psmouse->state = new_state; - psmouse->pktcnt = psmouse->out_of_sync = 0; - psmouse->ps2dev.flags = 0; - serio_continue_rx(psmouse->ps2dev.serio); -} - /* * psmouse_activate() enables the mouse so that we get motion reports from it. */ @@ -794,6 +844,111 @@ static void psmouse_deactivate(struct psmouse *psmouse) psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); } +/* + * psmouse_poll() - default poll hanlder. Everyone except for ALPS uses it. + */ + +static int psmouse_poll(struct psmouse *psmouse) +{ + return ps2_command(&psmouse->ps2dev, psmouse->packet, + PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); +} + + +/* + * psmouse_resync() attempts to re-validate current protocol. + */ + +static void psmouse_resync(void *p) +{ + struct psmouse *psmouse = p, *parent = NULL; + struct serio *serio = psmouse->ps2dev.serio; + psmouse_ret_t rc = PSMOUSE_GOOD_DATA; + int failed = 0, enabled = 0; + int i; + + down(&psmouse_sem); + + if (psmouse->state != PSMOUSE_RESYNCING) + goto out; + + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = serio_get_drvdata(serio->parent); + psmouse_deactivate(parent); + } + +/* + * Some mice don't ACK commands sent while they are in the middle of + * transmitting motion packet. To avoid delay we use ps2_sendbyte() + * instead of ps2_command() which would wait for 200ms for an ACK + * that may never come. + * As an additional quirk ALPS touchpads may not only forget to ACK + * disable command but will stop reporting taps, so if we see that + * mouse at least once ACKs disable we will do full reconnect if ACK + * is missing. + */ + psmouse->num_resyncs++; + + if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) { + if (psmouse->num_resyncs < 3 || psmouse->acks_disable_command) + failed = 1; + } else + psmouse->acks_disable_command = 1; + +/* + * Poll the mouse. If it was reset the packet will be shorter than + * psmouse->pktsize and ps2_command will fail. We do not expect and + * do not handle scenario when mouse "upgrades" its protocol while + * disconnected since it would require additional delay. If we ever + * see a mouse that does it we'll adjust the code. + */ + if (!failed) { + if (psmouse->poll(psmouse)) + failed = 1; + else { + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + for (i = 0; i < psmouse->pktsize; i++) { + psmouse->pktcnt++; + rc = psmouse->protocol_handler(psmouse, NULL); + if (rc != PSMOUSE_GOOD_DATA) + break; + } + if (rc != PSMOUSE_FULL_PACKET) + failed = 1; + psmouse_set_state(psmouse, PSMOUSE_RESYNCING); + } + } +/* + * Now try to enable mouse. We try to do that even if poll failed and also + * repeat our attempts 5 times, otherwise we may be left out with disabled + * mouse. + */ + for (i = 0; i < 5; i++) { + if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { + enabled = 1; + break; + } + msleep(200); + } + + if (!enabled) { + printk(KERN_WARNING "psmouse.c: failed to re-enable mouse on %s\n", + psmouse->ps2dev.serio->phys); + failed = 1; + } + + if (failed) { + psmouse_set_state(psmouse, PSMOUSE_IGNORE); + printk(KERN_INFO "psmouse.c: resync failed, issuing reconnect request\n"); + serio_reconnect(serio); + } else + psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + + if (parent) + psmouse_activate(parent); + out: + up(&psmouse_sem); +} /* * psmouse_cleanup() resets the mouse into power-on state. @@ -822,6 +977,11 @@ static void psmouse_disconnect(struct serio *serio) psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + /* make sure we don't have a resync in progress */ + up(&psmouse_sem); + flush_workqueue(kpsmoused_wq); + down(&psmouse_sem); + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); psmouse_deactivate(parent); @@ -859,6 +1019,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, struct psmouse_proto psmouse->set_rate = psmouse_set_rate; psmouse->set_resolution = psmouse_set_resolution; + psmouse->poll = psmouse_poll; psmouse->protocol_handler = psmouse_process_byte; psmouse->pktsize = 3; @@ -874,6 +1035,23 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, struct psmouse_proto else psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1); + /* + * If mouse's packet size is 3 there is no point in polling the + * device in hopes to detect protocol reset - we won't get less + * than 3 bytes response anyhow. + */ + if (psmouse->pktsize == 3) + psmouse->resync_time = 0; + + /* + * Some smart KVMs fake response to POLL command returning just + * 3 bytes and messing up our resync logic, so if initial poll + * fails we won't try polling the device anymore. Hopefully + * such KVM will maintain initially selected protocol. + */ + if (psmouse->resync_time && psmouse->poll(psmouse)) + psmouse->resync_time = 0; + sprintf(psmouse->devname, "%s %s %s", psmouse_protocol_by_type(psmouse->type)->name, psmouse->vendor, psmouse->name); @@ -914,6 +1092,7 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) goto out; ps2_init(&psmouse->ps2dev, serio); + INIT_WORK(&psmouse->resync_work, psmouse_resync, psmouse); psmouse->dev = input_dev; sprintf(psmouse->phys, "%s/input0", serio->phys); @@ -934,6 +1113,7 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) psmouse->rate = psmouse_rate; psmouse->resolution = psmouse_resolution; psmouse->resetafter = psmouse_resetafter; + psmouse->resync_time = parent ? 0 : psmouse_resync_time; psmouse->smartscroll = psmouse_smartscroll; psmouse_switch_protocol(psmouse, NULL); @@ -1278,13 +1458,21 @@ static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp) static int __init psmouse_init(void) { + kpsmoused_wq = create_singlethread_workqueue("kpsmoused"); + if (!kpsmoused_wq) { + printk(KERN_ERR "psmouse: failed to create kpsmoused workqueue\n"); + return -ENOMEM; + } + serio_register_driver(&psmouse_drv); + return 0; } static void __exit psmouse_exit(void) { serio_unregister_driver(&psmouse_drv); + destroy_workqueue(kpsmoused_wq); } module_init(psmouse_init); diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 7c4192bd127..4d9107fba6a 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -7,7 +7,7 @@ #define PSMOUSE_CMD_GETINFO 0x03e9 #define PSMOUSE_CMD_SETSTREAM 0x00ea #define PSMOUSE_CMD_SETPOLL 0x00f0 -#define PSMOUSE_CMD_POLL 0x03eb +#define PSMOUSE_CMD_POLL 0x00eb /* caller sets number of bytes to receive */ #define PSMOUSE_CMD_GETID 0x02f2 #define PSMOUSE_CMD_SETRATE 0x10f3 #define PSMOUSE_CMD_ENABLE 0x00f4 @@ -23,6 +23,7 @@ enum psmouse_state { PSMOUSE_IGNORE, PSMOUSE_INITIALIZING, + PSMOUSE_RESYNCING, PSMOUSE_CMD_MODE, PSMOUSE_ACTIVATED, }; @@ -38,15 +39,19 @@ struct psmouse { void *private; struct input_dev *dev; struct ps2dev ps2dev; + struct work_struct resync_work; char *vendor; char *name; unsigned char packet[8]; + unsigned char badbyte; unsigned char pktcnt; unsigned char pktsize; unsigned char type; + unsigned char acks_disable_command; unsigned int model; unsigned long last; unsigned long out_of_sync; + unsigned long num_resyncs; enum psmouse_state state; char devname[64]; char phys[32]; @@ -54,6 +59,7 @@ struct psmouse { unsigned int rate; unsigned int resolution; unsigned int resetafter; + unsigned int resync_time; unsigned int smartscroll; /* Logitech only */ psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs); @@ -62,6 +68,7 @@ struct psmouse { int (*reconnect)(struct psmouse *psmouse); void (*disconnect)(struct psmouse *psmouse); + int (*poll)(struct psmouse *psmouse); void (*pt_activate)(struct psmouse *psmouse); void (*pt_deactivate)(struct psmouse *psmouse); diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 97cdfd6acac..2051bec2c39 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -652,6 +652,8 @@ int synaptics_init(struct psmouse *psmouse) psmouse->disconnect = synaptics_disconnect; psmouse->reconnect = synaptics_reconnect; psmouse->pktsize = 6; + /* Synaptics can usually stay in sync without extra help */ + psmouse->resync_time = 0; if (SYN_CAP_PASS_THROUGH(priv->capabilities)) synaptics_pt_create(psmouse); -- cgit v1.2.3 From 5fce9d7bc5f485358263cf3d895e90c974449e1f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 14 Jan 2006 00:27:51 -0500 Subject: Input: HID - add more simulation usages Signed-off-by: Dmitry Torokhov --- drivers/usb/input/hid-input.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c index 9e5a80bbd16..36a08c04c46 100644 --- a/drivers/usb/input/hid-input.c +++ b/drivers/usb/input/hid-input.c @@ -135,8 +135,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_UP_SIMULATION: switch (usage->hid & 0xffff) { - case 0xba: map_abs(ABS_RUDDER); break; + case 0xba: map_abs(ABS_RUDDER); break; case 0xbb: map_abs(ABS_THROTTLE); break; + case 0xc4: map_abs(ABS_GAS); break; + case 0xc5: map_abs(ABS_BRAKE); break; + case 0xc8: map_abs(ABS_WHEEL); break; default: goto ignore; } break; -- cgit v1.2.3 From 1e27ffd4d7d39783c5196daa2584cca5785d1f95 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 14 Jan 2006 00:28:04 -0500 Subject: Input: wacom - fix compile on PowerPC Rename G4 (new Graphire4) to WACOM_G4 to avoid clashes on PowerPC Signed-off-by: Dmitry Torokhov --- drivers/usb/input/wacom.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c index 48df4cfd5a4..d3e15df9e81 100644 --- a/drivers/usb/input/wacom.c +++ b/drivers/usb/input/wacom.c @@ -95,7 +95,7 @@ MODULE_LICENSE(DRIVER_LICENSE); enum { PENPARTNER = 0, GRAPHIRE, - G4, + WACOM_G4, PL, INTUOS, INTUOS3, @@ -373,7 +373,7 @@ static void wacom_graphire_irq(struct urb *urb, struct pt_regs *regs) case 2: /* Mouse with wheel */ input_report_key(dev, BTN_MIDDLE, data[1] & 0x04); - if (wacom->features->type == G4) { + if (wacom->features->type == WACOM_G4) { rw = data[7] & 0x04 ? -(data[7] & 0x03) : (data[7] & 0x03); input_report_rel(dev, REL_WHEEL, rw); } else @@ -385,7 +385,7 @@ static void wacom_graphire_irq(struct urb *urb, struct pt_regs *regs) id = CURSOR_DEVICE_ID; input_report_key(dev, BTN_LEFT, data[1] & 0x01); input_report_key(dev, BTN_RIGHT, data[1] & 0x02); - if (wacom->features->type == G4) + if (wacom->features->type == WACOM_G4) input_report_abs(dev, ABS_DISTANCE, data[6]); else input_report_abs(dev, ABS_DISTANCE, data[7]); @@ -410,7 +410,7 @@ static void wacom_graphire_irq(struct urb *urb, struct pt_regs *regs) input_sync(dev); /* send pad data */ - if (wacom->features->type == G4) { + if (wacom->features->type == WACOM_G4) { /* fist time sending pad data */ if (wacom->tool[1] != BTN_TOOL_FINGER) { wacom->id[1] = 0; @@ -713,8 +713,8 @@ static struct wacom_features wacom_features[] = { { "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 32, GRAPHIRE, wacom_graphire_irq }, { "Wacom Graphire3", 8, 10208, 7424, 511, 32, GRAPHIRE, wacom_graphire_irq }, { "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 32, GRAPHIRE, wacom_graphire_irq }, - { "Wacom Graphire4 4x5", 8, 10208, 7424, 511, 32, G4, wacom_graphire_irq }, - { "Wacom Graphire4 6x8", 8, 16704, 12064, 511, 32, G4, wacom_graphire_irq }, + { "Wacom Graphire4 4x5", 8, 10208, 7424, 511, 32, WACOM_G4, wacom_graphire_irq }, + { "Wacom Graphire4 6x8", 8, 16704, 12064, 511, 32, WACOM_G4, wacom_graphire_irq }, { "Wacom Volito", 8, 5104, 3712, 511, 32, GRAPHIRE, wacom_graphire_irq }, { "Wacom PenStation2", 8, 3250, 2320, 255, 32, GRAPHIRE, wacom_graphire_irq }, { "Wacom Volito2 4x5", 8, 5104, 3712, 511, 32, GRAPHIRE, wacom_graphire_irq }, @@ -859,7 +859,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i input_set_abs_params(input_dev, ABS_PRESSURE, 0, wacom->features->pressure_max, 0, 0); switch (wacom->features->type) { - case G4: + case WACOM_G4: input_dev->evbit[0] |= BIT(EV_MSC); input_dev->mscbit[0] |= BIT(MSC_SERIAL); input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER); -- cgit v1.2.3 From eab9edd27f7ceaad6b57085817d63287bda15190 Mon Sep 17 00:00:00 2001 From: Michael Hanselmann Date: Sat, 14 Jan 2006 10:08:06 -0500 Subject: Input: HID - add support for fn key on Apple PowerBooks This patch implements support for the fn key on Apple PowerBooks using USB based keyboards and makes them behave like their ADB counterparts. Signed-off-by: Michael Hanselmann Acked-by: Rene Nussbaumer Acked-by: Johannes Berg Acked-by: Benjamin Herrenschmidt Acked-by: Vojtech Pavlik Signed-off-by: Dmitry Torokhov --- drivers/usb/input/Kconfig | 10 +++ drivers/usb/input/hid-core.c | 8 ++ drivers/usb/input/hid-input.c | 166 +++++++++++++++++++++++++++++++++++++++++- drivers/usb/input/hid.h | 31 +++++--- 4 files changed, 201 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig index 509dd0a04c5..5246b35301d 100644 --- a/drivers/usb/input/Kconfig +++ b/drivers/usb/input/Kconfig @@ -37,6 +37,16 @@ config USB_HIDINPUT If unsure, say Y. +config USB_HIDINPUT_POWERBOOK + bool "Enable support for iBook/PowerBook special keys" + default n + depends on USB_HIDINPUT + help + Say Y here if you want support for the special keys (Fn, Numlock) on + Apple iBooks and PowerBooks. + + If unsure, say N. + config HID_FF bool "Force feedback support (EXPERIMENTAL)" depends on USB_HIDINPUT && EXPERIMENTAL diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 1a2d20303ed..a91e72c4141 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -1585,6 +1585,14 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION }, + { USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN }, + { USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN }, + { USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN }, + { USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN }, + { USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN }, + { USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN }, + { USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN }, + { 0, 0 } }; diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c index 36a08c04c46..cb0d80f4925 100644 --- a/drivers/usb/input/hid-input.c +++ b/drivers/usb/input/hid-input.c @@ -73,6 +73,160 @@ static const struct { #define map_key_clear(c) do { map_key(c); clear_bit(c, bit); } while (0) #define map_ff_effect(c) do { set_bit(c, input->ffbit); } while (0) +#ifdef CONFIG_USB_HIDINPUT_POWERBOOK + +struct hidinput_key_translation { + u16 from; + u16 to; + u8 flags; +}; + +#define POWERBOOK_FLAG_FKEY 0x01 + +static struct hidinput_key_translation powerbook_fn_keys[] = { + { KEY_BACKSPACE, KEY_DELETE }, + { KEY_F1, KEY_BRIGHTNESSDOWN, POWERBOOK_FLAG_FKEY }, + { KEY_F2, KEY_BRIGHTNESSUP, POWERBOOK_FLAG_FKEY }, + { KEY_F3, KEY_MUTE, POWERBOOK_FLAG_FKEY }, + { KEY_F4, KEY_VOLUMEDOWN, POWERBOOK_FLAG_FKEY }, + { KEY_F5, KEY_VOLUMEUP, POWERBOOK_FLAG_FKEY }, + { KEY_F6, KEY_NUMLOCK, POWERBOOK_FLAG_FKEY }, + { KEY_F7, KEY_SWITCHVIDEOMODE, POWERBOOK_FLAG_FKEY }, + { KEY_F8, KEY_KBDILLUMTOGGLE, POWERBOOK_FLAG_FKEY }, + { KEY_F9, KEY_KBDILLUMDOWN, POWERBOOK_FLAG_FKEY }, + { KEY_F10, KEY_KBDILLUMUP, POWERBOOK_FLAG_FKEY }, + { KEY_UP, KEY_PAGEUP }, + { KEY_DOWN, KEY_PAGEDOWN }, + { KEY_LEFT, KEY_HOME }, + { KEY_RIGHT, KEY_END }, + { } +}; + +static struct hidinput_key_translation powerbook_numlock_keys[] = { + { KEY_J, KEY_KP1 }, + { KEY_K, KEY_KP2 }, + { KEY_L, KEY_KP3 }, + { KEY_U, KEY_KP4 }, + { KEY_I, KEY_KP5 }, + { KEY_O, KEY_KP6 }, + { KEY_7, KEY_KP7 }, + { KEY_8, KEY_KP8 }, + { KEY_9, KEY_KP9 }, + { KEY_M, KEY_KP0 }, + { KEY_DOT, KEY_KPDOT }, + { KEY_SLASH, KEY_KPPLUS }, + { KEY_SEMICOLON, KEY_KPMINUS }, + { KEY_P, KEY_KPASTERISK }, + { KEY_MINUS, KEY_KPEQUAL }, + { KEY_0, KEY_KPSLASH }, + { KEY_F6, KEY_NUMLOCK }, + { KEY_KPENTER, KEY_KPENTER }, + { KEY_BACKSPACE, KEY_BACKSPACE }, + { } +}; + +static int usbhid_pb_fnmode = 1; +module_param_named(pb_fnmode, usbhid_pb_fnmode, int, 0644); +MODULE_PARM_DESC(pb_fnmode, + "Mode of fn key on PowerBooks (0 = disabled, 1 = fkeyslast, 2 = fkeysfirst)"); + +static struct hidinput_key_translation *find_translation(struct hidinput_key_translation *table, u16 from) +{ + struct hidinput_key_translation *trans; + + /* Look for the translation */ + for (trans = table; trans->from; trans++) + if (trans->from == from) + return trans; + + return NULL; +} + +static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input, + struct hid_usage *usage, __s32 value) +{ + struct hidinput_key_translation *trans; + + if (usage->code == KEY_FN) { + if (value) hid->quirks |= HID_QUIRK_POWERBOOK_FN_ON; + else hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON; + + input_event(input, usage->type, usage->code, value); + + return 1; + } + + if (usbhid_pb_fnmode) { + int do_translate; + + trans = find_translation(powerbook_fn_keys, usage->code); + if (trans) { + if (test_bit(usage->code, hid->pb_pressed_fn)) + do_translate = 1; + else if (trans->flags & POWERBOOK_FLAG_FKEY) + do_translate = + (usbhid_pb_fnmode == 2 && (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) || + (usbhid_pb_fnmode == 1 && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)); + else + do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON); + + if (do_translate) { + if (value) + set_bit(usage->code, hid->pb_pressed_fn); + else + clear_bit(usage->code, hid->pb_pressed_fn); + + input_event(input, usage->type, trans->to, value); + + return 1; + } + } + + if (test_bit(usage->code, hid->pb_pressed_numlock) || + test_bit(LED_NUML, input->led)) { + trans = find_translation(powerbook_numlock_keys, usage->code); + + if (trans) { + if (value) + set_bit(usage->code, hid->pb_pressed_numlock); + else + clear_bit(usage->code, hid->pb_pressed_numlock); + + input_event(input, usage->type, trans->to, value); + } + + return 1; + } + } + + return 0; +} + +static void hidinput_pb_setup(struct input_dev *input) +{ + struct hidinput_key_translation *trans; + + set_bit(KEY_NUMLOCK, input->keybit); + + /* Enable all needed keys */ + for (trans = powerbook_fn_keys; trans->from; trans++) + set_bit(trans->to, input->keybit); + + for (trans = powerbook_numlock_keys; trans->from; trans++) + set_bit(trans->to, input->keybit); +} +#else +static inline int hidinput_pb_event(struct hid_device *hid, struct input_dev *input, + struct hid_usage *usage, __s32 value) +{ + return 0; +} + +static inline void hidinput_pb_setup(struct input_dev *input) +{ +} +#endif + static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage) { @@ -336,7 +490,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel set_bit(EV_REP, input->evbit); switch(usage->hid & HID_USAGE) { - case 0x003: map_key_clear(KEY_FN); break; + case 0x003: + /* The fn key on Apple PowerBooks */ + map_key_clear(KEY_FN); + hidinput_pb_setup(input); + break; + default: goto ignore; } break; @@ -493,6 +652,9 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } + if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) && hidinput_pb_event(hid, input, usage, value)) + return; + if (usage->hat_min < usage->hat_max || usage->hat_dir) { int hat_dir = usage->hat_dir; if (!hat_dir) @@ -535,7 +697,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } - if((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */ + if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */ return; input_event(input, usage->type, usage->code, value); diff --git a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h index 335316c7233..8b0d4346ce9 100644 --- a/drivers/usb/input/hid.h +++ b/drivers/usb/input/hid.h @@ -235,18 +235,20 @@ struct hid_item { * HID device quirks. */ -#define HID_QUIRK_INVERT 0x001 -#define HID_QUIRK_NOTOUCH 0x002 -#define HID_QUIRK_IGNORE 0x004 -#define HID_QUIRK_NOGET 0x008 -#define HID_QUIRK_HIDDEV 0x010 -#define HID_QUIRK_BADPAD 0x020 -#define HID_QUIRK_MULTI_INPUT 0x040 -#define HID_QUIRK_2WHEEL_MOUSE_HACK_7 0x080 -#define HID_QUIRK_2WHEEL_MOUSE_HACK_5 0x100 -#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x200 -#define HID_QUIRK_2WHEEL_POWERMOUSE 0x400 -#define HID_QUIRK_CYMOTION 0x800 +#define HID_QUIRK_INVERT 0x00000001 +#define HID_QUIRK_NOTOUCH 0x00000002 +#define HID_QUIRK_IGNORE 0x00000004 +#define HID_QUIRK_NOGET 0x00000008 +#define HID_QUIRK_HIDDEV 0x00000010 +#define HID_QUIRK_BADPAD 0x00000020 +#define HID_QUIRK_MULTI_INPUT 0x00000040 +#define HID_QUIRK_2WHEEL_MOUSE_HACK_7 0x00000080 +#define HID_QUIRK_2WHEEL_MOUSE_HACK_5 0x00000100 +#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x00000200 +#define HID_QUIRK_2WHEEL_POWERMOUSE 0x00000400 +#define HID_QUIRK_CYMOTION 0x00000800 +#define HID_QUIRK_POWERBOOK_HAS_FN 0x00001000 +#define HID_QUIRK_POWERBOOK_FN_ON 0x00002000 /* * This is the global environment of the parser. This information is @@ -432,6 +434,11 @@ struct hid_device { /* device report descriptor */ void (*ff_exit)(struct hid_device*); /* Called by hid_exit_ff(hid) */ int (*ff_event)(struct hid_device *hid, struct input_dev *input, unsigned int type, unsigned int code, int value); + +#ifdef CONFIG_USB_HIDINPUT_POWERBOOK + unsigned long pb_pressed_fn[NBITS(KEY_MAX)]; + unsigned long pb_pressed_numlock[NBITS(KEY_MAX)]; +#endif }; #define HID_GLOBAL_STACK_SIZE 4 -- cgit v1.2.3