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>
This commit is contained in:
David Herrmann 2013-05-05 23:12:51 +02:00 committed by Jiri Kosina
parent d758b1f0c5
commit 27f0694214
4 changed files with 194 additions and 4 deletions

View File

@ -28,7 +28,7 @@ ifdef CONFIG_LOGIWHEELS_FF
hid-logitech-y += hid-lg4ff.o
endif
hid-wiimote-y := hid-wiimote-core.o
hid-wiimote-y := hid-wiimote-core.o hid-wiimote-modules.o
ifdef CONFIG_HID_WIIMOTE_EXT
hid-wiimote-y += hid-wiimote-ext.o
endif

View File

@ -715,6 +715,124 @@ static void wiimote_ir_close(struct input_dev *dev)
wiimote_init_ir(wdata, 0);
}
/* device module handling */
static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
[WIIMOTE_DEV_PENDING] = (const __u8[]){
WIIMOD_NULL,
},
[WIIMOTE_DEV_UNKNOWN] = (const __u8[]){
WIIMOD_NULL,
},
[WIIMOTE_DEV_GENERIC] = (const __u8[]){
WIIMOD_NULL,
},
[WIIMOTE_DEV_GEN10] = (const __u8[]){
WIIMOD_NULL,
},
[WIIMOTE_DEV_GEN20] = (const __u8[]){
WIIMOD_NULL,
},
};
static void wiimote_modules_load(struct wiimote_data *wdata,
unsigned int devtype)
{
bool need_input = false;
const __u8 *mods, *iter;
const struct wiimod_ops *ops;
int ret;
mods = wiimote_devtype_mods[devtype];
for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
if (wiimod_table[*iter]->flags & WIIMOD_FLAG_INPUT) {
need_input = true;
break;
}
}
if (need_input) {
wdata->input = input_allocate_device();
if (!wdata->input)
return;
input_set_drvdata(wdata->input, wdata);
wdata->input->dev.parent = &wdata->hdev->dev;
wdata->input->id.bustype = wdata->hdev->bus;
wdata->input->id.vendor = wdata->hdev->vendor;
wdata->input->id.product = wdata->hdev->product;
wdata->input->id.version = wdata->hdev->version;
wdata->input->name = WIIMOTE_NAME;
}
for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
ops = wiimod_table[*iter];
if (!ops->probe)
continue;
ret = ops->probe(ops, wdata);
if (ret)
goto error;
}
if (wdata->input) {
ret = input_register_device(wdata->input);
if (ret)
goto error;
}
spin_lock_irq(&wdata->state.lock);
wdata->state.devtype = devtype;
spin_unlock_irq(&wdata->state.lock);
return;
error:
for ( ; iter-- != mods; ) {
ops = wiimod_table[*iter];
if (ops->remove)
ops->remove(ops, wdata);
}
if (wdata->input) {
input_free_device(wdata->input);
wdata->input = NULL;
}
}
static void wiimote_modules_unload(struct wiimote_data *wdata)
{
const __u8 *mods, *iter;
const struct wiimod_ops *ops;
unsigned long flags;
mods = wiimote_devtype_mods[wdata->state.devtype];
spin_lock_irqsave(&wdata->state.lock, flags);
wdata->state.devtype = WIIMOTE_DEV_UNKNOWN;
spin_unlock_irqrestore(&wdata->state.lock, flags);
/* find end of list */
for (iter = mods; *iter != WIIMOD_NULL; ++iter)
/* empty */ ;
if (wdata->input) {
input_get_device(wdata->input);
input_unregister_device(wdata->input);
}
for ( ; iter-- != mods; ) {
ops = wiimod_table[*iter];
if (ops->remove)
ops->remove(ops, wdata);
}
if (wdata->input) {
input_put_device(wdata->input);
wdata->input = NULL;
}
}
/* device (re-)initialization and detection */
static const char *wiimote_devtype_names[WIIMOTE_DEV_NUM] = {
@ -766,9 +884,7 @@ done:
hid_info(wdata->hdev, "detected device: %s\n",
wiimote_devtype_names[devtype]);
spin_lock_irq(&wdata->state.lock);
wdata->state.devtype = devtype;
spin_unlock_irq(&wdata->state.lock);
wiimote_modules_load(wdata, devtype);
}
static void wiimote_init_detect(struct wiimote_data *wdata)
@ -780,6 +896,7 @@ static void wiimote_init_detect(struct wiimote_data *wdata)
wiimote_cmd_acquire_noint(wdata);
spin_lock_irq(&wdata->state.lock);
wdata->state.devtype = WIIMOTE_DEV_UNKNOWN;
wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0);
wiiproto_req_status(wdata);
spin_unlock_irq(&wdata->state.lock);
@ -1313,6 +1430,7 @@ static void wiimote_destroy(struct wiimote_data *wdata)
wiiext_deinit(wdata);
wiimote_leds_destroy(wdata);
wiimote_modules_unload(wdata);
power_supply_unregister(&wdata->battery);
kfree(wdata->battery.name);
input_unregister_device(wdata->accel);

View File

@ -0,0 +1,45 @@
/*
* Device Modules for Nintendo Wii / Wii U HID Driver
* Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
/*
* Wiimote Modules
* Nintendo devices provide different peripherals and many new devices lack
* initial features like the IR camera. Therefore, each peripheral device is
* implemented as an independent module and we probe on each device only the
* modules for the hardware that really is available.
*
* Module registration is sequential. Unregistration is done in reverse order.
* After device detection, the needed modules are loaded. Users can trigger
* re-detection which causes all modules to be unloaded and then reload the
* modules for the new detected device.
*
* wdata->input is a shared input device. It is always initialized prior to
* module registration. If at least one registered module is marked as
* WIIMOD_FLAG_INPUT, then the input device will get registered after all
* modules were registered.
* Please note that it is unregistered _before_ the "remove" callbacks are
* called. This guarantees that no input interaction is done, anymore. However,
* the wiimote core keeps a reference to the input device so it is freed only
* after all modules were removed. It is safe to send events to unregistered
* input devices.
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/spinlock.h>
#include "hid-wiimote.h"
/* module table */
const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
};

View File

@ -108,6 +108,33 @@ struct wiimote_data {
struct work_struct init_worker;
};
/* wiimote modules */
enum wiimod_module {
WIIMOD_NUM,
WIIMOD_NULL = WIIMOD_NUM,
};
#define WIIMOD_FLAG_INPUT 0x0001
struct wiimod_ops {
__u16 flags;
unsigned long arg;
int (*probe) (const struct wiimod_ops *ops,
struct wiimote_data *wdata);
void (*remove) (const struct wiimod_ops *ops,
struct wiimote_data *wdata);
void (*in_keys) (struct wiimote_data *wdata, const __u8 *keys);
void (*in_accel) (struct wiimote_data *wdata, const __u8 *accel);
void (*in_ir) (struct wiimote_data *wdata, const __u8 *ir, bool packed,
unsigned int id);
};
extern const struct wiimod_ops *wiimod_table[WIIMOD_NUM];
/* wiimote requests */
enum wiiproto_reqs {
WIIPROTO_REQ_NULL = 0x0,
WIIPROTO_REQ_RUMBLE = 0x10,