Input: wacom - create inputs when wireless connect
authorChris Bagwell <chris@cnpbagwell.com>
Mon, 26 Mar 2012 06:26:20 +0000 (23:26 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Mon, 26 Mar 2012 06:32:59 +0000 (23:32 -0700)
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 <chris@cnpbagwell.com>
Tested-by: Jason Gerecke <killertofu@gmail.com>
Acked-by: Ping Cheng <pingc@wacom.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
drivers/input/tablet/wacom.h
drivers/input/tablet/wacom_sys.c
drivers/input/tablet/wacom_wac.c
drivers/input/tablet/wacom_wac.h

index 0783864a7dc2e1c77ad1aada99841ceab27106b3..febbfd9f3a84d2c9cb5e9363ff9d70897dd4c0fc 100644 (file)
@@ -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);
index d8cc9ce165ff2b3829a498aca8a4b44c3ec2db53..2fc77053960f1b9e5f89ec80eedbaa3b9ee105ec 100644 (file)
@@ -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);
index 6264e6a8d513744209b4d9caf16bcf01b1c60d35..fce7a09fb5db8795dc32991b1faa6900525b6245 100644 (file)
@@ -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;
 }
 
index 2c04b6248a568e1847d81c801ac7070dbaf66774..cffaf6b7e6e9de4ed33ce8dd4ae804e9affe7c46 100644 (file)
@@ -111,6 +111,7 @@ struct wacom_wac {
        struct wacom_features features;
        struct wacom_shared *shared;
        struct input_dev *input;
+       int pid;
 };
 
 #endif