Input: wacom - share pen info with touch of the same ID
authorPing Cheng <pingc@wacom.com>
Sat, 20 Mar 2010 05:18:15 +0000 (22:18 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 14 Apr 2010 06:24:06 +0000 (23:24 -0700)
Touch enbaled devices share the same product ID with pen. However,
we do not want to post touch events while pen is in prox. To do so,
we used to keep a local static variable to keep track of if pen is
in prox or not. This works fine for Tablet PC devices since there
is only one device attached. With the newer touch enabled regular
tablets, we can not make this assumption any more, i.e, one system
may have more than one identical tablet plugged in.

This patch adds an new entry, shared, into the struct wacom_wac so
touch data can access pen data to locally. This solution assumes
the two tools (touch and pen) of the same ID will be probed one
after the other without interruption in between by another Wacom
device of the same ID.

drivers/input/tablet/wacom_sys.c
drivers/input/tablet/wacom_wac.c
drivers/input/tablet/wacom_wac.h

index f46502589e4eaef0ef92eb0ee6d9a127ab714d83..a03ca219d49f2a34a410541f868bf7e278c93176 100644 (file)
@@ -528,6 +528,81 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
        return error;
 }
 
+struct wacom_usbdev_data {
+       struct list_head list;
+       struct kref kref;
+       struct usb_device *dev;
+       struct wacom_shared shared;
+};
+
+static LIST_HEAD(wacom_udev_list);
+static DEFINE_MUTEX(wacom_udev_list_lock);
+
+static struct wacom_usbdev_data *wacom_get_usbdev_data(struct usb_device *dev)
+{
+       struct wacom_usbdev_data *data;
+
+       list_for_each_entry(data, &wacom_udev_list, list) {
+               if (data->dev == dev) {
+                       kref_get(&data->kref);
+                       return data;
+               }
+       }
+
+       return NULL;
+}
+
+static int wacom_add_shared_data(struct wacom_wac *wacom,
+                                struct usb_device *dev)
+{
+       struct wacom_usbdev_data *data;
+       int retval = 0;
+
+       mutex_lock(&wacom_udev_list_lock);
+
+       data = wacom_get_usbdev_data(dev);
+       if (!data) {
+               data = kzalloc(sizeof(struct wacom_usbdev_data), GFP_KERNEL);
+               if (!data) {
+                       retval = -ENOMEM;
+                       goto out;
+               }
+
+               kref_init(&data->kref);
+               data->dev = dev;
+               list_add_tail(&data->list, &wacom_udev_list);
+       }
+
+       wacom->shared = &data->shared;
+
+out:
+       mutex_unlock(&wacom_udev_list_lock);
+       return retval;
+}
+
+static void wacom_release_shared_data(struct kref *kref)
+{
+       struct wacom_usbdev_data *data =
+               container_of(kref, struct wacom_usbdev_data, kref);
+
+       mutex_lock(&wacom_udev_list_lock);
+       list_del(&data->list);
+       mutex_unlock(&wacom_udev_list_lock);
+
+       kfree(data);
+}
+
+static void wacom_remove_shared_data(struct wacom_wac *wacom)
+{
+       struct wacom_usbdev_data *data;
+
+       if (wacom->shared) {
+               data = container_of(wacom->shared, struct wacom_usbdev_data, shared);
+               kref_put(&data->kref, wacom_release_shared_data);
+               wacom->shared = NULL;
+       }
+}
+
 static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
        struct usb_device *dev = interface_to_usbdev(intf);
@@ -600,6 +675,10 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
                        features->device_type == BTN_TOOL_PEN ?
                                " Pen" : " Finger",
                        sizeof(wacom_wac->name));
+
+               error = wacom_add_shared_data(wacom_wac, dev);
+               if (error)
+                       goto fail3;
        }
 
        input_dev->name = wacom_wac->name;
@@ -624,7 +703,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
 
        error = input_register_device(wacom->dev);
        if (error)
-               goto fail3;
+               goto fail4;
 
        /* Note that if query fails it is not a hard failure */
        wacom_query_tablet_data(intf, features);
@@ -632,6 +711,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
        usb_set_intfdata(intf, wacom);
        return 0;
 
+ fail4:        wacom_remove_shared_data(wacom_wac);
  fail3:        usb_free_urb(wacom->irq);
  fail2:        usb_buffer_free(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma);
  fail1:        input_free_device(input_dev);
@@ -651,6 +731,7 @@ static void wacom_disconnect(struct usb_interface *intf)
        usb_free_urb(wacom->irq);
        usb_buffer_free(interface_to_usbdev(intf), WACOM_PKGLEN_MAX,
                        wacom->wacom_wac->data, wacom->data_dma);
+       wacom_remove_shared_data(wacom->wacom_wac);
        kfree(wacom->wacom_wac);
        kfree(wacom);
 }
index b3ba3437a2eb87cc7e2d713933c73861626da2b6..428144af865f0dad14faf70950be505f4e56f2d8 100644 (file)
@@ -688,7 +688,6 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
        struct wacom_features *features = &wacom->features;
        char *data = wacom->data;
        int prox = 0, pressure, idx = -1;
-       static int stylusInProx, touchInProx = 1, touchOut;
        struct urb *urb = ((struct wacom_combo *)wcombo)->urb;
 
        dbg("wacom_tpc_irq: received report #%d", data[0]);
@@ -707,16 +706,12 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
                                prox = data[1] & 0x03;
                }
 
-               if (!stylusInProx) { /* stylus not in prox */
+               if (!wacom->shared->stylus_in_proximity) {
                        if (prox) {
-                               if (touchInProx) {
-                                       wacom_tpc_touch_in(wacom, wcombo);
-                                       touchOut = 1;
-                                       return 1;
-                               }
+                               wacom_tpc_touch_in(wacom, wcombo);
                        } else {
-                               /* 2FGT out-prox */
                                if (data[0] == WACOM_REPORT_TPC2FG) {
+                                       /* 2FGT out-prox */
                                        idx = (wacom->id[1] & 0x01) - 1;
                                        if (idx == 0) {
                                                wacom_tpc_touch_out(wacom, wcombo, idx);
@@ -727,23 +722,19 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
                                        idx = (wacom->id[1] & 0x02) - 1;
                                        if (idx == 1)
                                                wacom_tpc_touch_out(wacom, wcombo, idx);
-                               } else /* one finger touch */
+                               } else {
+                                       /* one finger touch */
                                        wacom_tpc_touch_out(wacom, wcombo, 0);
-                               touchOut = 0;
-                               touchInProx = 1;
-                               return 1;
+                               }
+                               wacom->id[0] = 0;
                        }
-               } else if (touchOut || !prox) { /* force touch out-prox */
+               } else if (wacom->id[0]) { /* force touch out-prox */
                        wacom_tpc_touch_out(wacom, wcombo, 0);
-                       touchOut = 0;
-                       touchInProx = 1;
-                       return 1;
                }
+               return 1;
        } else if (data[0] == WACOM_REPORT_PENABLED) { /* Penabled */
                prox = data[1] & 0x20;
 
-               touchInProx = 0;
-
                if (!wacom->id[0]) { /* first in prox */
                        /* Going into proximity select tool */
                        wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
@@ -751,6 +742,8 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
                                wacom->id[0] = STYLUS_DEVICE_ID;
                        else
                                wacom->id[0] = ERASER_DEVICE_ID;
+
+                       wacom->shared->stylus_in_proximity = true;
                }
                wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
                wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x10);
@@ -763,12 +756,10 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
                wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x05);
                if (!prox) { /* out-prox */
                        wacom->id[0] = 0;
-                       /* pen is out so touch can be enabled now */
-                       touchInProx = 1;
+                       wacom->shared->stylus_in_proximity = false;
                }
                wacom_report_key(wcombo, wacom->tool[0], prox);
                wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]);
-               stylusInProx = prox;
                return 1;
        }
        return 0;
index b50cf04e61a810d80dbeeade2f4c97ea33c84a7f..4b55fc7ad8aee9fd550c4b4d1788fbe32887b88e 100644 (file)
@@ -71,6 +71,10 @@ struct wacom_features {
        unsigned char unitExpo;
 };
 
+struct wacom_shared {
+       bool stylus_in_proximity;
+};
+
 struct wacom_wac {
        char name[64];
        unsigned char *data;
@@ -78,6 +82,7 @@ struct wacom_wac {
        int id[2];
        __u32 serial[2];
        struct wacom_features features;
+       struct wacom_shared *shared;
 };
 
 #endif