Merge tag 'for-linus-3.11-merge-window-part-1' of git://git.kernel.org/pub/scm/linux...
[firefly-linux-kernel-4.4.55.git] / drivers / hid / hid-wiimote-core.c
index 13963b4c8801f3c8afafd14a8759f6ab9464f1a7..0c06054cab8f01033404f35375c0e12584db3c51 100644 (file)
@@ -452,6 +452,8 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem)
                return WIIMOTE_EXT_CLASSIC_CONTROLLER;
        if (rmem[4] == 0x04 && rmem[5] == 0x02)
                return WIIMOTE_EXT_BALANCE_BOARD;
+       if (rmem[4] == 0x01 && rmem[5] == 0x20)
+               return WIIMOTE_EXT_PRO_CONTROLLER;
 
        return WIIMOTE_EXT_UNKNOWN;
 }
@@ -555,6 +557,7 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
                WIIMOD_NULL,
        },
        [WIIMOTE_DEV_UNKNOWN] = (const __u8[]){
+               WIIMOD_NO_MP,
                WIIMOD_NULL,
        },
        [WIIMOTE_DEV_GENERIC] = (const __u8[]){
@@ -591,11 +594,22 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
                WIIMOD_LED4,
                WIIMOD_ACCEL,
                WIIMOD_IR,
+               WIIMOD_BUILTIN_MP,
                WIIMOD_NULL,
        },
        [WIIMOTE_DEV_BALANCE_BOARD] = (const __u8[]) {
                WIIMOD_BATTERY,
                WIIMOD_LED1,
+               WIIMOD_NO_MP,
+               WIIMOD_NULL,
+       },
+       [WIIMOTE_DEV_PRO_CONTROLLER] = (const __u8[]) {
+               WIIMOD_BATTERY,
+               WIIMOD_LED1,
+               WIIMOD_LED2,
+               WIIMOD_LED3,
+               WIIMOD_LED4,
+               WIIMOD_NO_MP,
                WIIMOD_NULL,
        },
 };
@@ -782,6 +796,7 @@ static const char *wiimote_devtype_names[WIIMOTE_DEV_NUM] = {
        [WIIMOTE_DEV_GEN10] = "Nintendo Wii Remote (Gen 1)",
        [WIIMOTE_DEV_GEN20] = "Nintendo Wii Remote Plus (Gen 2)",
        [WIIMOTE_DEV_BALANCE_BOARD] = "Nintendo Wii Balance Board",
+       [WIIMOTE_DEV_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller",
 };
 
 /* Try to guess the device type based on all collected information. We
@@ -802,6 +817,9 @@ static void wiimote_init_set_type(struct wiimote_data *wdata,
        if (exttype == WIIMOTE_EXT_BALANCE_BOARD) {
                devtype = WIIMOTE_DEV_BALANCE_BOARD;
                goto done;
+       } else if (exttype == WIIMOTE_EXT_PRO_CONTROLLER) {
+               devtype = WIIMOTE_DEV_PRO_CONTROLLER;
+               goto done;
        }
 
        if (!strcmp(name, "Nintendo RVL-CNT-01")) {
@@ -813,6 +831,9 @@ static void wiimote_init_set_type(struct wiimote_data *wdata,
        } else if (!strcmp(name, "Nintendo RVL-WBC-01")) {
                devtype = WIIMOTE_DEV_BALANCE_BOARD;
                goto done;
+       } else if (!strcmp(name, "Nintendo RVL-CNT-01-UC")) {
+               devtype = WIIMOTE_DEV_PRO_CONTROLLER;
+               goto done;
        }
 
        if (vendor == USB_VENDOR_ID_NINTENDO) {
@@ -867,8 +888,13 @@ static void wiimote_init_detect(struct wiimote_data *wdata)
 out_release:
        wiimote_cmd_release(wdata);
        wiimote_init_set_type(wdata, exttype);
+
        /* schedule MP timer */
-       mod_timer(&wdata->timer, jiffies + HZ * 4);
+       spin_lock_irq(&wdata->state.lock);
+       if (!(wdata->state.flags & WIIPROTO_FLAG_BUILTIN_MP) &&
+           !(wdata->state.flags & WIIPROTO_FLAG_NO_MP))
+               mod_timer(&wdata->timer, jiffies + HZ * 4);
+       spin_unlock_irq(&wdata->state.lock);
 }
 
 /*
@@ -1037,7 +1063,8 @@ out_release:
        wiimote_cmd_release(wdata);
 
        /* only poll for MP if requested and if state didn't change */
-       if (ret && poll_mp)
+       if (ret && poll_mp && !(flags & WIIPROTO_FLAG_BUILTIN_MP) &&
+           !(flags & WIIPROTO_FLAG_NO_MP))
                wiimote_init_poll_mp(wdata);
 
        return ret;
@@ -1049,6 +1076,7 @@ static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = {
        [WIIMOTE_EXT_NUNCHUK] = "Nintendo Wii Nunchuk",
        [WIIMOTE_EXT_CLASSIC_CONTROLLER] = "Nintendo Wii Classic Controller",
        [WIIMOTE_EXT_BALANCE_BOARD] = "Nintendo Wii Balance Board",
+       [WIIMOTE_EXT_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller",
 };
 
 /*
@@ -1082,8 +1110,12 @@ static void wiimote_init_hotplug(struct wiimote_data *wdata)
 
        /* init extension and MP (deactivates current extension or MP) */
        wiimote_cmd_init_ext(wdata);
-       wiimote_cmd_init_mp(wdata);
-       mp = wiimote_cmd_read_mp(wdata, mpdata);
+       if (flags & WIIPROTO_FLAG_NO_MP) {
+               mp = false;
+       } else {
+               wiimote_cmd_init_mp(wdata);
+               mp = wiimote_cmd_read_mp(wdata, mpdata);
+       }
        exttype = wiimote_cmd_read_ext(wdata, extdata);
 
        wiimote_cmd_release(wdata);
@@ -1133,7 +1165,9 @@ static void wiimote_init_hotplug(struct wiimote_data *wdata)
                del_timer_sync(&wdata->timer);
        } else {
                /* reschedule MP hotplug timer */
-               mod_timer(&wdata->timer, jiffies + HZ * 4);
+               if (!(flags & WIIPROTO_FLAG_BUILTIN_MP) &&
+                   !(flags & WIIPROTO_FLAG_NO_MP))
+                       mod_timer(&wdata->timer, jiffies + HZ * 4);
        }
 
        spin_lock_irq(&wdata->state.lock);
@@ -1166,11 +1200,18 @@ static void wiimote_init_worker(struct work_struct *work)
 {
        struct wiimote_data *wdata = container_of(work, struct wiimote_data,
                                                  init_worker);
+       bool changed = false;
 
-       if (wdata->state.devtype == WIIMOTE_DEV_PENDING)
+       if (wdata->state.devtype == WIIMOTE_DEV_PENDING) {
                wiimote_init_detect(wdata);
-       if (!wiimote_init_check(wdata))
+               changed = true;
+       }
+
+       if (changed || !wiimote_init_check(wdata))
                wiimote_init_hotplug(wdata);
+
+       if (changed)
+               kobject_uevent(&wdata->hdev->dev.kobj, KOBJ_CHANGE);
 }
 
 void __wiimote_schedule(struct wiimote_data *wdata)
@@ -1254,11 +1295,19 @@ static bool valid_ext_handler(const struct wiimod_ops *ops, size_t len)
 static void handler_ext(struct wiimote_data *wdata, const __u8 *payload,
                        size_t len)
 {
+       static const __u8 invalid[21] = { 0xff, 0xff, 0xff, 0xff,
+                                         0xff, 0xff, 0xff, 0xff,
+                                         0xff, 0xff, 0xff, 0xff,
+                                         0xff, 0xff, 0xff, 0xff,
+                                         0xff, 0xff, 0xff, 0xff,
+                                         0xff };
        const __u8 *iter, *mods;
        const struct wiimod_ops *ops;
        bool is_mp;
 
-       if (len < 6)
+       if (len > 21)
+               len = 21;
+       if (len < 6 || !memcmp(payload, invalid, len))
                return;
 
        /* if MP is active, track MP slot hotplugging */
@@ -1591,6 +1640,88 @@ static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report,
        return 0;
 }
 
+static ssize_t wiimote_ext_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct wiimote_data *wdata = dev_to_wii(dev);
+       __u8 type;
+       unsigned long flags;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       type = wdata->state.exttype;
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       switch (type) {
+       case WIIMOTE_EXT_NONE:
+               return sprintf(buf, "none\n");
+       case WIIMOTE_EXT_NUNCHUK:
+               return sprintf(buf, "nunchuk\n");
+       case WIIMOTE_EXT_CLASSIC_CONTROLLER:
+               return sprintf(buf, "classic\n");
+       case WIIMOTE_EXT_BALANCE_BOARD:
+               return sprintf(buf, "balanceboard\n");
+       case WIIMOTE_EXT_PRO_CONTROLLER:
+               return sprintf(buf, "procontroller\n");
+       case WIIMOTE_EXT_UNKNOWN:
+               /* fallthrough */
+       default:
+               return sprintf(buf, "unknown\n");
+       }
+}
+
+static ssize_t wiimote_ext_store(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t count)
+{
+       struct wiimote_data *wdata = dev_to_wii(dev);
+
+       if (!strcmp(buf, "scan")) {
+               wiimote_schedule(wdata);
+       } else {
+               return -EINVAL;
+       }
+
+       return strnlen(buf, PAGE_SIZE);
+}
+
+static DEVICE_ATTR(extension, S_IRUGO | S_IWUSR | S_IWGRP, wiimote_ext_show,
+                  wiimote_ext_store);
+
+static ssize_t wiimote_dev_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct wiimote_data *wdata = dev_to_wii(dev);
+       __u8 type;
+       unsigned long flags;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       type = wdata->state.devtype;
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       switch (type) {
+       case WIIMOTE_DEV_GENERIC:
+               return sprintf(buf, "generic\n");
+       case WIIMOTE_DEV_GEN10:
+               return sprintf(buf, "gen10\n");
+       case WIIMOTE_DEV_GEN20:
+               return sprintf(buf, "gen20\n");
+       case WIIMOTE_DEV_BALANCE_BOARD:
+               return sprintf(buf, "balanceboard\n");
+       case WIIMOTE_DEV_PRO_CONTROLLER:
+               return sprintf(buf, "procontroller\n");
+       case WIIMOTE_DEV_PENDING:
+               return sprintf(buf, "pending\n");
+       case WIIMOTE_DEV_UNKNOWN:
+               /* fallthrough */
+       default:
+               return sprintf(buf, "unknown\n");
+       }
+}
+
+static DEVICE_ATTR(devtype, S_IRUGO, wiimote_dev_show, NULL);
+
 static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 {
        struct wiimote_data *wdata;
@@ -1631,6 +1762,9 @@ static void wiimote_destroy(struct wiimote_data *wdata)
        cancel_work_sync(&wdata->init_worker);
        del_timer_sync(&wdata->timer);
 
+       device_remove_file(&wdata->hdev->dev, &dev_attr_devtype);
+       device_remove_file(&wdata->hdev->dev, &dev_attr_extension);
+
        wiimote_mp_unload(wdata);
        wiimote_ext_unload(wdata);
        wiimote_modules_unload(wdata);
@@ -1673,6 +1807,18 @@ static int wiimote_hid_probe(struct hid_device *hdev,
                goto err_stop;
        }
 
+       ret = device_create_file(&hdev->dev, &dev_attr_extension);
+       if (ret) {
+               hid_err(hdev, "cannot create sysfs attribute\n");
+               goto err_close;
+       }
+
+       ret = device_create_file(&hdev->dev, &dev_attr_devtype);
+       if (ret) {
+               hid_err(hdev, "cannot create sysfs attribute\n");
+               goto err_ext;
+       }
+
        ret = wiidebug_init(wdata);
        if (ret)
                goto err_free;
@@ -1688,6 +1834,10 @@ err_free:
        wiimote_destroy(wdata);
        return ret;
 
+err_ext:
+       device_remove_file(&wdata->hdev->dev, &dev_attr_extension);
+err_close:
+       hid_hw_close(hdev);
 err_stop:
        hid_hw_stop(hdev);
 err: