summaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-wiimote-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid/hid-wiimote-core.c')
-rw-r--r--drivers/hid/hid-wiimote-core.c1658
1 files changed, 1101 insertions, 557 deletions
diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index e5ee1f20bbd9..0c06054cab8f 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -1,6 +1,6 @@
/*
- * HID driver for Nintendo Wiimote devices
- * Copyright (c) 2011 David Herrmann
+ * HID driver for Nintendo Wii / Wii U peripherals
+ * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
*/
/*
@@ -14,53 +14,19 @@
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/input.h>
-#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/power_supply.h>
#include <linux/spinlock.h>
#include "hid-ids.h"
#include "hid-wiimote.h"
-enum wiiproto_keys {
- WIIPROTO_KEY_LEFT,
- WIIPROTO_KEY_RIGHT,
- WIIPROTO_KEY_UP,
- WIIPROTO_KEY_DOWN,
- WIIPROTO_KEY_PLUS,
- WIIPROTO_KEY_MINUS,
- WIIPROTO_KEY_ONE,
- WIIPROTO_KEY_TWO,
- WIIPROTO_KEY_A,
- WIIPROTO_KEY_B,
- WIIPROTO_KEY_HOME,
- WIIPROTO_KEY_COUNT
-};
-
-static __u16 wiiproto_keymap[] = {
- KEY_LEFT, /* WIIPROTO_KEY_LEFT */
- KEY_RIGHT, /* WIIPROTO_KEY_RIGHT */
- KEY_UP, /* WIIPROTO_KEY_UP */
- KEY_DOWN, /* WIIPROTO_KEY_DOWN */
- KEY_NEXT, /* WIIPROTO_KEY_PLUS */
- KEY_PREVIOUS, /* WIIPROTO_KEY_MINUS */
- BTN_1, /* WIIPROTO_KEY_ONE */
- BTN_2, /* WIIPROTO_KEY_TWO */
- BTN_A, /* WIIPROTO_KEY_A */
- BTN_B, /* WIIPROTO_KEY_B */
- BTN_MODE, /* WIIPROTO_KEY_HOME */
-};
+/* output queue handling */
-static enum power_supply_property wiimote_battery_props[] = {
- POWER_SUPPLY_PROP_CAPACITY,
- POWER_SUPPLY_PROP_SCOPE,
-};
-
-static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
- size_t count)
+static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
+ size_t count)
{
__u8 *buf;
- ssize_t ret;
+ int ret;
if (!hdev->hid_output_raw_report)
return -ENODEV;
@@ -75,24 +41,33 @@ static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
return ret;
}
-static void wiimote_worker(struct work_struct *work)
+static void wiimote_queue_worker(struct work_struct *work)
{
- struct wiimote_data *wdata = container_of(work, struct wiimote_data,
- worker);
+ struct wiimote_queue *queue = container_of(work, struct wiimote_queue,
+ worker);
+ struct wiimote_data *wdata = container_of(queue, struct wiimote_data,
+ queue);
unsigned long flags;
+ int ret;
- spin_lock_irqsave(&wdata->qlock, flags);
+ spin_lock_irqsave(&wdata->queue.lock, flags);
- while (wdata->head != wdata->tail) {
- spin_unlock_irqrestore(&wdata->qlock, flags);
- wiimote_hid_send(wdata->hdev, wdata->outq[wdata->tail].data,
- wdata->outq[wdata->tail].size);
- spin_lock_irqsave(&wdata->qlock, flags);
+ while (wdata->queue.head != wdata->queue.tail) {
+ spin_unlock_irqrestore(&wdata->queue.lock, flags);
+ ret = wiimote_hid_send(wdata->hdev,
+ wdata->queue.outq[wdata->queue.tail].data,
+ wdata->queue.outq[wdata->queue.tail].size);
+ if (ret < 0) {
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ wiimote_cmd_abort(wdata);
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+ }
+ spin_lock_irqsave(&wdata->queue.lock, flags);
- wdata->tail = (wdata->tail + 1) % WIIMOTE_BUFSIZE;
+ wdata->queue.tail = (wdata->queue.tail + 1) % WIIMOTE_BUFSIZE;
}
- spin_unlock_irqrestore(&wdata->qlock, flags);
+ spin_unlock_irqrestore(&wdata->queue.lock, flags);
}
static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
@@ -103,7 +78,9 @@ static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
if (count > HID_MAX_BUFFER_SIZE) {
hid_warn(wdata->hdev, "Sending too large output report\n");
- return;
+
+ spin_lock_irqsave(&wdata->queue.lock, flags);
+ goto out_error;
}
/*
@@ -116,22 +93,28 @@ static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
* will reschedule itself until the queue is empty.
*/
- spin_lock_irqsave(&wdata->qlock, flags);
+ spin_lock_irqsave(&wdata->queue.lock, flags);
- memcpy(wdata->outq[wdata->head].data, buffer, count);
- wdata->outq[wdata->head].size = count;
- newhead = (wdata->head + 1) % WIIMOTE_BUFSIZE;
+ memcpy(wdata->queue.outq[wdata->queue.head].data, buffer, count);
+ wdata->queue.outq[wdata->queue.head].size = count;
+ newhead = (wdata->queue.head + 1) % WIIMOTE_BUFSIZE;
- if (wdata->head == wdata->tail) {
- wdata->head = newhead;
- schedule_work(&wdata->worker);
- } else if (newhead != wdata->tail) {
- wdata->head = newhead;
+ if (wdata->queue.head == wdata->queue.tail) {
+ wdata->queue.head = newhead;
+ schedule_work(&wdata->queue.worker);
+ } else if (newhead != wdata->queue.tail) {
+ wdata->queue.head = newhead;
} else {
hid_warn(wdata->hdev, "Output queue is full");
+ goto out_error;
}
- spin_unlock_irqrestore(&wdata->qlock, flags);
+ goto out_unlock;
+
+out_error:
+ wiimote_cmd_abort(wdata);
+out_unlock:
+ spin_unlock_irqrestore(&wdata->queue.lock, flags);
}
/*
@@ -147,7 +130,7 @@ static inline void wiiproto_keep_rumble(struct wiimote_data *wdata, __u8 *cmd1)
*cmd1 |= 0x01;
}
-static void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble)
+void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble)
{
__u8 cmd[2];
@@ -167,7 +150,7 @@ static void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble)
wiimote_queue(wdata, cmd, sizeof(cmd));
}
-static void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
+void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
{
__u8 cmd[2];
@@ -196,17 +179,46 @@ static void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
* Check what peripherals of the wiimote are currently
* active and select a proper DRM that supports all of
* the requested data inputs.
+ *
+ * Not all combinations are actually supported. The following
+ * combinations work only with limitations:
+ * - IR cam in extended or full mode disables any data transmission
+ * of extension controllers. There is no DRM mode that supports
+ * extension bytes plus extended/full IR.
+ * - IR cam with accelerometer and extension *_EXT8 is not supported.
+ * However, all extensions that need *_EXT8 are devices that don't
+ * support IR cameras. Hence, this shouldn't happen under normal
+ * operation.
+ * - *_EXT16 is only supported in combination with buttons and
+ * accelerometer. No IR or similar can be active simultaneously. As
+ * above, all modules that require it are mutually exclusive with
+ * IR/etc. so this doesn't matter.
*/
static __u8 select_drm(struct wiimote_data *wdata)
{
__u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR;
- bool ext = wiiext_active(wdata);
+ bool ext;
- if (ir == WIIPROTO_FLAG_IR_BASIC) {
- if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
- return WIIPROTO_REQ_DRM_KAIE;
+ ext = (wdata->state.flags & WIIPROTO_FLAG_EXT_USED) ||
+ (wdata->state.flags & WIIPROTO_FLAG_MP_USED);
+
+ /* some 3rd-party balance-boards are hard-coded to KEE, *sigh* */
+ if (wdata->state.devtype == WIIMOTE_DEV_BALANCE_BOARD) {
+ if (ext)
+ return WIIPROTO_REQ_DRM_KEE;
else
+ return WIIPROTO_REQ_DRM_K;
+ }
+
+ if (ir == WIIPROTO_FLAG_IR_BASIC) {
+ if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) {
+ if (ext)
+ return WIIPROTO_REQ_DRM_KAIE;
+ else
+ return WIIPROTO_REQ_DRM_KAI;
+ } else {
return WIIPROTO_REQ_DRM_KIE;
+ }
} else if (ir == WIIPROTO_FLAG_IR_EXT) {
return WIIPROTO_REQ_DRM_KAI;
} else if (ir == WIIPROTO_FLAG_IR_FULL) {
@@ -219,7 +231,7 @@ static __u8 select_drm(struct wiimote_data *wdata)
return WIIPROTO_REQ_DRM_KA;
} else {
if (ext)
- return WIIPROTO_REQ_DRM_KE;
+ return WIIPROTO_REQ_DRM_KEE;
else
return WIIPROTO_REQ_DRM_K;
}
@@ -230,7 +242,9 @@ void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm)
{
__u8 cmd[3];
- if (drm == WIIPROTO_REQ_NULL)
+ if (wdata->state.flags & WIIPROTO_FLAG_DRM_LOCKED)
+ drm = wdata->state.drm;
+ else if (drm == WIIPROTO_REQ_NULL)
drm = select_drm(wdata);
cmd[0] = WIIPROTO_REQ_DRM;
@@ -242,7 +256,7 @@ void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm)
wiimote_queue(wdata, cmd, sizeof(cmd));
}
-static void wiiproto_req_status(struct wiimote_data *wdata)
+void wiiproto_req_status(struct wiimote_data *wdata)
{
__u8 cmd[2];
@@ -253,7 +267,7 @@ static void wiiproto_req_status(struct wiimote_data *wdata)
wiimote_queue(wdata, cmd, sizeof(cmd));
}
-static void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel)
+void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel)
{
accel = !!accel;
if (accel == !!(wdata->state.flags & WIIPROTO_FLAG_ACCEL))
@@ -267,7 +281,7 @@ static void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel)
wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
}
-static void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags)
+void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags)
{
__u8 cmd[2];
@@ -278,7 +292,7 @@ static void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags)
wiimote_queue(wdata, cmd, sizeof(cmd));
}
-static void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags)
+void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags)
{
__u8 cmd[2];
@@ -394,399 +408,998 @@ ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset, __u8 *rmem,
return ret;
}
-static int wiimote_battery_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
+/* requires the cmd-mutex to be held */
+static int wiimote_cmd_init_ext(struct wiimote_data *wdata)
{
- struct wiimote_data *wdata = container_of(psy,
- struct wiimote_data, battery);
- int ret = 0, state;
- unsigned long flags;
+ __u8 wmem;
+ int ret;
- if (psp == POWER_SUPPLY_PROP_SCOPE) {
- val->intval = POWER_SUPPLY_SCOPE_DEVICE;
- return 0;
- }
+ /* initialize extension */
+ wmem = 0x55;
+ ret = wiimote_cmd_write(wdata, 0xa400f0, &wmem, sizeof(wmem));
+ if (ret)
+ return ret;
- ret = wiimote_cmd_acquire(wdata);
+ /* disable default encryption */
+ wmem = 0x0;
+ ret = wiimote_cmd_write(wdata, 0xa400fb, &wmem, sizeof(wmem));
if (ret)
return ret;
- spin_lock_irqsave(&wdata->state.lock, flags);
- wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0);
- wiiproto_req_status(wdata);
- spin_unlock_irqrestore(&wdata->state.lock, flags);
+ return 0;
+}
- ret = wiimote_cmd_wait(wdata);
- state = wdata->state.cmd_battery;
- wiimote_cmd_release(wdata);
+/* requires the cmd-mutex to be held */
+static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem)
+{
+ int ret;
+
+ /* read extension ID */
+ ret = wiimote_cmd_read(wdata, 0xa400fa, rmem, 6);
+ if (ret != 6)
+ return WIIMOTE_EXT_NONE;
+
+ hid_dbg(wdata->hdev, "extension ID: %02x:%02x %02x:%02x %02x:%02x\n",
+ rmem[0], rmem[1], rmem[2], rmem[3], rmem[4], rmem[5]);
+ if (rmem[0] == 0xff && rmem[1] == 0xff && rmem[2] == 0xff &&
+ rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff)
+ return WIIMOTE_EXT_NONE;
+
+ if (rmem[4] == 0x00 && rmem[5] == 0x00)
+ return WIIMOTE_EXT_NUNCHUK;
+ if (rmem[4] == 0x01 && rmem[5] == 0x01)
+ return WIIMOTE_EXT_CLASSIC_CONTROLLER;
+ if (rmem[4] == 0x04 && rmem[5] == 0x02)
+ return WIIMOTE_EXT_BALANCE_BOARD;
+ if (rmem[4] == 0x01 && rmem[5] == 0x20)
+ return WIIMOTE_EXT_PRO_CONTROLLER;
+
+ return WIIMOTE_EXT_UNKNOWN;
+}
+
+/* requires the cmd-mutex to be held */
+static int wiimote_cmd_init_mp(struct wiimote_data *wdata)
+{
+ __u8 wmem;
+ int ret;
+
+ /* initialize MP */
+ wmem = 0x55;
+ ret = wiimote_cmd_write(wdata, 0xa600f0, &wmem, sizeof(wmem));
if (ret)
return ret;
- switch (psp) {
- case POWER_SUPPLY_PROP_CAPACITY:
- val->intval = state * 100 / 255;
- break;
- default:
- ret = -EINVAL;
- break;
+ /* disable default encryption */
+ wmem = 0x0;
+ ret = wiimote_cmd_write(wdata, 0xa600fb, &wmem, sizeof(wmem));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* requires the cmd-mutex to be held */
+static bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype)
+{
+ __u8 wmem;
+
+ /* map MP with correct pass-through mode */
+ switch (exttype) {
+ case WIIMOTE_EXT_CLASSIC_CONTROLLER:
+ wmem = 0x07;
+ break;
+ case WIIMOTE_EXT_NUNCHUK:
+ wmem = 0x05;
+ break;
+ default:
+ wmem = 0x04;
+ break;
}
- return ret;
+ return wiimote_cmd_write(wdata, 0xa600fe, &wmem, sizeof(wmem));
}
-static int wiimote_init_ir(struct wiimote_data *wdata, __u16 mode)
+/* requires the cmd-mutex to be held */
+static bool wiimote_cmd_read_mp(struct wiimote_data *wdata, __u8 *rmem)
{
int ret;
- unsigned long flags;
- __u8 format = 0;
- static const __u8 data_enable[] = { 0x01 };
- static const __u8 data_sens1[] = { 0x02, 0x00, 0x00, 0x71, 0x01,
- 0x00, 0xaa, 0x00, 0x64 };
- static const __u8 data_sens2[] = { 0x63, 0x03 };
- static const __u8 data_fin[] = { 0x08 };
- spin_lock_irqsave(&wdata->state.lock, flags);
+ /* read motion plus ID */
+ ret = wiimote_cmd_read(wdata, 0xa600fa, rmem, 6);
+ if (ret != 6)
+ return false;
- if (mode == (wdata->state.flags & WIIPROTO_FLAGS_IR)) {
- spin_unlock_irqrestore(&wdata->state.lock, flags);
- return 0;
- }
+ hid_dbg(wdata->hdev, "motion plus ID: %02x:%02x %02x:%02x %02x:%02x\n",
+ rmem[0], rmem[1], rmem[2], rmem[3], rmem[4], rmem[5]);
- if (mode == 0) {
- wdata->state.flags &= ~WIIPROTO_FLAGS_IR;
- wiiproto_req_ir1(wdata, 0);
- wiiproto_req_ir2(wdata, 0);
- wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
- spin_unlock_irqrestore(&wdata->state.lock, flags);
- return 0;
- }
+ if (rmem[5] == 0x05)
+ return true;
- spin_unlock_irqrestore(&wdata->state.lock, flags);
+ hid_info(wdata->hdev, "unknown motion plus ID: %02x:%02x %02x:%02x %02x:%02x\n",
+ rmem[0], rmem[1], rmem[2], rmem[3], rmem[4], rmem[5]);
- ret = wiimote_cmd_acquire(wdata);
- if (ret)
- return ret;
+ return false;
+}
- /* send PIXEL CLOCK ENABLE cmd first */
- spin_lock_irqsave(&wdata->state.lock, flags);
- wiimote_cmd_set(wdata, WIIPROTO_REQ_IR1, 0);
- wiiproto_req_ir1(wdata, 0x06);
- spin_unlock_irqrestore(&wdata->state.lock, flags);
+/* requires the cmd-mutex to be held */
+static __u8 wiimote_cmd_read_mp_mapped(struct wiimote_data *wdata)
+{
+ int ret;
+ __u8 rmem[6];
- ret = wiimote_cmd_wait(wdata);
- if (ret)
- goto unlock;
- if (wdata->state.cmd_err) {
- ret = -EIO;
- goto unlock;
+ /* read motion plus ID */
+ ret = wiimote_cmd_read(wdata, 0xa400fa, rmem, 6);
+ if (ret != 6)
+ return WIIMOTE_MP_NONE;
+
+ hid_dbg(wdata->hdev, "mapped motion plus ID: %02x:%02x %02x:%02x %02x:%02x\n",
+ rmem[0], rmem[1], rmem[2], rmem[3], rmem[4], rmem[5]);
+
+ if (rmem[0] == 0xff && rmem[1] == 0xff && rmem[2] == 0xff &&
+ rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff)
+ return WIIMOTE_MP_NONE;
+
+ if (rmem[4] == 0x04 && rmem[5] == 0x05)
+ return WIIMOTE_MP_SINGLE;
+ else if (rmem[4] == 0x05 && rmem[5] == 0x05)
+ return WIIMOTE_MP_PASSTHROUGH_NUNCHUK;
+ else if (rmem[4] == 0x07 && rmem[5] == 0x05)
+ return WIIMOTE_MP_PASSTHROUGH_CLASSIC;
+
+ return WIIMOTE_MP_UNKNOWN;
+}
+
+/* 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_NO_MP,
+ WIIMOD_NULL,
+ },
+ [WIIMOTE_DEV_GENERIC] = (const __u8[]){
+ WIIMOD_KEYS,
+ WIIMOD_RUMBLE,
+ WIIMOD_BATTERY,
+ WIIMOD_LED1,
+ WIIMOD_LED2,
+ WIIMOD_LED3,
+ WIIMOD_LED4,
+ WIIMOD_ACCEL,
+ WIIMOD_IR,
+ WIIMOD_NULL,
+ },
+ [WIIMOTE_DEV_GEN10] = (const __u8[]){
+ WIIMOD_KEYS,
+ WIIMOD_RUMBLE,
+ WIIMOD_BATTERY,
+ WIIMOD_LED1,
+ WIIMOD_LED2,
+ WIIMOD_LED3,
+ WIIMOD_LED4,
+ WIIMOD_ACCEL,
+ WIIMOD_IR,
+ WIIMOD_NULL,
+ },
+ [WIIMOTE_DEV_GEN20] = (const __u8[]){
+ WIIMOD_KEYS,
+ WIIMOD_RUMBLE,
+ WIIMOD_BATTERY,
+ WIIMOD_LED1,
+ WIIMOD_LED2,
+ WIIMOD_LED3,
+ WIIMOD_LED4,
+ WIIMOD_ACCEL,
+ WIIMOD_IR,
+ WIIMOD_BUILTIN_MP,
+ WIIMOD_NULL,
+ },
+ [WIIMOTE_DEV_BALANCE_BOARD] = (const __u8[]) {
+ WIIMOD_BATTERY,
+ WIIMOD_LED1,
+ WIIMOD_NO_MP,
+ WIIMOD_NULL,
+ },
+ [WIIMOTE_DEV_PRO_CONTROLLER] = (const __u8[]) {
+ WIIMOD_BATTERY,
+ WIIMOD_LED1,
+ WIIMOD_LED2,
+ WIIMOD_LED3,
+ WIIMOD_LED4,
+ WIIMOD_NO_MP,
+ 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;
+ }
}
- /* enable IR LOGIC */
- spin_lock_irqsave(&wdata->state.lock, flags);
- wiimote_cmd_set(wdata, WIIPROTO_REQ_IR2, 0);
- wiiproto_req_ir2(wdata, 0x06);
- spin_unlock_irqrestore(&wdata->state.lock, flags);
+ 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;
+ }
- ret = wiimote_cmd_wait(wdata);
- if (ret)
- goto unlock;
- if (wdata->state.cmd_err) {
- ret = -EIO;
- goto unlock;
+ for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
+ ops = wiimod_table[*iter];
+ if (!ops->probe)
+ continue;
+
+ ret = ops->probe(ops, wdata);
+ if (ret)
+ goto error;
}
- /* enable IR cam but do not make it send data, yet */
- ret = wiimote_cmd_write(wdata, 0xb00030, data_enable,
- sizeof(data_enable));
- if (ret)
- goto unlock;
+ if (wdata->input) {
+ ret = input_register_device(wdata->input);
+ if (ret)
+ goto error;
+ }
- /* write first sensitivity block */
- ret = wiimote_cmd_write(wdata, 0xb00000, data_sens1,
- sizeof(data_sens1));
- if (ret)
- goto unlock;
+ spin_lock_irq(&wdata->state.lock);
+ wdata->state.devtype = devtype;
+ spin_unlock_irq(&wdata->state.lock);
+ return;
- /* write second sensitivity block */
- ret = wiimote_cmd_write(wdata, 0xb0001a, data_sens2,
- sizeof(data_sens2));
- if (ret)
- goto unlock;
+error:
+ for ( ; iter-- != mods; ) {
+ ops = wiimod_table[*iter];
+ if (ops->remove)
+ ops->remove(ops, wdata);
+ }
- /* put IR cam into desired state */
- switch (mode) {
- case WIIPROTO_FLAG_IR_FULL:
- format = 5;
- break;
- case WIIPROTO_FLAG_IR_EXT:
- format = 3;
- break;
- case WIIPROTO_FLAG_IR_BASIC:
- format = 1;
- break;
+ if (wdata->input) {
+ input_free_device(wdata->input);
+ wdata->input = NULL;
}
- ret = wiimote_cmd_write(wdata, 0xb00033, &format, sizeof(format));
- if (ret)
- goto unlock;
+}
- /* make IR cam send data */
- ret = wiimote_cmd_write(wdata, 0xb00030, data_fin, sizeof(data_fin));
- if (ret)
- goto unlock;
+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];
- /* request new DRM mode compatible to IR mode */
spin_lock_irqsave(&wdata->state.lock, flags);
- wdata->state.flags &= ~WIIPROTO_FLAGS_IR;
- wdata->state.flags |= mode & WIIPROTO_FLAGS_IR;
- wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+ wdata->state.devtype = WIIMOTE_DEV_UNKNOWN;
spin_unlock_irqrestore(&wdata->state.lock, flags);
-unlock:
- wiimote_cmd_release(wdata);
- return ret;
+ /* 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;
+ }
}
-static enum led_brightness wiimote_leds_get(struct led_classdev *led_dev)
+/* device extension handling */
+
+static void wiimote_ext_load(struct wiimote_data *wdata, unsigned int ext)
{
- struct wiimote_data *wdata;
- struct device *dev = led_dev->dev->parent;
- int i;
unsigned long flags;
- bool value = false;
+ const struct wiimod_ops *ops;
+ int ret;
- wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
+ ops = wiimod_ext_table[ext];
- for (i = 0; i < 4; ++i) {
- if (wdata->leds[i] == led_dev) {
- spin_lock_irqsave(&wdata->state.lock, flags);
- value = wdata->state.flags & WIIPROTO_FLAG_LED(i + 1);
- spin_unlock_irqrestore(&wdata->state.lock, flags);
- break;
- }
+ if (ops->probe) {
+ ret = ops->probe(ops, wdata);
+ if (ret)
+ ext = WIIMOTE_EXT_UNKNOWN;
}
- return value ? LED_FULL : LED_OFF;
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ wdata->state.exttype = ext;
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
}
-static void wiimote_leds_set(struct led_classdev *led_dev,
- enum led_brightness value)
+static void wiimote_ext_unload(struct wiimote_data *wdata)
{
- struct wiimote_data *wdata;
- struct device *dev = led_dev->dev->parent;
- int i;
unsigned long flags;
- __u8 state, flag;
+ const struct wiimod_ops *ops;
- wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
+ ops = wiimod_ext_table[wdata->state.exttype];
- for (i = 0; i < 4; ++i) {
- if (wdata->leds[i] == led_dev) {
- flag = WIIPROTO_FLAG_LED(i + 1);
- spin_lock_irqsave(&wdata->state.lock, flags);
- state = wdata->state.flags;
- if (value == LED_OFF)
- wiiproto_req_leds(wdata, state & ~flag);
- else
- wiiproto_req_leds(wdata, state | flag);
- spin_unlock_irqrestore(&wdata->state.lock, flags);
- break;
- }
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ wdata->state.exttype = WIIMOTE_EXT_UNKNOWN;
+ wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+ if (ops->remove)
+ ops->remove(ops, wdata);
+}
+
+static void wiimote_mp_load(struct wiimote_data *wdata)
+{
+ unsigned long flags;
+ const struct wiimod_ops *ops;
+ int ret;
+ __u8 mode = 2;
+
+ ops = &wiimod_mp;
+ if (ops->probe) {
+ ret = ops->probe(ops, wdata);
+ if (ret)
+ mode = 1;
}
+
+ spin_lock_irqsave(&wdata->state.lock, flags);
+ wdata->state.mp = mode;
+ spin_unlock_irqrestore(&wdata->state.lock, flags);
}
-static int wiimote_ff_play(struct input_dev *dev, void *data,
- struct ff_effect *eff)
+static void wiimote_mp_unload(struct wiimote_data *wdata)
{
- struct wiimote_data *wdata = input_get_drvdata(dev);
- __u8 value;
unsigned long flags;
+ const struct wiimod_ops *ops;
- /*
- * The wiimote supports only a single rumble motor so if any magnitude
- * is set to non-zero then we start the rumble motor. If both are set to
- * zero, we stop the rumble motor.
- */
+ if (wdata->state.mp < 2)
+ return;
- if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude)
- value = 1;
- else
- value = 0;
+ ops = &wiimod_mp;
spin_lock_irqsave(&wdata->state.lock, flags);
- wiiproto_req_rumble(wdata, value);
+ wdata->state.mp = 0;
+ wdata->state.flags &= ~WIIPROTO_FLAG_MP_USED;
spin_unlock_irqrestore(&wdata->state.lock, flags);
- return 0;
+ if (ops->remove)
+ ops->remove(ops, wdata);
}
-static int wiimote_input_open(struct input_dev *dev)
-{
- struct wiimote_data *wdata = input_get_drvdata(dev);
+/* device (re-)initialization and detection */
- return hid_hw_open(wdata->hdev);
-}
+static const char *wiimote_devtype_names[WIIMOTE_DEV_NUM] = {
+ [WIIMOTE_DEV_PENDING] = "Pending",
+ [WIIMOTE_DEV_UNKNOWN] = "Unknown",
+ [WIIMOTE_DEV_GENERIC] = "Generic",
+ [WIIMOTE_DEV_GEN10] = "Nintendo Wii Remote (Gen 1)",
+ [WIIMOTE_DEV_GEN20] = "Nintendo Wii Remote Plus (Gen 2)",
+ [WIIMOTE_DEV_BALANCE_BOARD] = "Nintendo Wii Balance Board",
+ [WIIMOTE_DEV_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller",
+};
-static void wiimote_input_close(struct input_dev *dev)
+/* Try to guess the device type based on all collected information. We
+ * first try to detect by static extension types, then VID/PID and the
+ * device name. If we cannot detect the device, we use
+ * WIIMOTE_DEV_GENERIC so all modules will get probed on the device. */
+static void wiimote_init_set_type(struct wiimote_data *wdata,
+ __u8 exttype)
{
- struct wiimote_data *wdata = input_get_drvdata(dev);
+ __u8 devtype = WIIMOTE_DEV_GENERIC;
+ __u16 vendor, product;
+ const char *name;
+
+ vendor = wdata->hdev->vendor;
+ product = wdata->hdev->product;
+ name = wdata->hdev->name;
+
+ if (exttype == WIIMOTE_EXT_BALANCE_BOARD) {
+ devtype = WIIMOTE_DEV_BALANCE_BOARD;
+ goto done;
+ } else if (exttype == WIIMOTE_EXT_PRO_CONTROLLER) {
+ devtype = WIIMOTE_DEV_PRO_CONTROLLER;
+ goto done;
+ }
- hid_hw_close(wdata->hdev);
+ if (!strcmp(name, "Nintendo RVL-CNT-01")) {
+ devtype = WIIMOTE_DEV_GEN10;
+ goto done;
+ } else if (!strcmp(name, "Nintendo RVL-CNT-01-TR")) {
+ devtype = WIIMOTE_DEV_GEN20;
+ goto done;
+ } else if (!strcmp(name, "Nintendo RVL-WBC-01")) {
+ devtype = WIIMOTE_DEV_BALANCE_BOARD;
+ goto done;
+ } else if (!strcmp(name, "Nintendo RVL-CNT-01-UC")) {
+ devtype = WIIMOTE_DEV_PRO_CONTROLLER;
+ goto done;
+ }
+
+ if (vendor == USB_VENDOR_ID_NINTENDO) {
+ if (product == USB_DEVICE_ID_NINTENDO_WIIMOTE) {
+ devtype = WIIMOTE_DEV_GEN10;
+ goto done;
+ } else if (product == USB_DEVICE_ID_NINTENDO_WIIMOTE2) {
+ devtype = WIIMOTE_DEV_GEN20;
+ goto done;
+ }
+ }
+
+done:
+ if (devtype == WIIMOTE_DEV_GENERIC)
+ hid_info(wdata->hdev, "cannot detect device; NAME: %s VID: %04x PID: %04x EXT: %04x\n",
+ name, vendor, product, exttype);
+ else
+ hid_info(wdata->hdev, "detected device: %s\n",
+ wiimote_devtype_names[devtype]);
+
+ wiimote_modules_load(wdata, devtype);
}
-static int wiimote_accel_open(struct input_dev *dev)
+static void wiimote_init_detect(struct wiimote_data *wdata)
{
- struct wiimote_data *wdata = input_get_drvdata(dev);
+ __u8 exttype = WIIMOTE_EXT_NONE, extdata[6];
+ bool ext;
int ret;
- unsigned long flags;
- ret = hid_hw_open(wdata->hdev);
+ 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);
+
+ ret = wiimote_cmd_wait_noint(wdata);
if (ret)
- return ret;
+ goto out_release;
- spin_lock_irqsave(&wdata->state.lock, flags);
- wiiproto_req_accel(wdata, true);
- spin_unlock_irqrestore(&wdata->state.lock, flags);
+ spin_lock_irq(&wdata->state.lock);
+ ext = wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED;
+ spin_unlock_irq(&wdata->state.lock);
- return 0;
+ if (!ext)
+ goto out_release;
+
+ wiimote_cmd_init_ext(wdata);
+ exttype = wiimote_cmd_read_ext(wdata, extdata);
+
+out_release:
+ wiimote_cmd_release(wdata);
+ wiimote_init_set_type(wdata, exttype);
+
+ /* schedule MP timer */
+ spin_lock_irq(&wdata->state.lock);
+ if (!(wdata->state.flags & WIIPROTO_FLAG_BUILTIN_MP) &&
+ !(wdata->state.flags & WIIPROTO_FLAG_NO_MP))
+ mod_timer(&wdata->timer, jiffies + HZ * 4);
+ spin_unlock_irq(&wdata->state.lock);
}
-static void wiimote_accel_close(struct input_dev *dev)
+/*
+ * MP hotplug events are not generated by the wiimote. Therefore, we need
+ * polling to detect it. We use a 4s interval for polling MP registers. This
+ * seems reasonable considering applications can trigger it manually via
+ * sysfs requests.
+ */
+static void wiimote_init_poll_mp(struct wiimote_data *wdata)
{
- struct wiimote_data *wdata = input_get_drvdata(dev);
- unsigned long flags;
+ bool mp;
+ __u8 mpdata[6];
- spin_lock_irqsave(&wdata->state.lock, flags);
- wiiproto_req_accel(wdata, false);
- spin_unlock_irqrestore(&wdata->state.lock, flags);
+ wiimote_cmd_acquire_noint(wdata);
+ wiimote_cmd_init_mp(wdata);
+ mp = wiimote_cmd_read_mp(wdata, mpdata);
+ wiimote_cmd_release(wdata);
- hid_hw_close(wdata->hdev);
+ /* load/unload MP module if it changed */
+ if (mp) {
+ if (!wdata->state.mp) {
+ hid_info(wdata->hdev, "detected extension: Nintendo Wii Motion Plus\n");
+ wiimote_mp_load(wdata);
+ }
+ } else if (wdata->state.mp) {
+ wiimote_mp_unload(wdata);
+ }
+
+ mod_timer(&wdata->timer, jiffies + HZ * 4);
}
-static int wiimote_ir_open(struct input_dev *dev)
+/*
+ * Check whether the wiimote is in the expected state. The extension registers
+ * may change during hotplug and initialization so we might get hotplug events
+ * that we caused by remapping some memory.
+ * We use some heuristics here to check known states. If the wiimote is in the
+ * expected state, we can ignore the hotplug event.
+ *
+ * Returns "true" if the device is in expected state, "false" if we should
+ * redo hotplug handling and extension initialization.
+ */
+static bool wiimote_init_check(struct wiimote_data *wdata)
{
- struct wiimote_data *wdata = input_get_drvdata(dev);
- int ret;
+ __u32 flags;
+ __u8 type, data[6];
+ bool ret, poll_mp;
- ret = hid_hw_open(wdata->hdev);
- if (ret)
- return ret;
+ spin_lock_irq(&wdata->state.lock);
+ flags = wdata->state.flags;
+ spin_unlock_irq(&wdata->state.lock);
- ret = wiimote_init_ir(wdata, WIIPROTO_FLAG_IR_BASIC);
- if (ret) {
- hid_hw_close(wdata->hdev);
- return ret;
+ wiimote_cmd_acquire_noint(wdata);
+
+ /* If MP is used and active, but the extension is not, we expect:
+ * read_mp_mapped() == WIIMOTE_MP_SINGLE
+ * state.flags == !EXT_ACTIVE && !MP_PLUGGED && MP_ACTIVE
+ * We do not check EXT_PLUGGED because it might change during
+ * initialization of MP without extensions.
+ * - If MP is unplugged/replugged, read_mp_mapped() fails
+ * - If EXT is plugged, MP_PLUGGED will get set */
+ if (wdata->state.exttype == WIIMOTE_EXT_NONE &&
+ wdata->state.mp > 0 && (flags & WIIPROTO_FLAG_MP_USED)) {
+ type = wiimote_cmd_read_mp_mapped(wdata);
+ ret = type == WIIMOTE_MP_SINGLE;
+
+ spin_lock_irq(&wdata->state.lock);
+ ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE);
+ ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED);
+ ret = ret && (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE);
+ spin_unlock_irq(&wdata->state.lock);
+
+ if (!ret)
+ hid_dbg(wdata->hdev, "state left: !EXT && MP\n");
+
+ /* while MP is mapped, we get EXT_PLUGGED events */
+ poll_mp = false;
+
+ goto out_release;
}
- return 0;
+ /* If MP is unused, but the extension port is used, we expect:
+ * read_ext == state.exttype
+ * state.flags == !MP_ACTIVE && EXT_ACTIVE
+ * - If MP is plugged/unplugged, our timer detects it
+ * - If EXT is unplugged/replugged, EXT_ACTIVE will become unset */
+ if (!(flags & WIIPROTO_FLAG_MP_USED) &&
+ wdata->state.exttype != WIIMOTE_EXT_NONE) {
+ type = wiimote_cmd_read_ext(wdata, data);
+ ret = type == wdata->state.exttype;
+
+ spin_lock_irq(&wdata->state.lock);
+ ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE);
+ ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE);
+ spin_unlock_irq(&wdata->state.lock);
+
+ if (!ret)
+ hid_dbg(wdata->hdev, "state left: EXT && !MP\n");
+
+ /* poll MP for hotplug events */
+ poll_mp = true;
+
+ goto out_release;
+ }
+
+ /* If neither MP nor an extension are used, we expect:
+ * read_ext() == WIIMOTE_EXT_NONE
+ * state.flags == !MP_ACTIVE && !EXT_ACTIVE && !EXT_PLUGGED
+ * No need to perform any action in this case as everything is
+ * disabled already.
+ * - If MP is plugged/unplugged, our timer detects it
+ * - If EXT is plugged, EXT_PLUGGED will be set */
+ if (!(flags & WIIPROTO_FLAG_MP_USED) &&
+ wdata->state.exttype == WIIMOTE_EXT_NONE) {
+ type = wiimote_cmd_read_ext(wdata, data);
+ ret = type == wdata->state.exttype;
+
+ spin_lock_irq(&wdata->state.lock);
+ ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE);
+ ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE);
+ ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED);
+ spin_unlock_irq(&wdata->state.lock);
+
+ if (!ret)
+ hid_dbg(wdata->hdev, "state left: !EXT && !MP\n");
+
+ /* poll MP for hotplug events */
+ poll_mp = true;
+
+ goto out_release;
+ }
+
+ /* The trickiest part is if both EXT and MP are active. We cannot read
+ * the EXT ID, anymore, because MP is mapped over it. However, we use
+ * a handy trick here:
+ * - EXT_ACTIVE is unset whenever !MP_PLUGGED is sent
+ * MP_PLUGGED might be re-sent again before we are scheduled, but
+ * EXT_ACTIVE will stay unset.
+ * So it is enough to check for mp_mapped() and MP_ACTIVE and
+ * EXT_ACTIVE. EXT_PLUGGED is a sanity check. */
+ if (wdata->state.exttype != WIIMOTE_EXT_NONE &&
+ wdata->state.mp > 0 && (flags & WIIPROTO_FLAG_MP_USED)) {
+ type = wiimote_cmd_read_mp_mapped(wdata);
+ ret = type != WIIMOTE_MP_NONE;
+ ret = ret && type != WIIMOTE_MP_UNKNOWN;
+ ret = ret && type != WIIMOTE_MP_SINGLE;
+
+ spin_lock_irq(&wdata->state.lock);
+ ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED);
+ ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE);
+ ret = ret && (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE);
+ spin_unlock_irq(&wdata->state.lock);
+
+ if (!ret)
+ hid_dbg(wdata->hdev, "state left: EXT && MP\n");
+
+ /* while MP is mapped, we get EXT_PLUGGED events */
+ poll_mp = false;
+
+ goto out_release;
+ }
+
+ /* unknown state */