HID: wiimote: add sysfs extension/device-type attrs
authorDavid Herrmann <dh.herrmann@gmail.com>
Sun, 5 May 2013 21:13:04 +0000 (23:13 +0200)
committerJiri Kosina <jkosina@suse.cz>
Mon, 3 Jun 2013 09:07:05 +0000 (11:07 +0200)
Two new attributes, "extension" and "devtype" now allow user-space to read
the extension type and device type. As device detection is asynchronous,
we send a CHANGED event after it is done. This also allows user-space to
wait for a device to settle before opening its input event devices.

The "extension" device is compatible with the old "extension" sysfs field
(which was registered by the static extension support code).

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Documentation/ABI/testing/sysfs-driver-hid-wiimote
drivers/hid/hid-wiimote-core.c

index 3d98009f447a67447956e36e585965403a33610c..e8f6b16723e810d33656742d2dced31c65c1c288 100644 (file)
@@ -12,7 +12,7 @@ Description:  Make it possible to set/get current led state. Reading from it
 What:          /sys/bus/hid/drivers/wiimote/<dev>/extension
 Date:          August 2011
 KernelVersion: 3.2
-Contact:       David Herrmann <dh.herrmann@googlemail.com>
+Contact:       David Herrmann <dh.herrmann@gmail.com>
 Description:   This file contains the currently connected and initialized
                extensions. It can be one of: none, motionp, nunchuck, classic,
                motionp+nunchuck, motionp+classic
@@ -20,3 +20,25 @@ Description: This file contains the currently connected and initialized
                the official Nintendo Nunchuck extension and classic is the
                Nintendo Classic Controller extension. The motionp extension can
                be combined with the other two.
+               Starting with kernel-version 3.11 Motion Plus hotplugging is
+               supported and if detected, it's no longer reported as static
+               extension. You will get uevent notifications for the motion-plus
+               device then.
+
+What:          /sys/bus/hid/drivers/wiimote/<dev>/devtype
+Date:          May 2013
+KernelVersion: 3.11
+Contact:       David Herrmann <dh.herrmann@gmail.com>
+Description:   While a device is initialized by the wiimote driver, we perform
+               a device detection and signal a "change" uevent after it is
+               done. This file shows the detected device type. "pending" means
+               that the detection is still ongoing, "unknown" means, that the
+               device couldn't be detected or loaded. "generic" means, that the
+               device couldn't be detected but supports basic Wii Remote
+               features and can be used.
+               Other strings for each device-type are available and may be
+               added if new device-specific detections are added.
+               Currently supported are:
+                       gen10: First Wii Remote generation
+                       gen20: Second Wii Remote Plus generation (builtin MP)
+                       balanceboard: Wii Balance Board
index 13963b4c8801f3c8afafd14a8759f6ab9464f1a7..fa58045cf7e054e93e60922303dae0208666d504 100644 (file)
@@ -1166,11 +1166,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);
+               changed = true;
+       }
+
        if (!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)
@@ -1591,6 +1598,84 @@ 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_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_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 +1716,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 +1761,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 +1788,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: