diff options
author | David Herrmann <dh.herrmann@gmail.com> | 2013-05-05 17:12:51 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2013-06-03 05:07:00 -0400 |
commit | 27f06942142e7a17757b5de1dc4f128c179b7c13 (patch) | |
tree | a494da599d33a444d530314221e2738f1f0a39cf /drivers/hid | |
parent | d758b1f0c527aedc5e83a565a0737d9ac21ea46a (diff) |
HID: wiimote: add sub-device module infrastructure
To avoid loading all sub-device drivers for every Wii Remote, even though
the required hardware might not be available, we introduce a module layer.
The module layer specifies which sub-devices are available on each
device-type. After device detection, we only load the modules for the
detected device. If module loading fails, we unload everything and mark
the device as WIIMOTE_DEV_UNKNOWN. As long as a device is marked as
"unknown", no sub-devices will be used and the device is considered
unsupported.
All the different sub-devices, including KEYS, RUMBLE, BATTERY, LEDS,
ACCELEROMETER, IR and more will be ported in follow-up patches to the new
module layer.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Makefile | 2 | ||||
-rw-r--r-- | drivers/hid/hid-wiimote-core.c | 124 | ||||
-rw-r--r-- | drivers/hid/hid-wiimote-modules.c | 45 | ||||
-rw-r--r-- | drivers/hid/hid-wiimote.h | 27 |
4 files changed, 194 insertions, 4 deletions
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 2065694f57ab..2c2222684043 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
@@ -28,7 +28,7 @@ ifdef CONFIG_LOGIWHEELS_FF | |||
28 | hid-logitech-y += hid-lg4ff.o | 28 | hid-logitech-y += hid-lg4ff.o |
29 | endif | 29 | endif |
30 | 30 | ||
31 | hid-wiimote-y := hid-wiimote-core.o | 31 | hid-wiimote-y := hid-wiimote-core.o hid-wiimote-modules.o |
32 | ifdef CONFIG_HID_WIIMOTE_EXT | 32 | ifdef CONFIG_HID_WIIMOTE_EXT |
33 | hid-wiimote-y += hid-wiimote-ext.o | 33 | hid-wiimote-y += hid-wiimote-ext.o |
34 | endif | 34 | endif |
diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index a025d2104d3c..275428b31509 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c | |||
@@ -715,6 +715,124 @@ static void wiimote_ir_close(struct input_dev *dev) | |||
715 | wiimote_init_ir(wdata, 0); | 715 | wiimote_init_ir(wdata, 0); |
716 | } | 716 | } |
717 | 717 | ||
718 | /* device module handling */ | ||
719 | |||
720 | static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = { | ||
721 | [WIIMOTE_DEV_PENDING] = (const __u8[]){ | ||
722 | WIIMOD_NULL, | ||
723 | }, | ||
724 | [WIIMOTE_DEV_UNKNOWN] = (const __u8[]){ | ||
725 | WIIMOD_NULL, | ||
726 | }, | ||
727 | [WIIMOTE_DEV_GENERIC] = (const __u8[]){ | ||
728 | WIIMOD_NULL, | ||
729 | }, | ||
730 | [WIIMOTE_DEV_GEN10] = (const __u8[]){ | ||
731 | WIIMOD_NULL, | ||
732 | }, | ||
733 | [WIIMOTE_DEV_GEN20] = (const __u8[]){ | ||
734 | WIIMOD_NULL, | ||
735 | }, | ||
736 | }; | ||
737 | |||
738 | static void wiimote_modules_load(struct wiimote_data *wdata, | ||
739 | unsigned int devtype) | ||
740 | { | ||
741 | bool need_input = false; | ||
742 | const __u8 *mods, *iter; | ||
743 | const struct wiimod_ops *ops; | ||
744 | int ret; | ||
745 | |||
746 | mods = wiimote_devtype_mods[devtype]; | ||
747 | |||
748 | for (iter = mods; *iter != WIIMOD_NULL; ++iter) { | ||
749 | if (wiimod_table[*iter]->flags & WIIMOD_FLAG_INPUT) { | ||
750 | need_input = true; | ||
751 | break; | ||
752 | } | ||
753 | } | ||
754 | |||
755 | if (need_input) { | ||
756 | wdata->input = input_allocate_device(); | ||
757 | if (!wdata->input) | ||
758 | return; | ||
759 | |||
760 | input_set_drvdata(wdata->input, wdata); | ||
761 | wdata->input->dev.parent = &wdata->hdev->dev; | ||
762 | wdata->input->id.bustype = wdata->hdev->bus; | ||
763 | wdata->input->id.vendor = wdata->hdev->vendor; | ||
764 | wdata->input->id.product = wdata->hdev->product; | ||
765 | wdata->input->id.version = wdata->hdev->version; | ||
766 | wdata->input->name = WIIMOTE_NAME; | ||
767 | } | ||
768 | |||
769 | for (iter = mods; *iter != WIIMOD_NULL; ++iter) { | ||
770 | ops = wiimod_table[*iter]; | ||
771 | if (!ops->probe) | ||
772 | continue; | ||
773 | |||
774 | ret = ops->probe(ops, wdata); | ||
775 | if (ret) | ||
776 | goto error; | ||
777 | } | ||
778 | |||
779 | if (wdata->input) { | ||
780 | ret = input_register_device(wdata->input); | ||
781 | if (ret) | ||
782 | goto error; | ||
783 | } | ||
784 | |||
785 | spin_lock_irq(&wdata->state.lock); | ||
786 | wdata->state.devtype = devtype; | ||
787 | spin_unlock_irq(&wdata->state.lock); | ||
788 | return; | ||
789 | |||
790 | error: | ||
791 | for ( ; iter-- != mods; ) { | ||
792 | ops = wiimod_table[*iter]; | ||
793 | if (ops->remove) | ||
794 | ops->remove(ops, wdata); | ||
795 | } | ||
796 | |||
797 | if (wdata->input) { | ||
798 | input_free_device(wdata->input); | ||
799 | wdata->input = NULL; | ||
800 | } | ||
801 | } | ||
802 | |||
803 | static void wiimote_modules_unload(struct wiimote_data *wdata) | ||
804 | { | ||
805 | const __u8 *mods, *iter; | ||
806 | const struct wiimod_ops *ops; | ||
807 | unsigned long flags; | ||
808 | |||
809 | mods = wiimote_devtype_mods[wdata->state.devtype]; | ||
810 | |||
811 | spin_lock_irqsave(&wdata->state.lock, flags); | ||
812 | wdata->state.devtype = WIIMOTE_DEV_UNKNOWN; | ||
813 | spin_unlock_irqrestore(&wdata->state.lock, flags); | ||
814 | |||
815 | /* find end of list */ | ||
816 | for (iter = mods; *iter != WIIMOD_NULL; ++iter) | ||
817 | /* empty */ ; | ||
818 | |||
819 | if (wdata->input) { | ||
820 | input_get_device(wdata->input); | ||
821 | input_unregister_device(wdata->input); | ||
822 | } | ||
823 | |||
824 | for ( ; iter-- != mods; ) { | ||
825 | ops = wiimod_table[*iter]; | ||
826 | if (ops->remove) | ||
827 | ops->remove(ops, wdata); | ||
828 | } | ||
829 | |||
830 | if (wdata->input) { | ||
831 | input_put_device(wdata->input); | ||
832 | wdata->input = NULL; | ||
833 | } | ||
834 | } | ||
835 | |||
718 | /* device (re-)initialization and detection */ | 836 | /* device (re-)initialization and detection */ |
719 | 837 | ||
720 | static const char *wiimote_devtype_names[WIIMOTE_DEV_NUM] = { | 838 | static const char *wiimote_devtype_names[WIIMOTE_DEV_NUM] = { |
@@ -766,9 +884,7 @@ done: | |||
766 | hid_info(wdata->hdev, "detected device: %s\n", | 884 | hid_info(wdata->hdev, "detected device: %s\n", |
767 | wiimote_devtype_names[devtype]); | 885 | wiimote_devtype_names[devtype]); |
768 | 886 | ||
769 | spin_lock_irq(&wdata->state.lock); | 887 | wiimote_modules_load(wdata, devtype); |
770 | wdata->state.devtype = devtype; | ||
771 | spin_unlock_irq(&wdata->state.lock); | ||
772 | } | 888 | } |
773 | 889 | ||
774 | static void wiimote_init_detect(struct wiimote_data *wdata) | 890 | static void wiimote_init_detect(struct wiimote_data *wdata) |
@@ -780,6 +896,7 @@ static void wiimote_init_detect(struct wiimote_data *wdata) | |||
780 | wiimote_cmd_acquire_noint(wdata); | 896 | wiimote_cmd_acquire_noint(wdata); |
781 | 897 | ||
782 | spin_lock_irq(&wdata->state.lock); | 898 | spin_lock_irq(&wdata->state.lock); |
899 | wdata->state.devtype = WIIMOTE_DEV_UNKNOWN; | ||
783 | wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0); | 900 | wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0); |
784 | wiiproto_req_status(wdata); | 901 | wiiproto_req_status(wdata); |
785 | spin_unlock_irq(&wdata->state.lock); | 902 | spin_unlock_irq(&wdata->state.lock); |
@@ -1313,6 +1430,7 @@ static void wiimote_destroy(struct wiimote_data *wdata) | |||
1313 | wiiext_deinit(wdata); | 1430 | wiiext_deinit(wdata); |
1314 | wiimote_leds_destroy(wdata); | 1431 | wiimote_leds_destroy(wdata); |
1315 | 1432 | ||
1433 | wiimote_modules_unload(wdata); | ||
1316 | power_supply_unregister(&wdata->battery); | 1434 | power_supply_unregister(&wdata->battery); |
1317 | kfree(wdata->battery.name); | 1435 | kfree(wdata->battery.name); |
1318 | input_unregister_device(wdata->accel); | 1436 | input_unregister_device(wdata->accel); |
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c new file mode 100644 index 000000000000..5dcdd234f29c --- /dev/null +++ b/drivers/hid/hid-wiimote-modules.c | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * Device Modules for Nintendo Wii / Wii U HID Driver | ||
3 | * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com> | ||
4 | */ | ||
5 | |||
6 | /* | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the Free | ||
9 | * Software Foundation; either version 2 of the License, or (at your option) | ||
10 | * any later version. | ||
11 | */ | ||
12 | |||
13 | /* | ||
14 | * Wiimote Modules | ||
15 | * Nintendo devices provide different peripherals and many new devices lack | ||
16 | * initial features like the IR camera. Therefore, each peripheral device is | ||
17 | * implemented as an independent module and we probe on each device only the | ||
18 | * modules for the hardware that really is available. | ||
19 | * | ||
20 | * Module registration is sequential. Unregistration is done in reverse order. | ||
21 | * After device detection, the needed modules are loaded. Users can trigger | ||
22 | * re-detection which causes all modules to be unloaded and then reload the | ||
23 | * modules for the new detected device. | ||
24 | * | ||
25 | * wdata->input is a shared input device. It is always initialized prior to | ||
26 | * module registration. If at least one registered module is marked as | ||
27 | * WIIMOD_FLAG_INPUT, then the input device will get registered after all | ||
28 | * modules were registered. | ||
29 | * Please note that it is unregistered _before_ the "remove" callbacks are | ||
30 | * called. This guarantees that no input interaction is done, anymore. However, | ||
31 | * the wiimote core keeps a reference to the input device so it is freed only | ||
32 | * after all modules were removed. It is safe to send events to unregistered | ||
33 | * input devices. | ||
34 | */ | ||
35 | |||
36 | #include <linux/device.h> | ||
37 | #include <linux/hid.h> | ||
38 | #include <linux/input.h> | ||
39 | #include <linux/spinlock.h> | ||
40 | #include "hid-wiimote.h" | ||
41 | |||
42 | /* module table */ | ||
43 | |||
44 | const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = { | ||
45 | }; | ||
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h index 34417021606e..3c94e3c657c6 100644 --- a/drivers/hid/hid-wiimote.h +++ b/drivers/hid/hid-wiimote.h | |||
@@ -108,6 +108,33 @@ struct wiimote_data { | |||
108 | struct work_struct init_worker; | 108 | struct work_struct init_worker; |
109 | }; | 109 | }; |
110 | 110 | ||
111 | /* wiimote modules */ | ||
112 | |||
113 | enum wiimod_module { | ||
114 | WIIMOD_NUM, | ||
115 | WIIMOD_NULL = WIIMOD_NUM, | ||
116 | }; | ||
117 | |||
118 | #define WIIMOD_FLAG_INPUT 0x0001 | ||
119 | |||
120 | struct wiimod_ops { | ||
121 | __u16 flags; | ||
122 | unsigned long arg; | ||
123 | int (*probe) (const struct wiimod_ops *ops, | ||
124 | struct wiimote_data *wdata); | ||
125 | void (*remove) (const struct wiimod_ops *ops, | ||
126 | struct wiimote_data *wdata); | ||
127 | |||
128 | void (*in_keys) (struct wiimote_data *wdata, const __u8 *keys); | ||
129 | void (*in_accel) (struct wiimote_data *wdata, const __u8 *accel); | ||
130 | void (*in_ir) (struct wiimote_data *wdata, const __u8 *ir, bool packed, | ||
131 | unsigned int id); | ||
132 | }; | ||
133 | |||
134 | extern const struct wiimod_ops *wiimod_table[WIIMOD_NUM]; | ||
135 | |||
136 | /* wiimote requests */ | ||
137 | |||
111 | enum wiiproto_reqs { | 138 | enum wiiproto_reqs { |
112 | WIIPROTO_REQ_NULL = 0x0, | 139 | WIIPROTO_REQ_NULL = 0x0, |
113 | WIIPROTO_REQ_RUMBLE = 0x10, | 140 | WIIPROTO_REQ_RUMBLE = 0x10, |