]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/hid/wacom_sys.c
Merge branches 'for-4.5/upstream-fixes', 'for-4.6/cmedia', 'for-4.6/i2c-hid', 'for...
[mirror_ubuntu-artful-kernel.git] / drivers / hid / wacom_sys.c
index e06af5b9f59e822e7d2a38e3cfcc478961e7a207..68a560957871c5a497e4e3d0229ca03dc359d8da 100644 (file)
@@ -686,7 +686,7 @@ out:
 static ssize_t wacom_led_select_store(struct device *dev, int set_id,
                                      const char *buf, size_t count)
 {
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct hid_device *hdev = to_hid_device(dev);
        struct wacom *wacom = hid_get_drvdata(hdev);
        unsigned int id;
        int err;
@@ -714,7 +714,7 @@ static ssize_t wacom_led##SET_ID##_select_store(struct device *dev, \
 static ssize_t wacom_led##SET_ID##_select_show(struct device *dev,     \
        struct device_attribute *attr, char *buf)                       \
 {                                                                      \
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);\
+       struct hid_device *hdev = to_hid_device(dev);\
        struct wacom *wacom = hid_get_drvdata(hdev);                    \
        return scnprintf(buf, PAGE_SIZE, "%d\n",                        \
                         wacom->led.select[SET_ID]);                    \
@@ -750,7 +750,7 @@ static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest,
 static ssize_t wacom_##name##_luminance_store(struct device *dev,      \
        struct device_attribute *attr, const char *buf, size_t count)   \
 {                                                                      \
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);\
+       struct hid_device *hdev = to_hid_device(dev);\
        struct wacom *wacom = hid_get_drvdata(hdev);                    \
                                                                        \
        return wacom_luminance_store(wacom, &wacom->led.field,          \
@@ -773,7 +773,7 @@ DEVICE_LUMINANCE_ATTR(buttons, img_lum);
 static ssize_t wacom_button_image_store(struct device *dev, int button_id,
                                        const char *buf, size_t count)
 {
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct hid_device *hdev = to_hid_device(dev);
        struct wacom *wacom = hid_get_drvdata(hdev);
        int err;
        unsigned len;
@@ -1097,7 +1097,7 @@ static ssize_t wacom_show_speed(struct device *dev,
                                struct device_attribute
                                *attr, char *buf)
 {
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct hid_device *hdev = to_hid_device(dev);
        struct wacom *wacom = hid_get_drvdata(hdev);
 
        return snprintf(buf, PAGE_SIZE, "%i\n", wacom->wacom_wac.bt_high_speed);
@@ -1107,7 +1107,7 @@ static ssize_t wacom_store_speed(struct device *dev,
                                struct device_attribute *attr,
                                const char *buf, size_t count)
 {
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct hid_device *hdev = to_hid_device(dev);
        struct wacom *wacom = hid_get_drvdata(hdev);
        u8 new_speed;
 
@@ -1130,8 +1130,8 @@ static ssize_t wacom_show_remote_mode(struct kobject *kobj,
                                      struct kobj_attribute *kattr,
                                      char *buf, int index)
 {
-       struct device *dev = container_of(kobj->parent, struct device, kobj);
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct device *dev = kobj_to_dev(kobj->parent);
+       struct hid_device *hdev = to_hid_device(dev);
        struct wacom *wacom = hid_get_drvdata(hdev);
        u8 mode;
 
@@ -1241,8 +1241,8 @@ static ssize_t wacom_store_unpair_remote(struct kobject *kobj,
                                         const char *buf, size_t count)
 {
        unsigned char selector = 0;
-       struct device *dev = container_of(kobj->parent, struct device, kobj);
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct device *dev = kobj_to_dev(kobj->parent);
+       struct hid_device *hdev = to_hid_device(dev);
        struct wacom *wacom = hid_get_drvdata(hdev);
        int err;
 
@@ -1353,11 +1353,13 @@ static void wacom_clean_inputs(struct wacom *wacom)
                else
                        input_free_device(wacom->wacom_wac.pad_input);
        }
-       if (wacom->remote_dir)
-               kobject_put(wacom->remote_dir);
+       kobject_put(wacom->remote_dir);
        wacom->wacom_wac.pen_input = NULL;
        wacom->wacom_wac.touch_input = NULL;
        wacom->wacom_wac.pad_input = NULL;
+       wacom->wacom_wac.pen_registered = false;
+       wacom->wacom_wac.touch_registered = false;
+       wacom->wacom_wac.pad_registered = false;
        wacom_destroy_leds(wacom);
 }
 
@@ -1495,123 +1497,6 @@ static void wacom_calculate_res(struct wacom_features *features)
                                                    features->unitExpo);
 }
 
-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;
-       struct hid_device *hdev1, *hdev2;
-       struct wacom *wacom1, *wacom2;
-       struct wacom_wac *wacom_wac1, *wacom_wac2;
-       int error;
-
-       /*
-        * Regardless if this is a disconnect or a new tablet,
-        * remove any existing input and battery devices.
-        */
-
-       wacom_destroy_battery(wacom);
-
-       /* Stylus interface */
-       hdev1 = usb_get_intfdata(usbdev->config->interface[1]);
-       wacom1 = hid_get_drvdata(hdev1);
-       wacom_wac1 = &(wacom1->wacom_wac);
-       wacom_clean_inputs(wacom1);
-
-       /* Touch interface */
-       hdev2 = usb_get_intfdata(usbdev->config->interface[2]);
-       wacom2 = hid_get_drvdata(hdev2);
-       wacom_wac2 = &(wacom2->wacom_wac);
-       wacom_clean_inputs(wacom2);
-
-       if (wacom_wac->pid == 0) {
-               hid_info(wacom->hdev, "wireless tablet disconnected\n");
-               wacom_wac1->shared->type = 0;
-       } else {
-               const struct hid_device_id *id = wacom_ids;
-
-               hid_info(wacom->hdev, "wireless tablet connected with PID %x\n",
-                        wacom_wac->pid);
-
-               while (id->bus) {
-                       if (id->vendor == USB_VENDOR_ID_WACOM &&
-                           id->product == wacom_wac->pid)
-                               break;
-                       id++;
-               }
-
-               if (!id->bus) {
-                       hid_info(wacom->hdev, "ignoring unknown PID.\n");
-                       return;
-               }
-
-               /* Stylus interface */
-               wacom_wac1->features =
-                       *((struct wacom_features *)id->driver_data);
-               wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PEN;
-               wacom_set_default_phy(&wacom_wac1->features);
-               wacom_calculate_res(&wacom_wac1->features);
-               snprintf(wacom_wac1->pen_name, WACOM_NAME_MAX, "%s (WL) Pen",
-                        wacom_wac1->features.name);
-               if (wacom_wac1->features.type < BAMBOO_PEN ||
-                   wacom_wac1->features.type > BAMBOO_PT) {
-                       snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad",
-                                wacom_wac1->features.name);
-                       wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PAD;
-               }
-               wacom_wac1->shared->touch_max = wacom_wac1->features.touch_max;
-               wacom_wac1->shared->type = wacom_wac1->features.type;
-               wacom_wac1->pid = wacom_wac->pid;
-               error = wacom_allocate_inputs(wacom1) ||
-                       wacom_register_inputs(wacom1);
-               if (error)
-                       goto fail;
-
-               /* Touch interface */
-               if (wacom_wac1->features.touch_max ||
-                   (wacom_wac1->features.type >= INTUOSHT &&
-                   wacom_wac1->features.type <= BAMBOO_PT)) {
-                       wacom_wac2->features =
-                               *((struct wacom_features *)id->driver_data);
-                       wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
-                       wacom_set_default_phy(&wacom_wac2->features);
-                       wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096;
-                       wacom_calculate_res(&wacom_wac2->features);
-                       snprintf(wacom_wac2->touch_name, WACOM_NAME_MAX,
-                                "%s (WL) Finger",wacom_wac2->features.name);
-                       if (wacom_wac1->features.touch_max)
-                               wacom_wac2->features.device_type |= WACOM_DEVICETYPE_TOUCH;
-                       if (wacom_wac1->features.type >= INTUOSHT &&
-                           wacom_wac1->features.type <= BAMBOO_PT) {
-                               snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX,
-                                        "%s (WL) Pad",wacom_wac2->features.name);
-                               wacom_wac2->features.device_type |= WACOM_DEVICETYPE_PAD;
-                       }
-                       wacom_wac2->pid = wacom_wac->pid;
-                       error = wacom_allocate_inputs(wacom2) ||
-                               wacom_register_inputs(wacom2);
-                       if (error)
-                               goto fail;
-
-                       if ((wacom_wac1->features.type == INTUOSHT ||
-                           wacom_wac1->features.type == INTUOSHT2) &&
-                           wacom_wac1->features.touch_max)
-                               wacom_wac->shared->touch_input = wacom_wac2->touch_input;
-               }
-
-               error = wacom_initialize_battery(wacom);
-               if (error)
-                       goto fail;
-       }
-
-       return;
-
-fail:
-       wacom_clean_inputs(wacom1);
-       wacom_clean_inputs(wacom2);
-       return;
-}
-
 void wacom_battery_work(struct work_struct *work)
 {
        struct wacom *wacom = container_of(work, struct wacom, work);
@@ -1643,7 +1528,7 @@ static size_t wacom_compute_pktlen(struct hid_device *hdev)
        return size;
 }
 
-static void wacom_update_name(struct wacom *wacom)
+static void wacom_update_name(struct wacom *wacom, const char *suffix)
 {
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
        struct wacom_features *features = &wacom_wac->features;
@@ -1679,68 +1564,28 @@ static void wacom_update_name(struct wacom *wacom)
 
        /* Append the device type to the name */
        snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name),
-               "%s Pen", name);
+               "%s%s Pen", name, suffix);
        snprintf(wacom_wac->touch_name, sizeof(wacom_wac->touch_name),
-               "%s Finger", name);
+               "%s%s Finger", name, suffix);
        snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
-               "%s Pad", name);
+               "%s%s Pad", name, suffix);
 }
 
-static int wacom_probe(struct hid_device *hdev,
-               const struct hid_device_id *id)
+static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
 {
-       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
-       struct usb_device *dev = interface_to_usbdev(intf);
-       struct wacom *wacom;
-       struct wacom_wac *wacom_wac;
-       struct wacom_features *features;
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct wacom_features *features = &wacom_wac->features;
+       struct hid_device *hdev = wacom->hdev;
        int error;
        unsigned int connect_mask = HID_CONNECT_HIDRAW;
 
-       if (!id->driver_data)
-               return -EINVAL;
-
-       hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
-
-       /* hid-core sets this quirk for the boot interface */
-       hdev->quirks &= ~HID_QUIRK_NOGET;
-
-       wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
-       if (!wacom)
-               return -ENOMEM;
-
-       hid_set_drvdata(hdev, wacom);
-       wacom->hdev = hdev;
-
-       /* ask for the report descriptor to be loaded by HID */
-       error = hid_parse(hdev);
-       if (error) {
-               hid_err(hdev, "parse failed\n");
-               goto fail_parse;
-       }
-
-       wacom_wac = &wacom->wacom_wac;
-       wacom_wac->features = *((struct wacom_features *)id->driver_data);
-       features = &wacom_wac->features;
        features->pktlen = wacom_compute_pktlen(hdev);
-       if (features->pktlen > WACOM_PKGLEN_MAX) {
-               error = -EINVAL;
-               goto fail_pktlen;
-       }
-
-       if (features->check_for_hid_type && features->hid_type != hdev->type) {
-               error = -ENODEV;
-               goto fail_type;
-       }
-
-       wacom->usbdev = dev;
-       wacom->intf = intf;
-       mutex_init(&wacom->lock);
-       INIT_WORK(&wacom->work, wacom_wireless_work);
+       if (features->pktlen > WACOM_PKGLEN_MAX)
+               return -EINVAL;
 
        error = wacom_allocate_inputs(wacom);
        if (error)
-               goto fail_allocate_inputs;
+               return error;
 
        /*
         * Bamboo Pad has a generic hid handling for the Pen, and we switch it
@@ -1753,7 +1598,7 @@ static int wacom_probe(struct hid_device *hdev,
                } else if ((features->pktlen != WACOM_PKGLEN_BPAD_TOUCH) &&
                           (features->pktlen != WACOM_PKGLEN_BPAD_TOUCH_USB)) {
                        error = -ENODEV;
-                       goto fail_shared_data;
+                       goto fail_allocate_inputs;
                }
        }
 
@@ -1773,14 +1618,14 @@ static int wacom_probe(struct hid_device *hdev,
                         error ? "Ignoring" : "Assuming pen");
 
                if (error)
-                       goto fail_shared_data;
+                       goto fail_parsed;
 
                features->device_type |= WACOM_DEVICETYPE_PEN;
        }
 
        wacom_calculate_res(features);
 
-       wacom_update_name(wacom);
+       wacom_update_name(wacom, wireless ? " (WL)" : "");
 
        error = wacom_add_shared_data(hdev);
        if (error)
@@ -1797,14 +1642,6 @@ static int wacom_probe(struct hid_device *hdev,
        if (error)
                goto fail_register_inputs;
 
-       if (hdev->bus == BUS_BLUETOOTH) {
-               error = device_create_file(&hdev->dev, &dev_attr_speed);
-               if (error)
-                       hid_warn(hdev,
-                                "can't create sysfs speed attribute err: %d\n",
-                                error);
-       }
-
        if (features->type == HID_GENERIC)
                connect_mask |= HID_CONNECT_DRIVER;
 
@@ -1815,8 +1652,10 @@ static int wacom_probe(struct hid_device *hdev,
                goto fail_hw_start;
        }
 
-       /* Note that if query fails it is not a hard failure */
-       wacom_query_tablet_data(hdev, features);
+       if (!wireless) {
+               /* Note that if query fails it is not a hard failure */
+               wacom_query_tablet_data(hdev, features);
+       }
 
        /* touch only Bamboo doesn't support pen */
        if ((features->type == BAMBOO_TOUCH) &&
@@ -1845,18 +1684,166 @@ static int wacom_probe(struct hid_device *hdev,
        return 0;
 
 fail_hw_start:
-       if (hdev->bus == BUS_BLUETOOTH)
-               device_remove_file(&hdev->dev, &dev_attr_speed);
+       hid_hw_stop(hdev);
 fail_register_inputs:
        wacom_clean_inputs(wacom);
        wacom_destroy_battery(wacom);
 fail_battery:
        wacom_remove_shared_data(wacom);
 fail_shared_data:
-       wacom_clean_inputs(wacom);
+fail_parsed:
 fail_allocate_inputs:
+       wacom_clean_inputs(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;
+       struct hid_device *hdev1, *hdev2;
+       struct wacom *wacom1, *wacom2;
+       struct wacom_wac *wacom_wac1, *wacom_wac2;
+       int error;
+
+       /*
+        * Regardless if this is a disconnect or a new tablet,
+        * remove any existing input and battery devices.
+        */
+
+       wacom_destroy_battery(wacom);
+
+       /* Stylus interface */
+       hdev1 = usb_get_intfdata(usbdev->config->interface[1]);
+       wacom1 = hid_get_drvdata(hdev1);
+       wacom_wac1 = &(wacom1->wacom_wac);
+       wacom_clean_inputs(wacom1);
+
+       /* Touch interface */
+       hdev2 = usb_get_intfdata(usbdev->config->interface[2]);
+       wacom2 = hid_get_drvdata(hdev2);
+       wacom_wac2 = &(wacom2->wacom_wac);
+       wacom_clean_inputs(wacom2);
+
+       if (wacom_wac->pid == 0) {
+               hid_info(wacom->hdev, "wireless tablet disconnected\n");
+               wacom_wac1->shared->type = 0;
+       } else {
+               const struct hid_device_id *id = wacom_ids;
+
+               hid_info(wacom->hdev, "wireless tablet connected with PID %x\n",
+                        wacom_wac->pid);
+
+               while (id->bus) {
+                       if (id->vendor == USB_VENDOR_ID_WACOM &&
+                           id->product == wacom_wac->pid)
+                               break;
+                       id++;
+               }
+
+               if (!id->bus) {
+                       hid_info(wacom->hdev, "ignoring unknown PID.\n");
+                       return;
+               }
+
+               /* Stylus interface */
+               wacom_wac1->features =
+                       *((struct wacom_features *)id->driver_data);
+
+               wacom_wac1->pid = wacom_wac->pid;
+               hid_hw_stop(hdev1);
+               error = wacom_parse_and_register(wacom1, true);
+               if (error)
+                       goto fail;
+
+               /* Touch interface */
+               if (wacom_wac1->features.touch_max ||
+                   (wacom_wac1->features.type >= INTUOSHT &&
+                   wacom_wac1->features.type <= BAMBOO_PT)) {
+                       wacom_wac2->features =
+                               *((struct wacom_features *)id->driver_data);
+                       wacom_wac2->pid = wacom_wac->pid;
+                       hid_hw_stop(hdev2);
+                       error = wacom_parse_and_register(wacom2, true);
+                       if (error)
+                               goto fail;
+               }
+
+               error = wacom_initialize_battery(wacom);
+               if (error)
+                       goto fail;
+       }
+
+       return;
+
+fail:
+       wacom_clean_inputs(wacom1);
+       wacom_clean_inputs(wacom2);
+       return;
+}
+
+static int wacom_probe(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+       struct usb_device *dev = interface_to_usbdev(intf);
+       struct wacom *wacom;
+       struct wacom_wac *wacom_wac;
+       struct wacom_features *features;
+       int error;
+
+       if (!id->driver_data)
+               return -EINVAL;
+
+       hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
+
+       /* hid-core sets this quirk for the boot interface */
+       hdev->quirks &= ~HID_QUIRK_NOGET;
+
+       wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
+       if (!wacom)
+               return -ENOMEM;
+
+       hid_set_drvdata(hdev, wacom);
+       wacom->hdev = hdev;
+
+       wacom_wac = &wacom->wacom_wac;
+       wacom_wac->features = *((struct wacom_features *)id->driver_data);
+       features = &wacom_wac->features;
+
+       if (features->check_for_hid_type && features->hid_type != hdev->type) {
+               error = -ENODEV;
+               goto fail_type;
+       }
+
+       wacom->usbdev = dev;
+       wacom->intf = intf;
+       mutex_init(&wacom->lock);
+       INIT_WORK(&wacom->work, wacom_wireless_work);
+
+       /* ask for the report descriptor to be loaded by HID */
+       error = hid_parse(hdev);
+       if (error) {
+               hid_err(hdev, "parse failed\n");
+               goto fail_parse;
+       }
+
+       error = wacom_parse_and_register(wacom, false);
+       if (error)
+               goto fail_parse;
+
+       if (hdev->bus == BUS_BLUETOOTH) {
+               error = device_create_file(&hdev->dev, &dev_attr_speed);
+               if (error)
+                       hid_warn(hdev,
+                                "can't create sysfs speed attribute err: %d\n",
+                                error);
+       }
+
+       return 0;
+
 fail_type:
-fail_pktlen:
 fail_parse:
        kfree(wacom);
        hid_set_drvdata(hdev, NULL);
@@ -1866,6 +1853,11 @@ fail_parse:
 static void wacom_remove(struct hid_device *hdev)
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct wacom_features *features = &wacom_wac->features;
+
+       if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR)
+               hid_hw_close(hdev);
 
        hid_hw_stop(hdev);