From 16bf288c4be67b68c3fcb6561ff145702cb7bd22 Mon Sep 17 00:00:00 2001 From: Chris Bagwell Date: Sun, 25 Mar 2012 23:26:20 -0700 Subject: [PATCH] Input: wacom - create inputs when wireless connect When a tablet connect or disconnect is detected, schedule work queue to register or unregister related input devices. When a wireless tablet connects, it reports same USB PID used if tablet is connected with USB cable. Use this to update features values, set input capabilities, and then register device. From there, the Pen and Touch interfaces will reuse the existing tablet's IRQ routines. Its possible that 1 receiver is shared with 2 tablets with different PID (small and medium Bamboo for example) so the input is unregister at disconnect to better support this case. Signed-off-by: Chris Bagwell Tested-by: Jason Gerecke Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom.h | 7 +++ drivers/input/tablet/wacom_sys.c | 91 ++++++++++++++++++++++++++++---- drivers/input/tablet/wacom_wac.c | 20 ++++++- drivers/input/tablet/wacom_wac.h | 1 + 4 files changed, 109 insertions(+), 10 deletions(-) diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h index 0783864a7dc2..febbfd9f3a84 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/input/tablet/wacom.h @@ -112,6 +112,7 @@ struct wacom { struct urb *irq; struct wacom_wac wacom_wac; struct mutex lock; + struct work_struct work; bool open; char phys[32]; struct wacom_led { @@ -122,6 +123,12 @@ struct wacom { } led; }; +static inline void wacom_schedule_work(struct wacom_wac *wacom_wac) +{ + struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); + schedule_work(&wacom->work); +} + extern const struct usb_device_id wacom_ids[]; void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index d8cc9ce165ff..2fc77053960f 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -167,6 +167,19 @@ static void wacom_close(struct input_dev *dev) usb_autopm_put_interface(wacom->intf); } +/* + * Static values for max X/Y and resolution of Pen interface is stored in + * features. This mean physical size of active area can be computed. + * This is useful to do when Pen and Touch have same active area of tablet. + * This means for Touch device, we only need to find max X/Y value and we + * have enough information to compute resolution of touch. + */ +static void wacom_set_phy_from_res(struct wacom_features *features) +{ + features->x_phy = (features->x_max * 100) / features->x_resolution; + features->y_phy = (features->y_max * 100) / features->y_resolution; +} + static int wacom_parse_logical_collection(unsigned char *report, struct wacom_features *features) { @@ -178,15 +191,7 @@ static int wacom_parse_logical_collection(unsigned char *report, features->pktlen = WACOM_PKGLEN_BBTOUCH3; features->device_type = BTN_TOOL_FINGER; - /* - * Stylus and Touch have same active area - * so compute physical size based on stylus - * data before its overwritten. - */ - features->x_phy = - (features->x_max * features->x_resolution) / 100; - features->y_phy = - (features->y_max * features->y_resolution) / 100; + wacom_set_phy_from_res(features); features->x_max = features->y_max = get_unaligned_le16(&report[10]); @@ -869,6 +874,72 @@ static int wacom_register_input(struct wacom *wacom) return error; } +static void wacom_wireless_work(struct work_struct *work) +{ + struct wacom *wacom = container_of(work, struct wacom, work); + struct usb_device *usbdev = wacom->usbdev; + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + + /* + * Regardless if this is a disconnect or a new tablet, + * remove any existing input devices. + */ + + /* Stylus interface */ + wacom = usb_get_intfdata(usbdev->config->interface[1]); + if (wacom->wacom_wac.input) + input_unregister_device(wacom->wacom_wac.input); + wacom->wacom_wac.input = 0; + + /* Touch interface */ + wacom = usb_get_intfdata(usbdev->config->interface[2]); + if (wacom->wacom_wac.input) + input_unregister_device(wacom->wacom_wac.input); + wacom->wacom_wac.input = 0; + + if (wacom_wac->pid == 0) { + printk(KERN_INFO "wacom: wireless tablet disconnected\n"); + } else { + const struct usb_device_id *id = wacom_ids; + + printk(KERN_INFO + "wacom: wireless tablet connected with PID %x\n", + wacom_wac->pid); + + while (id->match_flags) { + if (id->idVendor == USB_VENDOR_ID_WACOM && + id->idProduct == wacom_wac->pid) + break; + id++; + } + + if (!id->match_flags) { + printk(KERN_INFO + "wacom: ignorning unknown PID.\n"); + return; + } + + /* Stylus interface */ + wacom = usb_get_intfdata(usbdev->config->interface[1]); + wacom_wac = &wacom->wacom_wac; + wacom_wac->features = + *((struct wacom_features *)id->driver_info); + wacom_wac->features.device_type = BTN_TOOL_PEN; + wacom_register_input(wacom); + + /* Touch interface */ + wacom = usb_get_intfdata(usbdev->config->interface[2]); + wacom_wac = &wacom->wacom_wac; + wacom_wac->features = + *((struct wacom_features *)id->driver_info); + wacom_wac->features.pktlen = WACOM_PKGLEN_BBTOUCH3; + wacom_wac->features.device_type = BTN_TOOL_FINGER; + wacom_set_phy_from_res(&wacom_wac->features); + wacom_wac->features.x_max = wacom_wac->features.y_max = 4096; + wacom_register_input(wacom); + } +} + static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); @@ -907,6 +978,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i wacom->usbdev = dev; wacom->intf = intf; mutex_init(&wacom->lock); + INIT_WORK(&wacom->work, wacom_wireless_work); usb_make_path(dev, wacom->phys, sizeof(wacom->phys)); strlcat(wacom->phys, "/input0", sizeof(wacom->phys)); @@ -977,6 +1049,7 @@ static void wacom_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); usb_kill_urb(wacom->irq); + cancel_work_sync(&wacom->work); if (wacom->wacom_wac.input) input_unregister_device(wacom->wacom_wac.input); wacom_destroy_leds(wacom); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 6264e6a8d513..fce7a09fb5db 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1046,9 +1046,27 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) { - if (len != WACOM_PKGLEN_WIRELESS) + unsigned char *data = wacom->data; + int connected; + + if (len != WACOM_PKGLEN_WIRELESS || data[0] != 0x80) return 0; + connected = data[1] & 0x01; + if (connected) { + int pid; + + pid = get_unaligned_be16(&data[6]); + if (wacom->pid != pid) { + wacom->pid = pid; + wacom_schedule_work(wacom); + } + } else if (wacom->pid != 0) { + /* disconnected while previously connected */ + wacom->pid = 0; + wacom_schedule_work(wacom); + } + return 0; } diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 2c04b6248a56..cffaf6b7e6e9 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -111,6 +111,7 @@ struct wacom_wac { struct wacom_features features; struct wacom_shared *shared; struct input_dev *input; + int pid; }; #endif