Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 27f06942 authored by David Herrmann's avatar David Herrmann Committed by Jiri Kosina
Browse files

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: default avatarDavid Herrmann <dh.herrmann@gmail.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent d758b1f0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+121 −3
Original line number Diff line number Diff line
@@ -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 @@ static void wiimote_init_set_type(struct wiimote_data *wdata,
		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);
+45 −0
Original line number Diff line number Diff line
/*
 * 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] = {
};
+27 −0
Original line number Diff line number Diff line
@@ -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,