Commit 4148b6bf authored by David Herrmann's avatar David Herrmann Committed by Jiri Kosina

HID: wiimote: add extension hotplug support

The Wii Remote has several extension ports. The first port (EXT) provides
hotplug events whenever an extension is plugged. The second port (MP)
does not provide hotplug events by default. Instead, we have to map MP
into EXT to get events for it.

This patch introduces hotplug support for extensions. It is fairly
complicated to get this right because the Wii Remote sends a lot of
noise-hotplug events while activating extension ports. We need to filter
the events and only handle the events that are real hotplug events.

Mapping MP into EXT is easy. But if we want both, MP _and_ EXT at the same
time, we need to map MP into EXT and enable a passthrough-mode. This will
then send real EXT events through the mapped MP interleaved with real MP
events. But once MP is mapped, we no longer have access to the real EXT
registers so we need to perform setup _before_ mapping MP. Furthermore, we
no longer can read EXT IDs so we cannot verify if EXT is still the same
extension that we expect it to be.
We deal with this by unmapping MP whenever we got into a situation where
EXT might have changed. We then re-read EXT and MP and remap everything.

The real Wii Console takes a fairly easy approach: It simply reconnects to
the device on hotplug events that it didn't expect. So if a program wants
MP events, but MP is disconnected, it fails and reconnects so it can wait
for MP hotplug events again.
This simplifies hotplugging a lot because we just react on PLUG events and
ignore UNPLUG events.
The more sophisticated Wii applications avoid reconnection (well, they
still reconnect during many weird events, but at least not during UNPLUG)
but they start polling the device. This allows them to disable the device,
poll for the extension ports to settle and then initialize them again.
Unfortunately, this approach fails whenever an extension is replugged
while it is initialized. We would loose UNPLUG events and polling the
device later will give unreliable results because the extension port might
be in some weird state, even though it's actually unplugged.

Our approach is a real HOTPLUG approch. We keep track of the EXT and
mapped MP hotplug events whenever they occur. We then re-evaluate the
device state and initialize any possible new extension or deinitialize any
gone extension. Only during initialization, we set an extension port
ACTIVE. However, during an unplug event we mark them as INACTIVE. This
guarantess that a fast UNPLUG -> PLUG event sequence doesn't keep them
marked as PLUGGED+ACTIVE but only PLUGGED.
To deal with annoying noise-hotplug events during extension mapping, we
simply rescan the device before performing any mapping. This allows us to
ignore all the noise events as long as the device is in the correct state.

Long story short: EXT and MP registers are sparsely known and we need to
jump through hoops to get reliable HOTPLUG working. But while Nintendo
needs *FOUR* Bluetooth reconnections for the shortest imaginable
boot->menu->game->menu->shutdown sequence, we now need *ZERO*.

As always, 3rd party devices tend to break whenever we behave differently
than the original Wii. So there are also devices which _expect_ a
disconnect after UNPLUG. Obviously, these devices won't benefit from this
patch. But all official devices were tested extensively and work great
during any hotplug sequence. Yay!
Signed-off-by: default avatarDavid Herrmann <dh.herrmann@gmail.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 3b5f03c4
This diff is collapsed.
......@@ -788,8 +788,19 @@ static const struct wiimod_ops wiimod_ir = {
.in_ir = wiimod_ir_in_ir,
};
/*
* Motion Plus
*/
const struct wiimod_ops wiimod_mp = {
.flags = 0,
.arg = 0,
};
/* module table */
static const struct wiimod_ops wiimod_dummy;
const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
[WIIMOD_KEYS] = &wiimod_keys,
[WIIMOD_RUMBLE] = &wiimod_rumble,
......@@ -801,3 +812,8 @@ const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
[WIIMOD_ACCEL] = &wiimod_accel,
[WIIMOD_IR] = &wiimod_ir,
};
const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = {
[WIIMOTE_EXT_NONE] = &wiimod_dummy,
[WIIMOTE_EXT_UNKNOWN] = &wiimod_dummy,
};
......@@ -22,6 +22,7 @@
#include <linux/mutex.h>
#include <linux/power_supply.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#define WIIMOTE_NAME "Nintendo Wii Remote"
#define WIIMOTE_BUFSIZE 32
......@@ -36,6 +37,12 @@
#define WIIPROTO_FLAG_IR_EXT 0x80
#define WIIPROTO_FLAG_IR_FULL 0xc0 /* IR_BASIC | IR_EXT */
#define WIIPROTO_FLAG_EXT_PLUGGED 0x0100
#define WIIPROTO_FLAG_EXT_USED 0x0200
#define WIIPROTO_FLAG_EXT_ACTIVE 0x0400
#define WIIPROTO_FLAG_MP_PLUGGED 0x0800
#define WIIPROTO_FLAG_MP_USED 0x1000
#define WIIPROTO_FLAG_MP_ACTIVE 0x2000
#define WIIPROTO_FLAG_EXITING 0x4000
#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
......@@ -75,6 +82,14 @@ enum wiimote_exttype {
WIIMOTE_EXT_NUM,
};
enum wiimote_mptype {
WIIMOTE_MP_NONE,
WIIMOTE_MP_UNKNOWN,
WIIMOTE_MP_SINGLE,
WIIMOTE_MP_PASSTHROUGH_NUNCHUK,
WIIMOTE_MP_PASSTHROUGH_CLASSIC,
};
struct wiimote_buf {
__u8 data[HID_MAX_BUFFER_SIZE];
size_t size;
......@@ -94,6 +109,8 @@ struct wiimote_state {
__u8 accel_split[2];
__u8 drm;
__u8 devtype;
__u8 exttype;
__u8 mp;
/* synchronous cmd requests */
struct mutex sync;
......@@ -115,6 +132,7 @@ struct wiimote_data {
struct input_dev *accel;
struct input_dev *ir;
struct power_supply battery;
struct timer_list timer;
struct wiimote_ext *ext;
struct wiimote_debug *debug;
......@@ -140,6 +158,8 @@ enum wiimod_module {
};
#define WIIMOD_FLAG_INPUT 0x0001
#define WIIMOD_FLAG_EXT8 0x0002
#define WIIMOD_FLAG_EXT16 0x0004
struct wiimod_ops {
__u16 flags;
......@@ -153,9 +173,13 @@ struct wiimod_ops {
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);
void (*in_mp) (struct wiimote_data *wdata, const __u8 *mp);
void (*in_ext) (struct wiimote_data *wdata, const __u8 *ext);
};
extern const struct wiimod_ops *wiimod_table[WIIMOD_NUM];
extern const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM];
extern const struct wiimod_ops wiimod_mp;
/* wiimote requests */
......@@ -172,23 +196,48 @@ enum wiiproto_reqs {
WIIPROTO_REQ_STATUS = 0x20,
WIIPROTO_REQ_DATA = 0x21,
WIIPROTO_REQ_RETURN = 0x22,
/* DRM_K: BB*2 */
WIIPROTO_REQ_DRM_K = 0x30,
/* DRM_KA: BB*2 AA*3 */
WIIPROTO_REQ_DRM_KA = 0x31,
/* DRM_KE: BB*2 EE*8 */
WIIPROTO_REQ_DRM_KE = 0x32,
/* DRM_KAI: BB*2 AA*3 II*12 */
WIIPROTO_REQ_DRM_KAI = 0x33,
/* DRM_KEE: BB*2 EE*19 */
WIIPROTO_REQ_DRM_KEE = 0x34,
/* DRM_KAE: BB*2 AA*3 EE*16 */
WIIPROTO_REQ_DRM_KAE = 0x35,
/* DRM_KIE: BB*2 II*10 EE*9 */
WIIPROTO_REQ_DRM_KIE = 0x36,
/* DRM_KAIE: BB*2 AA*3 II*10 EE*6 */
WIIPROTO_REQ_DRM_KAIE = 0x37,
/* DRM_E: EE*21 */
WIIPROTO_REQ_DRM_E = 0x3d,
/* DRM_SKAI1: BB*2 AA*1 II*18 */
WIIPROTO_REQ_DRM_SKAI1 = 0x3e,
/* DRM_SKAI2: BB*2 AA*1 II*18 */
WIIPROTO_REQ_DRM_SKAI2 = 0x3f,
WIIPROTO_REQ_MAX
};
#define dev_to_wii(pdev) hid_get_drvdata(container_of(pdev, struct hid_device, \
dev))
void __wiimote_schedule(struct wiimote_data *wdata);
extern void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm);
extern void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble);
extern void wiiproto_req_leds(struct wiimote_data *wdata, int leds);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment