HID: wiimote: Add sysfs support to wiimote driver
David Herrmann [Tue, 5 Jul 2011 11:45:19 +0000 (13:45 +0200)]
Add sysfs files for each led of the wiimote. Writing 1 to the file
enables the led and 0 disables the led.

We do not need memory barriers when checking wdata->ready since we use
a spinlock directly after it.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

Documentation/ABI/testing/sysfs-driver-hid-wiimote [new file with mode: 0644]
drivers/hid/hid-wiimote.c

diff --git a/Documentation/ABI/testing/sysfs-driver-hid-wiimote b/Documentation/ABI/testing/sysfs-driver-hid-wiimote
new file mode 100644 (file)
index 0000000..5d5a16e
--- /dev/null
@@ -0,0 +1,10 @@
+What:          /sys/bus/hid/drivers/wiimote/<dev>/led1
+What:          /sys/bus/hid/drivers/wiimote/<dev>/led2
+What:          /sys/bus/hid/drivers/wiimote/<dev>/led3
+What:          /sys/bus/hid/drivers/wiimote/<dev>/led4
+Date:          July 2011
+KernelVersion: 3.1
+Contact:       David Herrmann <dh.herrmann@googlemail.com>
+Description:   Make it possible to set/get current led state. Reading from it
+               returns 0 if led is off and 1 if it is on. Writing 0 to it
+               disables the led, writing 1 enables it.
index f9a3bcb..a594383 100644 (file)
@@ -87,6 +87,9 @@ static __u16 wiiproto_keymap[] = {
        BTN_MODE,       /* WIIPROTO_KEY_HOME */
 };
 
+#define dev_to_wii(pdev) hid_get_drvdata(container_of(pdev, struct hid_device, \
+                                                                       dev))
+
 static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
                                                                size_t count)
 {
@@ -189,6 +192,55 @@ static void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
        wiimote_queue(wdata, cmd, sizeof(cmd));
 }
 
+#define wiifs_led_show_set(num)                                                \
+static ssize_t wiifs_led_show_##num(struct device *dev,                        \
+                       struct device_attribute *attr, char *buf)       \
+{                                                                      \
+       struct wiimote_data *wdata = dev_to_wii(dev);                   \
+       unsigned long flags;                                            \
+       int state;                                                      \
+                                                                       \
+       if (!atomic_read(&wdata->ready))                                \
+               return -EBUSY;                                          \
+                                                                       \
+       spin_lock_irqsave(&wdata->state.lock, flags);                   \
+       state = !!(wdata->state.flags & WIIPROTO_FLAG_LED##num);        \
+       spin_unlock_irqrestore(&wdata->state.lock, flags);              \
+                                                                       \
+       return sprintf(buf, "%d\n", state);                             \
+}                                                                      \
+static ssize_t wiifs_led_set_##num(struct device *dev,                 \
+       struct device_attribute *attr, const char *buf, size_t count)   \
+{                                                                      \
+       struct wiimote_data *wdata = dev_to_wii(dev);                   \
+       int tmp = simple_strtoul(buf, NULL, 10);                        \
+       unsigned long flags;                                            \
+       __u8 state;                                                     \
+                                                                       \
+       if (!atomic_read(&wdata->ready))                                \
+               return -EBUSY;                                          \
+                                                                       \
+       spin_lock_irqsave(&wdata->state.lock, flags);                   \
+                                                                       \
+       state = wdata->state.flags;                                     \
+                                                                       \
+       if (tmp)                                                        \
+               wiiproto_req_leds(wdata, state | WIIPROTO_FLAG_LED##num);\
+       else                                                            \
+               wiiproto_req_leds(wdata, state & ~WIIPROTO_FLAG_LED##num);\
+                                                                       \
+       spin_unlock_irqrestore(&wdata->state.lock, flags);              \
+                                                                       \
+       return count;                                                   \
+}                                                                      \
+static DEVICE_ATTR(led##num, S_IRUGO | S_IWUSR, wiifs_led_show_##num,  \
+                                               wiifs_led_set_##num)
+
+wiifs_led_show_set(1);
+wiifs_led_show_set(2);
+wiifs_led_show_set(3);
+wiifs_led_show_set(4);
+
 static int wiimote_input_event(struct input_dev *dev, unsigned int type,
                                                unsigned int code, int value)
 {
@@ -325,6 +377,19 @@ static int wiimote_hid_probe(struct hid_device *hdev,
                return -ENOMEM;
        }
 
+       ret = device_create_file(&hdev->dev, &dev_attr_led1);
+       if (ret)
+               goto err;
+       ret = device_create_file(&hdev->dev, &dev_attr_led2);
+       if (ret)
+               goto err;
+       ret = device_create_file(&hdev->dev, &dev_attr_led3);
+       if (ret)
+               goto err;
+       ret = device_create_file(&hdev->dev, &dev_attr_led4);
+       if (ret)
+               goto err;
+
        ret = hid_parse(hdev);
        if (ret) {
                hid_err(hdev, "HID parse failed\n");
@@ -359,6 +424,10 @@ err_stop:
        hid_hw_stop(hdev);
 err:
        input_free_device(wdata->input);
+       device_remove_file(&hdev->dev, &dev_attr_led1);
+       device_remove_file(&hdev->dev, &dev_attr_led2);
+       device_remove_file(&hdev->dev, &dev_attr_led3);
+       device_remove_file(&hdev->dev, &dev_attr_led4);
        wiimote_destroy(wdata);
        return ret;
 }
@@ -369,6 +438,11 @@ static void wiimote_hid_remove(struct hid_device *hdev)
 
        hid_info(hdev, "Device removed\n");
 
+       device_remove_file(&hdev->dev, &dev_attr_led1);
+       device_remove_file(&hdev->dev, &dev_attr_led2);
+       device_remove_file(&hdev->dev, &dev_attr_led3);
+       device_remove_file(&hdev->dev, &dev_attr_led4);
+
        hid_hw_stop(hdev);
        input_unregister_device(wdata->input);