Merge branch 'pm-cpufreq'
[cascardo/linux.git] / drivers / hid / wacom_sys.c
index 792708b..5e7a564 100644 (file)
@@ -527,36 +527,95 @@ struct wacom_hdev_data {
 static LIST_HEAD(wacom_udev_list);
 static DEFINE_MUTEX(wacom_udev_list_lock);
 
+static bool compare_device_paths(struct hid_device *hdev_a,
+               struct hid_device *hdev_b, char separator)
+{
+       int n1 = strrchr(hdev_a->phys, separator) - hdev_a->phys;
+       int n2 = strrchr(hdev_b->phys, separator) - hdev_b->phys;
+
+       if (n1 != n2 || n1 <= 0 || n2 <= 0)
+               return false;
+
+       return !strncmp(hdev_a->phys, hdev_b->phys, n1);
+}
+
 static bool wacom_are_sibling(struct hid_device *hdev,
                struct hid_device *sibling)
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_features *features = &wacom->wacom_wac.features;
-       int vid = features->oVid;
-       int pid = features->oPid;
-       int n1,n2;
+       struct wacom *sibling_wacom = hid_get_drvdata(sibling);
+       struct wacom_features *sibling_features = &sibling_wacom->wacom_wac.features;
+       __u32 oVid = features->oVid ? features->oVid : hdev->vendor;
+       __u32 oPid = features->oPid ? features->oPid : hdev->product;
 
-       if (vid == 0 && pid == 0) {
-               vid = hdev->vendor;
-               pid = hdev->product;
+       /* The defined oVid/oPid must match that of the sibling */
+       if (features->oVid != HID_ANY_ID && sibling->vendor != oVid)
+               return false;
+       if (features->oPid != HID_ANY_ID && sibling->product != oPid)
+               return false;
+
+       /*
+        * Devices with the same VID/PID must share the same physical
+        * device path, while those with different VID/PID must share
+        * the same physical parent device path.
+        */
+       if (hdev->vendor == sibling->vendor && hdev->product == sibling->product) {
+               if (!compare_device_paths(hdev, sibling, '/'))
+                       return false;
+       } else {
+               if (!compare_device_paths(hdev, sibling, '.'))
+                       return false;
        }
 
-       if (vid != sibling->vendor || pid != sibling->product)
+       /* Skip the remaining heuristics unless you are a HID_GENERIC device */
+       if (features->type != HID_GENERIC)
+               return true;
+
+       /*
+        * Direct-input devices may not be siblings of indirect-input
+        * devices.
+        */
+       if ((features->device_type & WACOM_DEVICETYPE_DIRECT) &&
+           !(sibling_features->device_type & WACOM_DEVICETYPE_DIRECT))
                return false;
 
-       /* Compare the physical path. */
-       n1 = strrchr(hdev->phys, '.') - hdev->phys;
-       n2 = strrchr(sibling->phys, '.') - sibling->phys;
-       if (n1 != n2 || n1 <= 0 || n2 <= 0)
+       /*
+        * Indirect-input devices may not be siblings of direct-input
+        * devices.
+        */
+       if (!(features->device_type & WACOM_DEVICETYPE_DIRECT) &&
+           (sibling_features->device_type & WACOM_DEVICETYPE_DIRECT))
+               return false;
+
+       /* Pen devices may only be siblings of touch devices */
+       if ((features->device_type & WACOM_DEVICETYPE_PEN) &&
+           !(sibling_features->device_type & WACOM_DEVICETYPE_TOUCH))
                return false;
 
-       return !strncmp(hdev->phys, sibling->phys, n1);
+       /* Touch devices may only be siblings of pen devices */
+       if ((features->device_type & WACOM_DEVICETYPE_TOUCH) &&
+           !(sibling_features->device_type & WACOM_DEVICETYPE_PEN))
+               return false;
+
+       /*
+        * No reason could be found for these two devices to NOT be
+        * siblings, so there's a good chance they ARE siblings
+        */
+       return true;
 }
 
 static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev)
 {
        struct wacom_hdev_data *data;
 
+       /* Try to find an already-probed interface from the same device */
+       list_for_each_entry(data, &wacom_udev_list, list) {
+               if (compare_device_paths(hdev, data->dev, '/'))
+                       return data;
+       }
+
+       /* Fallback to finding devices that appear to be "siblings" */
        list_for_each_entry(data, &wacom_udev_list, list) {
                if (wacom_are_sibling(hdev, data->dev)) {
                        kref_get(&data->kref);
@@ -1336,18 +1395,13 @@ static int wacom_initialize_leds(struct wacom *wacom)
 }
 
 static enum power_supply_property wacom_battery_props[] = {
+       POWER_SUPPLY_PROP_MODEL_NAME,
        POWER_SUPPLY_PROP_PRESENT,
        POWER_SUPPLY_PROP_STATUS,
        POWER_SUPPLY_PROP_SCOPE,
        POWER_SUPPLY_PROP_CAPACITY
 };
 
-static enum power_supply_property wacom_ac_props[] = {
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_SCOPE,
-};
-
 static int wacom_battery_get_property(struct power_supply *psy,
                                      enum power_supply_property psp,
                                      union power_supply_propval *val)
@@ -1356,6 +1410,9 @@ static int wacom_battery_get_property(struct power_supply *psy,
        int ret = 0;
 
        switch (psp) {
+               case POWER_SUPPLY_PROP_MODEL_NAME:
+                       val->strval = battery->wacom->wacom_wac.name;
+                       break;
                case POWER_SUPPLY_PROP_PRESENT:
                        val->intval = battery->bat_connected;
                        break;
@@ -1384,44 +1441,22 @@ static int wacom_battery_get_property(struct power_supply *psy,
        return ret;
 }
 
-static int wacom_ac_get_property(struct power_supply *psy,
-                               enum power_supply_property psp,
-                               union power_supply_propval *val)
-{
-       struct wacom_battery *battery = power_supply_get_drvdata(psy);
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_PRESENT:
-               /* fall through */
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = battery->ps_connected;
-               break;
-       case POWER_SUPPLY_PROP_SCOPE:
-               val->intval = POWER_SUPPLY_SCOPE_DEVICE;
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
-
 static int __wacom_initialize_battery(struct wacom *wacom,
                                      struct wacom_battery *battery)
 {
        static atomic_t battery_no = ATOMIC_INIT(0);
        struct device *dev = &wacom->hdev->dev;
        struct power_supply_config psy_cfg = { .drv_data = battery, };
-       struct power_supply *ps_bat, *ps_ac;
+       struct power_supply *ps_bat;
        struct power_supply_desc *bat_desc = &battery->bat_desc;
-       struct power_supply_desc *ac_desc = &battery->ac_desc;
        unsigned long n;
        int error;
 
        if (!devres_open_group(dev, bat_desc, GFP_KERNEL))
                return -ENOMEM;
 
+       battery->wacom = wacom;
+
        n = atomic_inc_return(&battery_no) - 1;
 
        bat_desc->properties = wacom_battery_props;
@@ -1432,31 +1467,15 @@ static int __wacom_initialize_battery(struct wacom *wacom,
        bat_desc->type = POWER_SUPPLY_TYPE_USB;
        bat_desc->use_for_apm = 0;
 
-       ac_desc->properties = wacom_ac_props;
-       ac_desc->num_properties = ARRAY_SIZE(wacom_ac_props);
-       ac_desc->get_property = wacom_ac_get_property;
-       sprintf(battery->ac_name, "wacom_ac_%ld", n);
-       ac_desc->name = battery->ac_name;
-       ac_desc->type = POWER_SUPPLY_TYPE_MAINS;
-       ac_desc->use_for_apm = 0;
-
        ps_bat = devm_power_supply_register(dev, bat_desc, &psy_cfg);
        if (IS_ERR(ps_bat)) {
                error = PTR_ERR(ps_bat);
                goto err;
        }
 
-       ps_ac = devm_power_supply_register(dev, ac_desc, &psy_cfg);
-       if (IS_ERR(ps_ac)) {
-               error = PTR_ERR(ps_ac);
-               goto err;
-       }
-
        power_supply_powers(ps_bat, &wacom->hdev->dev);
-       power_supply_powers(ps_ac, &wacom->hdev->dev);
 
        battery->battery = ps_bat;
-       battery->ac = ps_ac;
 
        devres_close_group(dev, bat_desc);
        return 0;
@@ -1480,7 +1499,6 @@ static void wacom_destroy_battery(struct wacom *wacom)
                devres_release_group(&wacom->hdev->dev,
                                     &wacom->battery.bat_desc);
                wacom->battery.battery = NULL;
-               wacom->battery.ac = NULL;
        }
 }
 
@@ -1910,6 +1928,9 @@ static void wacom_update_name(struct wacom *wacom, const char *suffix)
                strlcpy(name, features->name, sizeof(name));
        }
 
+       snprintf(wacom_wac->name, sizeof(wacom_wac->name), "%s%s",
+                name, suffix);
+
        /* Append the device type to the name */
        snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name),
                "%s%s Pen", name, suffix);
@@ -2144,6 +2165,8 @@ static void wacom_wireless_work(struct work_struct *work)
                                goto fail;
                }
 
+               strlcpy(wacom_wac->name, wacom_wac1->name,
+                       sizeof(wacom_wac->name));
                error = wacom_initialize_battery(wacom);
                if (error)
                        goto fail;