All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/26] Wii Remote Extension Hotplugging Support
@ 2013-05-05 21:12 David Herrmann
  2013-05-05 21:12 ` [PATCH 01/26] HID: wiimote: extend driver description David Herrmann
                   ` (26 more replies)
  0 siblings, 27 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

Hi

This is my second revision of the hid-wiimote rework. It features:
  - Full extension hotplugging support: We can now initialize extensions during
    runtime and no longer require them to be plugged during connection setup.
  - Sub-device modularization: Nintendo produced many different devices which
    are based on the Wii-Remote protocol. However, they often emulate the core
    protocol but don't feature the hardware. The modularization allows us to
    disable hardware that isn't present and instead provide a consistent API
    to user-space.
    For instance, patch 24/26 adds "Wii U Pro Controller" support and avoids
    all the standard wii-remote input devices (rumble, IR, accelerometer,
    extensions, ...) and instead provides a single interface for the
    Pro-Controller features. That makes working with the device from user-space
    a lot easier.
  - Speaker support: Patch 26/26 adds an alsa sound driver and allows user-space
    to use the built-in speaker like any standard sound card.
  - Fixes: Several small fixes are included. They are very hard to backport so I
    didn't CC stable@vger. However, none of the fixes are crucial so I think we
    can ignore the stable kernels here. They fix things like reducing timeouts
    and debugfs bugs.

The Bluetooth HIDP rework is now upstream and already in David Miller's queue.
It will be part of linux-3.10 so if someone wants to test this, I recommend
using linux-next. However, this series doesn't depend on it. But if you want to
avoid Bluetooth l2cap oopses during hotplugging, you should use linux-next..

I did several code review-rounds myself and tested this with 4 different Wii
Remote revisions simultaneously. I didn't get any kernel oops, panic or deadlock
and it works very well. I also tested different extension devices, 3rd party
devices (including balance-board and Pro-Controller) and emulated devices. It
also works with Wii-U devices.
I did some backwards-compatibility checks. Obviously, old user-space won't
notice hot-plugged devices, but other than that the patches should be 100%
backwards-compatible.

Note that the driver doesn't support extension devices between patch 13 and 16.
But extension support is reworked heavily, so there is no reason to do a bisect
on wiimote extensions, anyway. Other than that the series should be bisectable.


Patch 24/26 is marked as RFC because it adds Wii-U-Pro-Controller mappings. If
someone could review them, I would be very glad. Because I want to avoid
introducing horrible mappings as I did with the main Wii-Remote buttons..

Patch 26/26 is marked as RFC as it adds an Alsa driver which I'd like to get
reviewed by ALSA developers. It works for me (and heavily tested), but it's my
first alsa driver, so I am not entirely sure. (alsa-devel is CC'ed for this
patch).


ABI changes are tagged as 3.11 so no need to push this into 3.10.
Sorry for the 3 weeks delay, but I thought I'd do some more review myself before
resending it.


Cheers
David

David Herrmann (26):
  HID: wiimote: extend driver description
  HID: wiimote: move queue handling into separate struct
  HID: wiimote: keep HID device open
  HID: wiimote: add device detection
  HID: wiimote: use cached battery values on I/O failure
  HID: wiimote: wake up if output queue failed
  HID: wiimote: add sub-device module infrastructure
  HID: wiimote: convert KEYS and RUMBLE to modules
  HID: wiimote: convert BATTERY to module
  HID: wiimote: convert LEDS to modules
  HID: wiimote: convert ACCEL to module
  HID: wiimote: convert IR to module
  HID: wiimote: add extension hotplug support
  HID: wiimote: add Balance Board support
  HID: wiimote: add Nunchuk support
  HID: wiimote: add Classic Controller extension
  HID: wiimote: add Motion Plus extension module
  HID: wiimote: fix ctx pointer in debugfs DRM-write
  HID: wiimote: lock DRM mode during debugfs overwrite
  HID: wiimote: add sysfs extension/device-type attrs
  HID: wiimote: add "bboard_calib" attribute
  HID: wiimote: remove old static extension support
  HID: wiimote: add MP quirks
  HID: wiimote: support Nintendo Wii U Pro Controller
  HID: wiimote: fix DRM debug-attr to correctly parse input
  HID/ALSA: wiimote: add speaker support

 Documentation/ABI/testing/sysfs-driver-hid-wiimote |   39 +-
 drivers/hid/Kconfig                                |   31 +-
 drivers/hid/Makefile                               |    8 +-
 drivers/hid/hid-wiimote-core.c                     | 1714 ++++++++++------
 drivers/hid/hid-wiimote-debug.c                    |   14 +-
 drivers/hid/hid-wiimote-ext.c                      |  849 --------
 drivers/hid/hid-wiimote-modules.c                  | 2091 ++++++++++++++++++++
 drivers/hid/hid-wiimote-speaker.c                  |  662 +++++++
 drivers/hid/hid-wiimote.h                          |  234 ++-
 9 files changed, 4188 insertions(+), 1454 deletions(-)
 delete mode 100644 drivers/hid/hid-wiimote-ext.c
 create mode 100644 drivers/hid/hid-wiimote-modules.c
 create mode 100644 drivers/hid/hid-wiimote-speaker.c

-- 
1.8.2.2


^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH 01/26] HID: wiimote: extend driver description
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-06-03  9:19   ` Jiri Kosina
  2013-05-05 21:12 ` [PATCH 02/26] HID: wiimote: move queue handling into separate struct David Herrmann
                   ` (25 subsequent siblings)
  26 siblings, 1 reply; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

The hid-wiimote driver supports more than the Wii Remote. Nintendo
produced many devices based on the Wii Remote, which have extension
devices built-in. It is not clear to many users, that these devices have
anything in common with the Wii Remote, so fix the driver description.

This also updates the copyright information for the coming hotplugging
rework.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/Kconfig             | 20 ++++++++++++++++++--
 drivers/hid/hid-wiimote-core.c  |  6 +++---
 drivers/hid/hid-wiimote-debug.c |  4 ++--
 drivers/hid/hid-wiimote-ext.c   |  4 ++--
 drivers/hid/hid-wiimote.h       |  4 ++--
 5 files changed, 27 insertions(+), 11 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 613083a..e05dda3 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -696,13 +696,29 @@ config HID_WACOM
 	Support for Wacom Graphire Bluetooth and Intuos4 WL tablets.
 
 config HID_WIIMOTE
-	tristate "Nintendo Wii Remote support"
+	tristate "Nintendo Wii / Wii U peripherals"
 	depends on HID
 	depends on LEDS_CLASS
 	select POWER_SUPPLY
 	select INPUT_FF_MEMLESS
 	---help---
-	Support for the Nintendo Wii Remote bluetooth device.
+	Support for Nintendo Wii and Wii U Bluetooth peripherals. Supported
+	devices are the Wii Remote and its extension devices, but also devices
+	based on the Wii Remote like the Wii U Pro Controller or the
+	Wii Balance Board.
+
+	Support for all official Nintendo extensions is available, however, 3rd
+	party extensions might not be supported. Please report these devices to:
+	  http://github.com/dvdhrm/xwiimote/issues
+
+	Other Nintendo Wii U peripherals that are IEEE 802.11 based (including
+	the Wii U Gamepad) might be supported in the future. But currently
+	support is limited to Bluetooth based devices.
+
+	If unsure, say N.
+
+	To compile this driver as a module, choose M here: the
+	module will be called hid-wiimote.
 
 config HID_WIIMOTE_EXT
 	bool "Nintendo Wii Remote Extension support"
diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index e5ee1f2..119dc56 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>
  */
 
 /*
@@ -1331,4 +1331,4 @@ module_hid_driver(wiimote_hid_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
-MODULE_DESCRIPTION(WIIMOTE_NAME " Device Driver");
+MODULE_DESCRIPTION("Driver for Nintendo Wii / Wii U peripherals");
diff --git a/drivers/hid/hid-wiimote-debug.c b/drivers/hid/hid-wiimote-debug.c
index 90124ff..fdd30dd 100644
--- a/drivers/hid/hid-wiimote-debug.c
+++ b/drivers/hid/hid-wiimote-debug.c
@@ -1,6 +1,6 @@
 /*
- * Debug support for HID Nintendo Wiimote devices
- * Copyright (c) 2011 David Herrmann
+ * Debug support for HID Nintendo Wii / Wii U peripherals
+ * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
  */
 
 /*
diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c
index 0472191..1c104fc 100644
--- a/drivers/hid/hid-wiimote-ext.c
+++ b/drivers/hid/hid-wiimote-ext.c
@@ -1,6 +1,6 @@
 /*
- * HID driver for Nintendo Wiimote extension devices
- * Copyright (c) 2011 David Herrmann
+ * HID driver for Nintendo Wii / Wii U peripheral extensions
+ * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
  */
 
 /*
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index c81dbeb..a2629ed 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -2,8 +2,8 @@
 #define __HID_WIIMOTE_H
 
 /*
- * 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>
  */
 
 /*
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 02/26] HID: wiimote: move queue handling into separate struct
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
  2013-05-05 21:12 ` [PATCH 01/26] HID: wiimote: extend driver description David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-05-05 21:12 ` [PATCH 03/26] HID: wiimote: keep HID device open David Herrmann
                   ` (24 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

The output queue is independent of the other wiimote modules and can run
on its own. Therefore, move its members into a separate struct so we don't
run into name collisions with other modules.

This is only a syntactic change that renames all queue members to queue.*.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c | 53 +++++++++++++++++++++++-------------------
 drivers/hid/hid-wiimote.h      | 15 +++++++-----
 2 files changed, 38 insertions(+), 30 deletions(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 119dc56..2d85d3a 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -56,6 +56,8 @@ static enum power_supply_property wiimote_battery_props[] = {
 	POWER_SUPPLY_PROP_SCOPE,
 };
 
+/* output queue handling */
+
 static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
 								size_t count)
 {
@@ -75,24 +77,27 @@ 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;
 
-	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);
+		wiimote_hid_send(wdata->hdev,
+				 wdata->queue.outq[wdata->queue.tail].data,
+				 wdata->queue.outq[wdata->queue.tail].size);
+		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,
@@ -116,22 +121,22 @@ 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");
 	}
 
-	spin_unlock_irqrestore(&wdata->qlock, flags);
+	spin_unlock_irqrestore(&wdata->queue.lock, flags);
 }
 
 /*
@@ -1157,8 +1162,8 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 	input_set_abs_params(wdata->ir, ABS_HAT3X, 0, 1023, 2, 4);
 	input_set_abs_params(wdata->ir, ABS_HAT3Y, 0, 767, 2, 4);
 
-	spin_lock_init(&wdata->qlock);
-	INIT_WORK(&wdata->worker, wiimote_worker);
+	spin_lock_init(&wdata->queue.lock);
+	INIT_WORK(&wdata->queue.worker, wiimote_queue_worker);
 
 	spin_lock_init(&wdata->state.lock);
 	init_completion(&wdata->state.ready);
@@ -1187,7 +1192,7 @@ static void wiimote_destroy(struct wiimote_data *wdata)
 	input_unregister_device(wdata->accel);
 	input_unregister_device(wdata->ir);
 	input_unregister_device(wdata->input);
-	cancel_work_sync(&wdata->worker);
+	cancel_work_sync(&wdata->queue.worker);
 	hid_hw_stop(wdata->hdev);
 
 	kfree(wdata);
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index a2629ed..2700d47 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -48,6 +48,14 @@ struct wiimote_buf {
 	size_t size;
 };
 
+struct wiimote_queue {
+	spinlock_t lock;
+	struct work_struct worker;
+	__u8 head;
+	__u8 tail;
+	struct wiimote_buf outq[WIIMOTE_BUFSIZE];
+};
+
 struct wiimote_state {
 	spinlock_t lock;
 	__u8 flags;
@@ -77,12 +85,7 @@ struct wiimote_data {
 	struct wiimote_ext *ext;
 	struct wiimote_debug *debug;
 
-	spinlock_t qlock;
-	__u8 head;
-	__u8 tail;
-	struct wiimote_buf outq[WIIMOTE_BUFSIZE];
-	struct work_struct worker;
-
+	struct wiimote_queue queue;
 	struct wiimote_state state;
 };
 
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 03/26] HID: wiimote: keep HID device open
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
  2013-05-05 21:12 ` [PATCH 01/26] HID: wiimote: extend driver description David Herrmann
  2013-05-05 21:12 ` [PATCH 02/26] HID: wiimote: move queue handling into separate struct David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-05-05 21:12 ` [PATCH 04/26] HID: wiimote: add device detection David Herrmann
                   ` (23 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

We need constant I/O to keep the state up-to-date and not miss any
packets. Hence, call hid_hw_open() during setup and hid_hw_close() during
destruction.

These are no-ops for Bluetooth HIDP, but lets be safe.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c | 48 ++++++++++--------------------------------
 drivers/hid/hid-wiimote-ext.c  | 12 -----------
 2 files changed, 11 insertions(+), 49 deletions(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 2d85d3a..02656a8 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -626,30 +626,11 @@ static int wiimote_ff_play(struct input_dev *dev, void *data,
 	return 0;
 }
 
-static int wiimote_input_open(struct input_dev *dev)
-{
-	struct wiimote_data *wdata = input_get_drvdata(dev);
-
-	return hid_hw_open(wdata->hdev);
-}
-
-static void wiimote_input_close(struct input_dev *dev)
-{
-	struct wiimote_data *wdata = input_get_drvdata(dev);
-
-	hid_hw_close(wdata->hdev);
-}
-
 static int wiimote_accel_open(struct input_dev *dev)
 {
 	struct wiimote_data *wdata = input_get_drvdata(dev);
-	int ret;
 	unsigned long flags;
 
-	ret = hid_hw_open(wdata->hdev);
-	if (ret)
-		return ret;
-
 	spin_lock_irqsave(&wdata->state.lock, flags);
 	wiiproto_req_accel(wdata, true);
 	spin_unlock_irqrestore(&wdata->state.lock, flags);
@@ -665,26 +646,13 @@ static void wiimote_accel_close(struct input_dev *dev)
 	spin_lock_irqsave(&wdata->state.lock, flags);
 	wiiproto_req_accel(wdata, false);
 	spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-	hid_hw_close(wdata->hdev);
 }
 
 static int wiimote_ir_open(struct input_dev *dev)
 {
 	struct wiimote_data *wdata = input_get_drvdata(dev);
-	int ret;
-
-	ret = hid_hw_open(wdata->hdev);
-	if (ret)
-		return ret;
-
-	ret = wiimote_init_ir(wdata, WIIPROTO_FLAG_IR_BASIC);
-	if (ret) {
-		hid_hw_close(wdata->hdev);
-		return ret;
-	}
 
-	return 0;
+	return wiimote_init_ir(wdata, WIIPROTO_FLAG_IR_BASIC);
 }
 
 static void wiimote_ir_close(struct input_dev *dev)
@@ -692,7 +660,6 @@ static void wiimote_ir_close(struct input_dev *dev)
 	struct wiimote_data *wdata = input_get_drvdata(dev);
 
 	wiimote_init_ir(wdata, 0);
-	hid_hw_close(wdata->hdev);
 }
 
 static void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
@@ -1091,8 +1058,6 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 	hid_set_drvdata(hdev, wdata);
 
 	input_set_drvdata(wdata->input, wdata);
-	wdata->input->open = wiimote_input_open;
-	wdata->input->close = wiimote_input_close;
 	wdata->input->dev.parent = &wdata->hdev->dev;
 	wdata->input->id.bustype = wdata->hdev->bus;
 	wdata->input->id.vendor = wdata->hdev->vendor;
@@ -1193,6 +1158,7 @@ static void wiimote_destroy(struct wiimote_data *wdata)
 	input_unregister_device(wdata->ir);
 	input_unregister_device(wdata->input);
 	cancel_work_sync(&wdata->queue.worker);
+	hid_hw_close(wdata->hdev);
 	hid_hw_stop(wdata->hdev);
 
 	kfree(wdata);
@@ -1224,10 +1190,16 @@ static int wiimote_hid_probe(struct hid_device *hdev,
 		goto err;
 	}
 
+	ret = hid_hw_open(hdev);
+	if (ret) {
+		hid_err(hdev, "cannot start hardware I/O\n");
+		goto err_stop;
+	}
+
 	ret = input_register_device(wdata->accel);
 	if (ret) {
 		hid_err(hdev, "Cannot register input device\n");
-		goto err_stop;
+		goto err_close;
 	}
 
 	ret = input_register_device(wdata->ir);
@@ -1298,6 +1270,8 @@ err_input:
 err_ir:
 	input_unregister_device(wdata->accel);
 	wdata->accel = NULL;
+err_close:
+	hid_hw_close(hdev);
 err_stop:
 	hid_hw_stop(hdev);
 err:
diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c
index 1c104fc..267c89c 100644
--- a/drivers/hid/hid-wiimote-ext.c
+++ b/drivers/hid/hid-wiimote-ext.c
@@ -661,11 +661,6 @@ static DEVICE_ATTR(extension, S_IRUGO, wiiext_show, NULL);
 static int wiiext_input_open(struct input_dev *dev)
 {
 	struct wiimote_ext *ext = input_get_drvdata(dev);
-	int ret;
-
-	ret = hid_hw_open(ext->wdata->hdev);
-	if (ret)
-		return ret;
 
 	atomic_inc(&ext->opened);
 	wiiext_schedule(ext);
@@ -679,17 +674,11 @@ static void wiiext_input_close(struct input_dev *dev)
 
 	atomic_dec(&ext->opened);
 	wiiext_schedule(ext);
-	hid_hw_close(ext->wdata->hdev);
 }
 
 static int wiiext_mp_open(struct input_dev *dev)
 {
 	struct wiimote_ext *ext = input_get_drvdata(dev);
-	int ret;
-
-	ret = hid_hw_open(ext->wdata->hdev);
-	if (ret)
-		return ret;
 
 	atomic_inc(&ext->mp_opened);
 	wiiext_schedule(ext);
@@ -703,7 +692,6 @@ static void wiiext_mp_close(struct input_dev *dev)
 
 	atomic_dec(&ext->mp_opened);
 	wiiext_schedule(ext);
-	hid_hw_close(ext->wdata->hdev);
 }
 
 /* Initializes the extension driver of a wiimote */
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 04/26] HID: wiimote: add device detection
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (2 preceding siblings ...)
  2013-05-05 21:12 ` [PATCH 03/26] HID: wiimote: keep HID device open David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-05-05 21:12 ` [PATCH 05/26] HID: wiimote: use cached battery values on I/O failure David Herrmann
                   ` (22 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

Nintendo produced many different devices that are internally based on the
Wii Remote protocol but provide different peripherals. To support these
devices, we need to schedule a device detection during initialization.

Device detection includes requesting a status report, reading extension
information and then evaluating which device we may be dealing with.

We currently detect gen1 and gen2 Wii Remote devices. All other devices
are marked as generic devices. More detections will be added later.

In followup patches we will be using these device IDs to control which
peripherals to initialize. For instance if a device is known to have no IR
camera, there is no need to provide the IR input device nor trying to
access IR registers. In fact, there are 3rd party devices that break if we
try things like this (hurray!).

The init_worker will be scheduled whenever we get hotplug events. This
isn't implemented, yet and will be added later. However, we need to make
sure that this worker can be called multiple times.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c | 153 ++++++++++++++++++++++++++++++++++++++++-
 drivers/hid/hid-wiimote.h      |  37 +++++++++-
 2 files changed, 188 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 02656a8..76d2c73 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -399,6 +399,45 @@ ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset, __u8 *rmem,
 	return ret;
 }
 
+/* requires the cmd-mutex to be held */
+static int wiimote_cmd_init_ext(struct wiimote_data *wdata)
+{
+	__u8 wmem;
+	int ret;
+
+	/* initialize extension */
+	wmem = 0x55;
+	ret = wiimote_cmd_write(wdata, 0xa400f0, &wmem, sizeof(wmem));
+	if (ret)
+		return ret;
+
+	/* disable default encryption */
+	wmem = 0x0;
+	ret = wiimote_cmd_write(wdata, 0xa400fb, &wmem, sizeof(wmem));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/* requires the cmd-mutex to be held */
+static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata)
+{
+	__u8 rmem[6];
+	int ret;
+
+	/* read extension ID */
+	ret = wiimote_cmd_read(wdata, 0xa400fa, rmem, 6);
+	if (ret != 6)
+		return WIIMOTE_EXT_NONE;
+
+	if (rmem[0] == 0xff && rmem[1] == 0xff && rmem[2] == 0xff &&
+	    rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff)
+		return WIIMOTE_EXT_NONE;
+
+	return WIIMOTE_EXT_UNKNOWN;
+}
+
 static int wiimote_battery_get_property(struct power_supply *psy,
 						enum power_supply_property psp,
 						union power_supply_propval *val)
@@ -662,6 +701,105 @@ static void wiimote_ir_close(struct input_dev *dev)
 	wiimote_init_ir(wdata, 0);
 }
 
+/* device (re-)initialization and detection */
+
+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)",
+};
+
+/* 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)
+{
+	__u8 devtype = WIIMOTE_DEV_GENERIC;
+	__u16 vendor, product;
+	const char *name;
+
+	vendor = wdata->hdev->vendor;
+	product = wdata->hdev->product;
+	name = wdata->hdev->name;
+
+	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;
+	}
+
+	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]);
+
+	spin_lock_irq(&wdata->state.lock);
+	wdata->state.devtype = devtype;
+	spin_unlock_irq(&wdata->state.lock);
+}
+
+static void wiimote_init_detect(struct wiimote_data *wdata)
+{
+	__u8 exttype = WIIMOTE_EXT_NONE;
+	bool ext;
+	int ret;
+
+	wiimote_cmd_acquire_noint(wdata);
+
+	spin_lock_irq(&wdata->state.lock);
+	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)
+		goto out_release;
+
+	spin_lock_irq(&wdata->state.lock);
+	ext = wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED;
+	spin_unlock_irq(&wdata->state.lock);
+
+	if (!ext)
+		goto out_release;
+
+	wiimote_cmd_init_ext(wdata);
+	exttype = wiimote_cmd_read_ext(wdata);
+
+out_release:
+	wiimote_cmd_release(wdata);
+	wiimote_init_set_type(wdata, exttype);
+}
+
+static void wiimote_init_worker(struct work_struct *work)
+{
+	struct wiimote_data *wdata = container_of(work, struct wiimote_data,
+						  init_worker);
+
+	if (wdata->state.devtype == WIIMOTE_DEV_PENDING)
+		wiimote_init_detect(wdata);
+}
+
+/* protocol handlers */
+
 static void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
 {
 	input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_LEFT],
@@ -776,7 +914,14 @@ static void handler_status(struct wiimote_data *wdata, const __u8 *payload)
 {
 	handler_status_K(wdata, payload);
 
-	wiiext_event(wdata, payload[2] & 0x02);
+	/* update extension status */
+	if (payload[2] & 0x02) {
+		wdata->state.flags |= WIIPROTO_FLAG_EXT_PLUGGED;
+		wiiext_event(wdata, true);
+	} else {
+		wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
+		wiiext_event(wdata, false);
+	}
 
 	if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0)) {
 		wdata->state.cmd_battery = payload[5];
@@ -1135,6 +1280,8 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 	mutex_init(&wdata->state.sync);
 	wdata->state.drm = WIIPROTO_REQ_DRM_K;
 
+	INIT_WORK(&wdata->init_worker, wiimote_init_worker);
+
 	return wdata;
 
 err_ir:
@@ -1157,6 +1304,7 @@ static void wiimote_destroy(struct wiimote_data *wdata)
 	input_unregister_device(wdata->accel);
 	input_unregister_device(wdata->ir);
 	input_unregister_device(wdata->input);
+	cancel_work_sync(&wdata->init_worker);
 	cancel_work_sync(&wdata->queue.worker);
 	hid_hw_close(wdata->hdev);
 	hid_hw_stop(wdata->hdev);
@@ -1253,6 +1401,9 @@ static int wiimote_hid_probe(struct hid_device *hdev,
 	wiiproto_req_leds(wdata, WIIPROTO_FLAG_LED1);
 	spin_unlock_irq(&wdata->state.lock);
 
+	/* schedule device detection */
+	schedule_work(&wdata->init_worker);
+
 	return 0;
 
 err_free:
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 2700d47..301607d 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -35,6 +35,8 @@
 #define WIIPROTO_FLAG_IR_BASIC		0x40
 #define WIIPROTO_FLAG_IR_EXT		0x80
 #define WIIPROTO_FLAG_IR_FULL		0xc0 /* IR_BASIC | IR_EXT */
+#define WIIPROTO_FLAG_EXT_PLUGGED	0x0100
+
 #define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
 					WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
 #define WIIPROTO_FLAGS_IR (WIIPROTO_FLAG_IR_BASIC | WIIPROTO_FLAG_IR_EXT | \
@@ -43,6 +45,21 @@
 /* return flag for led \num */
 #define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1))
 
+enum wiimote_devtype {
+	WIIMOTE_DEV_PENDING,
+	WIIMOTE_DEV_UNKNOWN,
+	WIIMOTE_DEV_GENERIC,
+	WIIMOTE_DEV_GEN10,
+	WIIMOTE_DEV_GEN20,
+	WIIMOTE_DEV_NUM,
+};
+
+enum wiimote_exttype {
+	WIIMOTE_EXT_NONE,
+	WIIMOTE_EXT_UNKNOWN,
+	WIIMOTE_EXT_NUM,
+};
+
 struct wiimote_buf {
 	__u8 data[HID_MAX_BUFFER_SIZE];
 	size_t size;
@@ -58,9 +75,10 @@ struct wiimote_queue {
 
 struct wiimote_state {
 	spinlock_t lock;
-	__u8 flags;
+	__u32 flags;
 	__u8 accel_split[2];
 	__u8 drm;
+	__u8 devtype;
 
 	/* synchronous cmd requests */
 	struct mutex sync;
@@ -87,6 +105,7 @@ struct wiimote_data {
 
 	struct wiimote_queue queue;
 	struct wiimote_state state;
+	struct work_struct init_worker;
 };
 
 enum wiiproto_reqs {
@@ -181,6 +200,11 @@ static inline int wiimote_cmd_acquire(struct wiimote_data *wdata)
 	return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0;
 }
 
+static inline void wiimote_cmd_acquire_noint(struct wiimote_data *wdata)
+{
+	mutex_lock(&wdata->state.sync);
+}
+
 /* requires the state.lock spinlock to be held */
 static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd,
 								__u32 opt)
@@ -208,4 +232,15 @@ static inline int wiimote_cmd_wait(struct wiimote_data *wdata)
 		return 0;
 }
 
+static inline int wiimote_cmd_wait_noint(struct wiimote_data *wdata)
+{
+	unsigned long ret;
+
+	ret = wait_for_completion_timeout(&wdata->state.ready, HZ);
+	if (!ret)
+		return -EIO;
+	else
+		return 0;
+}
+
 #endif
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 05/26] HID: wiimote: use cached battery values on I/O failure
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (3 preceding siblings ...)
  2013-05-05 21:12 ` [PATCH 04/26] HID: wiimote: add device detection David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-05-05 21:12 ` [PATCH 06/26] HID: wiimote: wake up if output queue failed David Herrmann
                   ` (21 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

Battery reports are sent along every status report of the Wii Remote.
So chances are pretty high that we have an up-to-date battery
cache at any time. Therefore, initialize the battery-cache to 100% and
then return battery values from the cache if the query fails.

This works around a power_supply limitation in that it requires us to be
able to query the device during power_supply registration and
removal. Otherwise, "add" or "remove" udev events are not sent. If
we answer these requests from our cache instead, we avoid dropping these
events and no longer cause warnings printed.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 76d2c73..00a9b6f 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -461,12 +461,12 @@ static int wiimote_battery_get_property(struct power_supply *psy,
 	wiiproto_req_status(wdata);
 	spin_unlock_irqrestore(&wdata->state.lock, flags);
 
-	ret = wiimote_cmd_wait(wdata);
-	state = wdata->state.cmd_battery;
+	wiimote_cmd_wait(wdata);
 	wiimote_cmd_release(wdata);
 
-	if (ret)
-		return ret;
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	state = wdata->state.cmd_battery;
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
 
 	switch (psp) {
 		case POWER_SUPPLY_PROP_CAPACITY:
@@ -923,10 +923,9 @@ static void handler_status(struct wiimote_data *wdata, const __u8 *payload)
 		wiiext_event(wdata, false);
 	}
 
-	if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0)) {
-		wdata->state.cmd_battery = payload[5];
+	wdata->state.cmd_battery = payload[5];
+	if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0))
 		wiimote_cmd_complete(wdata);
-	}
 }
 
 /* reduced generic report with "BB BB" key data only */
@@ -1279,6 +1278,7 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 	init_completion(&wdata->state.ready);
 	mutex_init(&wdata->state.sync);
 	wdata->state.drm = WIIPROTO_REQ_DRM_K;
+	wdata->state.cmd_battery = 0xff;
 
 	INIT_WORK(&wdata->init_worker, wiimote_init_worker);
 
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 06/26] HID: wiimote: wake up if output queue failed
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (4 preceding siblings ...)
  2013-05-05 21:12 ` [PATCH 05/26] HID: wiimote: use cached battery values on I/O failure David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-05-05 21:12 ` [PATCH 07/26] HID: wiimote: add sub-device module infrastructure David Herrmann
                   ` (20 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

Our output queue is asynchronous but synchronous reports may wait for a
response to their request. Therefore, wake them up unconditionally if an
output report couldn't be sent. But keep the report ID intact so we don't
incorrectly assume our request succeeded.

Note that the underlying connection is required to be reliable and does
retransmission itself. So it is safe to assume that if the transmission
fails, the device is in inconsistent state. Hence, we abort every request
if any output report fails. No need to verify which report failed.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c | 24 +++++++++++++++++++-----
 drivers/hid/hid-wiimote.h      | 19 +++++++++++++++++++
 2 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 00a9b6f..a025d21 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -58,11 +58,11 @@ static enum power_supply_property wiimote_battery_props[] = {
 
 /* output queue handling */
 
-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;
@@ -84,14 +84,20 @@ static void wiimote_queue_worker(struct work_struct *work)
 	struct wiimote_data *wdata = container_of(queue, struct wiimote_data,
 						  queue);
 	unsigned long flags;
+	int ret;
 
 	spin_lock_irqsave(&wdata->queue.lock, flags);
 
 	while (wdata->queue.head != wdata->queue.tail) {
 		spin_unlock_irqrestore(&wdata->queue.lock, flags);
-		wiimote_hid_send(wdata->hdev,
+		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->queue.tail = (wdata->queue.tail + 1) % WIIMOTE_BUFSIZE;
@@ -108,7 +114,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;
 	}
 
 	/*
@@ -134,8 +142,14 @@ static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
 		wdata->queue.head = newhead;
 	} else {
 		hid_warn(wdata->hdev, "Output queue is full");
+		goto out_error;
 	}
 
+	goto out_unlock;
+
+out_error:
+	wiimote_cmd_abort(wdata);
+out_unlock:
 	spin_unlock_irqrestore(&wdata->queue.lock, flags);
 }
 
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 301607d..3441702 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -195,6 +195,16 @@ static inline void wiimote_cmd_complete(struct wiimote_data *wdata)
 	complete(&wdata->state.ready);
 }
 
+/* requires the state.lock spinlock to be held */
+static inline void wiimote_cmd_abort(struct wiimote_data *wdata)
+{
+	/* Abort synchronous request by waking up the sleeping caller. But
+	 * reset the state.cmd field to an invalid value so no further event
+	 * handlers will work with it. */
+	wdata->state.cmd = WIIPROTO_REQ_MAX;
+	complete(&wdata->state.ready);
+}
+
 static inline int wiimote_cmd_acquire(struct wiimote_data *wdata)
 {
 	return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0;
@@ -223,11 +233,17 @@ static inline int wiimote_cmd_wait(struct wiimote_data *wdata)
 {
 	int ret;
 
+	/* The completion acts as implicit memory barrier so we can safely
+	 * assume that state.cmd is set on success/failure and isn't accessed
+	 * by any other thread, anymore. */
+
 	ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ);
 	if (ret < 0)
 		return -ERESTARTSYS;
 	else if (ret == 0)
 		return -EIO;
+	else if (wdata->state.cmd != WIIPROTO_REQ_NULL)
+		return -EIO;
 	else
 		return 0;
 }
@@ -236,9 +252,12 @@ static inline int wiimote_cmd_wait_noint(struct wiimote_data *wdata)
 {
 	unsigned long ret;
 
+	/* no locking needed; see wiimote_cmd_wait() */
 	ret = wait_for_completion_timeout(&wdata->state.ready, HZ);
 	if (!ret)
 		return -EIO;
+	else if (wdata->state.cmd != WIIPROTO_REQ_NULL)
+		return -EIO;
 	else
 		return 0;
 }
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 07/26] HID: wiimote: add sub-device module infrastructure
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (5 preceding siblings ...)
  2013-05-05 21:12 ` [PATCH 06/26] HID: wiimote: wake up if output queue failed David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-05-05 21:12 ` [PATCH 08/26] HID: wiimote: convert KEYS and RUMBLE to modules David Herrmann
                   ` (19 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

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>
---
 drivers/hid/Makefile              |   2 +-
 drivers/hid/hid-wiimote-core.c    | 124 +++++++++++++++++++++++++++++++++++++-
 drivers/hid/hid-wiimote-modules.c |  45 ++++++++++++++
 drivers/hid/hid-wiimote.h         |  27 +++++++++
 4 files changed, 194 insertions(+), 4 deletions(-)
 create mode 100644 drivers/hid/hid-wiimote-modules.c

diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 2735151..6015af5 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -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
diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index a025d21..275428b 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)
 	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);
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
new file mode 100644
index 0000000..5dcdd23
--- /dev/null
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -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] = {
+};
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 3441702..3c94e3c 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -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,
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 08/26] HID: wiimote: convert KEYS and RUMBLE to modules
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (6 preceding siblings ...)
  2013-05-05 21:12 ` [PATCH 07/26] HID: wiimote: add sub-device module infrastructure David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-05-05 21:12 ` [PATCH 09/26] HID: wiimote: convert BATTERY to module David Herrmann
                   ` (18 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

This introduces the first sub-device modules by converting the KEYS and
RUMBLE sub-devices into wiimote modules. Both must be converted at once
because they depend on the built-in shared input device.

This mostly moves code from wiimote-core to wiimote-modules and doesn't
change any semantics or ABI.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c    | 134 ++++++--------------------------------
 drivers/hid/hid-wiimote-modules.c | 134 ++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-wiimote.h         |  18 +++++
 3 files changed, 172 insertions(+), 114 deletions(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 275428b..6ada226 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -22,35 +22,6 @@
 #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 */
-};
-
 static enum power_supply_property wiimote_battery_props[] = {
 	POWER_SUPPLY_PROP_CAPACITY,
 	POWER_SUPPLY_PROP_SCOPE,
@@ -166,7 +137,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];
 
@@ -654,31 +625,6 @@ static void wiimote_leds_set(struct led_classdev *led_dev,
 	}
 }
 
-static int wiimote_ff_play(struct input_dev *dev, void *data,
-							struct ff_effect *eff)
-{
-	struct wiimote_data *wdata = input_get_drvdata(dev);
-	__u8 value;
-	unsigned long flags;
-
-	/*
-	 * 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 (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude)
-		value = 1;
-	else
-		value = 0;
-
-	spin_lock_irqsave(&wdata->state.lock, flags);
-	wiiproto_req_rumble(wdata, value);
-	spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-	return 0;
-}
-
 static int wiimote_accel_open(struct input_dev *dev)
 {
 	struct wiimote_data *wdata = input_get_drvdata(dev);
@@ -725,12 +671,18 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_GENERIC] = (const __u8[]){
+		WIIMOD_KEYS,
+		WIIMOD_RUMBLE,
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_GEN10] = (const __u8[]){
+		WIIMOD_KEYS,
+		WIIMOD_RUMBLE,
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_GEN20] = (const __u8[]){
+		WIIMOD_KEYS,
+		WIIMOD_RUMBLE,
 		WIIMOD_NULL,
 	},
 };
@@ -933,29 +885,17 @@ static void wiimote_init_worker(struct work_struct *work)
 
 static void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
 {
-	input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_LEFT],
-							!!(payload[0] & 0x01));
-	input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_RIGHT],
-							!!(payload[0] & 0x02));
-	input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_DOWN],
-							!!(payload[0] & 0x04));
-	input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_UP],
-							!!(payload[0] & 0x08));
-	input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_PLUS],
-							!!(payload[0] & 0x10));
-	input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_TWO],
-							!!(payload[1] & 0x01));
-	input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_ONE],
-							!!(payload[1] & 0x02));
-	input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_B],
-							!!(payload[1] & 0x04));
-	input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_A],
-							!!(payload[1] & 0x08));
-	input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_MINUS],
-							!!(payload[1] & 0x10));
-	input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_HOME],
-							!!(payload[1] & 0x80));
-	input_sync(wdata->input);
+	const __u8 *iter, *mods;
+	const struct wiimod_ops *ops;
+
+	mods = wiimote_devtype_mods[wdata->state.devtype];
+	for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
+		ops = wiimod_table[*iter];
+		if (ops->in_keys) {
+			ops->in_keys(wdata, payload);
+			break;
+		}
+	}
 }
 
 static void handler_accel(struct wiimote_data *wdata, const __u8 *payload)
@@ -1319,38 +1259,17 @@ err:
 static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 {
 	struct wiimote_data *wdata;
-	int i;
 
 	wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
 	if (!wdata)
 		return NULL;
 
-	wdata->input = input_allocate_device();
-	if (!wdata->input)
-		goto err;
-
 	wdata->hdev = hdev;
 	hid_set_drvdata(hdev, wdata);
 
-	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;
-
-	set_bit(EV_KEY, wdata->input->evbit);
-	for (i = 0; i < WIIPROTO_KEY_COUNT; ++i)
-		set_bit(wiiproto_keymap[i], wdata->input->keybit);
-
-	set_bit(FF_RUMBLE, wdata->input->ffbit);
-	if (input_ff_create_memless(wdata->input, NULL, wiimote_ff_play))
-		goto err_input;
-
 	wdata->accel = input_allocate_device();
 	if (!wdata->accel)
-		goto err_input;
+		goto err;
 
 	input_set_drvdata(wdata->accel, wdata);
 	wdata->accel->open = wiimote_accel_open;
@@ -1417,8 +1336,6 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 
 err_ir:
 	input_free_device(wdata->accel);
-err_input:
-	input_free_device(wdata->input);
 err:
 	kfree(wdata);
 	return NULL;
@@ -1430,13 +1347,12 @@ static void wiimote_destroy(struct wiimote_data *wdata)
 	wiiext_deinit(wdata);
 	wiimote_leds_destroy(wdata);
 
+	cancel_work_sync(&wdata->init_worker);
 	wiimote_modules_unload(wdata);
 	power_supply_unregister(&wdata->battery);
 	kfree(wdata->battery.name);
 	input_unregister_device(wdata->accel);
 	input_unregister_device(wdata->ir);
-	input_unregister_device(wdata->input);
-	cancel_work_sync(&wdata->init_worker);
 	cancel_work_sync(&wdata->queue.worker);
 	hid_hw_close(wdata->hdev);
 	hid_hw_stop(wdata->hdev);
@@ -1488,12 +1404,6 @@ static int wiimote_hid_probe(struct hid_device *hdev,
 		goto err_ir;
 	}
 
-	ret = input_register_device(wdata->input);
-	if (ret) {
-		hid_err(hdev, "Cannot register input device\n");
-		goto err_input;
-	}
-
 	wdata->battery.properties = wiimote_battery_props;
 	wdata->battery.num_properties = ARRAY_SIZE(wiimote_battery_props);
 	wdata->battery.get_property = wiimote_battery_get_property;
@@ -1545,9 +1455,6 @@ err_free:
 err_battery:
 	kfree(wdata->battery.name);
 err_battery_name:
-	input_unregister_device(wdata->input);
-	wdata->input = NULL;
-err_input:
 	input_unregister_device(wdata->ir);
 	wdata->ir = NULL;
 err_ir:
@@ -1560,7 +1467,6 @@ err_stop:
 err:
 	input_free_device(wdata->ir);
 	input_free_device(wdata->accel);
-	input_free_device(wdata->input);
 	kfree(wdata);
 	return ret;
 }
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index 5dcdd23..616f240 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -39,7 +39,141 @@
 #include <linux/spinlock.h>
 #include "hid-wiimote.h"
 
+/*
+ * Keys
+ * The initial Wii Remote provided a bunch of buttons that are reported as
+ * part of the core protocol. Many later devices dropped these and report
+ * invalid data in the core button reports. Load this only on devices which
+ * correctly send button reports.
+ * It uses the shared input device.
+ */
+
+static const __u16 wiimod_keys_map[] = {
+	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 */
+};
+
+static void wiimod_keys_in_keys(struct wiimote_data *wdata, const __u8 *keys)
+{
+	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_LEFT],
+							!!(keys[0] & 0x01));
+	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_RIGHT],
+							!!(keys[0] & 0x02));
+	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_DOWN],
+							!!(keys[0] & 0x04));
+	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_UP],
+							!!(keys[0] & 0x08));
+	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_PLUS],
+							!!(keys[0] & 0x10));
+	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_TWO],
+							!!(keys[1] & 0x01));
+	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_ONE],
+							!!(keys[1] & 0x02));
+	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_B],
+							!!(keys[1] & 0x04));
+	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_A],
+							!!(keys[1] & 0x08));
+	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_MINUS],
+							!!(keys[1] & 0x10));
+	input_report_key(wdata->input, wiimod_keys_map[WIIPROTO_KEY_HOME],
+							!!(keys[1] & 0x80));
+	input_sync(wdata->input);
+}
+
+static int wiimod_keys_probe(const struct wiimod_ops *ops,
+			     struct wiimote_data *wdata)
+{
+	unsigned int i;
+
+	set_bit(EV_KEY, wdata->input->evbit);
+	for (i = 0; i < WIIPROTO_KEY_COUNT; ++i)
+		set_bit(wiimod_keys_map[i], wdata->input->keybit);
+
+	return 0;
+}
+
+static const struct wiimod_ops wiimod_keys = {
+	.flags = WIIMOD_FLAG_INPUT,
+	.arg = 0,
+	.probe = wiimod_keys_probe,
+	.remove = NULL,
+	.in_keys = wiimod_keys_in_keys,
+};
+
+/*
+ * Rumble
+ * Nearly all devices provide a rumble feature. A small motor for
+ * force-feedback effects. We provide an FF_RUMBLE memless ff device on the
+ * shared input device if this module is loaded.
+ * The rumble motor is controlled via a flag on almost every output report so
+ * the wiimote core handles the rumble flag. But if a device doesn't provide
+ * the rumble motor, this flag shouldn't be set.
+ */
+
+static int wiimod_rumble_play(struct input_dev *dev, void *data,
+			      struct ff_effect *eff)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+	__u8 value;
+	unsigned long flags;
+
+	/*
+	 * 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 (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude)
+		value = 1;
+	else
+		value = 0;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wiiproto_req_rumble(wdata, value);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	return 0;
+}
+
+static int wiimod_rumble_probe(const struct wiimod_ops *ops,
+			       struct wiimote_data *wdata)
+{
+	set_bit(FF_RUMBLE, wdata->input->ffbit);
+	if (input_ff_create_memless(wdata->input, NULL, wiimod_rumble_play))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void wiimod_rumble_remove(const struct wiimod_ops *ops,
+				 struct wiimote_data *wdata)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wiiproto_req_rumble(wdata, 0);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+static const struct wiimod_ops wiimod_rumble = {
+	.flags = WIIMOD_FLAG_INPUT,
+	.arg = 0,
+	.probe = wiimod_rumble_probe,
+	.remove = wiimod_rumble_remove,
+};
+
 /* module table */
 
 const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
+	[WIIMOD_KEYS] = &wiimod_keys,
+	[WIIMOD_RUMBLE] = &wiimod_rumble,
 };
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 3c94e3c..93c48fb 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -45,6 +45,21 @@
 /* return flag for led \num */
 #define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1))
 
+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
+};
+
 enum wiimote_devtype {
 	WIIMOTE_DEV_PENDING,
 	WIIMOTE_DEV_UNKNOWN,
@@ -111,6 +126,8 @@ struct wiimote_data {
 /* wiimote modules */
 
 enum wiimod_module {
+	WIIMOD_KEYS,
+	WIIMOD_RUMBLE,
 	WIIMOD_NUM,
 	WIIMOD_NULL = WIIMOD_NUM,
 };
@@ -166,6 +183,7 @@ enum wiiproto_reqs {
 									dev))
 
 extern void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm);
+extern void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble);
 extern int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
 						const __u8 *wmem, __u8 size);
 extern ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset,
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 09/26] HID: wiimote: convert BATTERY to module
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (7 preceding siblings ...)
  2013-05-05 21:12 ` [PATCH 08/26] HID: wiimote: convert KEYS and RUMBLE to modules David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-05-05 21:12 ` [PATCH 10/26] HID: wiimote: convert LEDS to modules David Herrmann
                   ` (17 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

This introduces a new sub-device module for the BATTERY handlers. It
moves the whole power_supply battery handling over to wiimote-modules.

This doesn't change any semantics or ABI but only converts the battery
handling into a sub-device module.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c    | 80 ++------------------------------
 drivers/hid/hid-wiimote-modules.c | 98 +++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-wiimote.h         |  2 +
 3 files changed, 104 insertions(+), 76 deletions(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 6ada226..bb1b3e3 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -17,16 +17,10 @@
 #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"
 
-static enum power_supply_property wiimote_battery_props[] = {
-	POWER_SUPPLY_PROP_CAPACITY,
-	POWER_SUPPLY_PROP_SCOPE,
-};
-
 /* output queue handling */
 
 static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
@@ -232,7 +226,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];
 
@@ -423,48 +417,6 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata)
 	return WIIMOTE_EXT_UNKNOWN;
 }
 
-static int wiimote_battery_get_property(struct power_supply *psy,
-						enum power_supply_property psp,
-						union power_supply_propval *val)
-{
-	struct wiimote_data *wdata = container_of(psy,
-						struct wiimote_data, battery);
-	int ret = 0, state;
-	unsigned long flags;
-
-	if (psp == POWER_SUPPLY_PROP_SCOPE) {
-		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
-		return 0;
-	}
-
-	ret = wiimote_cmd_acquire(wdata);
-	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);
-
-	wiimote_cmd_wait(wdata);
-	wiimote_cmd_release(wdata);
-
-	spin_lock_irqsave(&wdata->state.lock, flags);
-	state = wdata->state.cmd_battery;
-	spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-	switch (psp) {
-		case POWER_SUPPLY_PROP_CAPACITY:
-			val->intval = state * 100 / 255;
-			break;
-		default:
-			ret = -EINVAL;
-			break;
-	}
-
-	return ret;
-}
-
 static int wiimote_init_ir(struct wiimote_data *wdata, __u16 mode)
 {
 	int ret;
@@ -673,16 +625,19 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 	[WIIMOTE_DEV_GENERIC] = (const __u8[]){
 		WIIMOD_KEYS,
 		WIIMOD_RUMBLE,
+		WIIMOD_BATTERY,
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_GEN10] = (const __u8[]){
 		WIIMOD_KEYS,
 		WIIMOD_RUMBLE,
+		WIIMOD_BATTERY,
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_GEN20] = (const __u8[]){
 		WIIMOD_KEYS,
 		WIIMOD_RUMBLE,
+		WIIMOD_BATTERY,
 		WIIMOD_NULL,
 	},
 };
@@ -1349,8 +1304,6 @@ static void wiimote_destroy(struct wiimote_data *wdata)
 
 	cancel_work_sync(&wdata->init_worker);
 	wiimote_modules_unload(wdata);
-	power_supply_unregister(&wdata->battery);
-	kfree(wdata->battery.name);
 	input_unregister_device(wdata->accel);
 	input_unregister_device(wdata->ir);
 	cancel_work_sync(&wdata->queue.worker);
@@ -1404,26 +1357,6 @@ static int wiimote_hid_probe(struct hid_device *hdev,
 		goto err_ir;
 	}
 
-	wdata->battery.properties = wiimote_battery_props;
-	wdata->battery.num_properties = ARRAY_SIZE(wiimote_battery_props);
-	wdata->battery.get_property = wiimote_battery_get_property;
-	wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
-	wdata->battery.use_for_apm = 0;
-	wdata->battery.name = kasprintf(GFP_KERNEL, "wiimote_battery_%s",
-					wdata->hdev->uniq);
-	if (!wdata->battery.name) {
-		ret = -ENOMEM;
-		goto err_battery_name;
-	}
-
-	ret = power_supply_register(&wdata->hdev->dev, &wdata->battery);
-	if (ret) {
-		hid_err(hdev, "Cannot register battery device\n");
-		goto err_battery;
-	}
-
-	power_supply_powers(&wdata->battery, &hdev->dev);
-
 	ret = wiimote_leds_create(wdata);
 	if (ret)
 		goto err_free;
@@ -1452,11 +1385,6 @@ err_free:
 	wiimote_destroy(wdata);
 	return ret;
 
-err_battery:
-	kfree(wdata->battery.name);
-err_battery_name:
-	input_unregister_device(wdata->ir);
-	wdata->ir = NULL;
 err_ir:
 	input_unregister_device(wdata->accel);
 	wdata->accel = NULL;
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index 616f240..4cbbbe6 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -171,9 +171,107 @@ static const struct wiimod_ops wiimod_rumble = {
 	.remove = wiimod_rumble_remove,
 };
 
+/*
+ * Battery
+ * 1 byte of battery capacity information is sent along every protocol status
+ * report. The wiimote core caches it but we try to update it on every
+ * user-space request.
+ * This is supported by nearly every device so it's almost always enabled.
+ */
+
+static enum power_supply_property wiimod_battery_props[] = {
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_SCOPE,
+};
+
+static int wiimod_battery_get_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       union power_supply_propval *val)
+{
+	struct wiimote_data *wdata = container_of(psy, struct wiimote_data,
+						  battery);
+	int ret = 0, state;
+	unsigned long flags;
+
+	if (psp == POWER_SUPPLY_PROP_SCOPE) {
+		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+		return 0;
+	} else if (psp != POWER_SUPPLY_PROP_CAPACITY) {
+		return -EINVAL;
+	}
+
+	ret = wiimote_cmd_acquire(wdata);
+	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);
+
+	wiimote_cmd_wait(wdata);
+	wiimote_cmd_release(wdata);
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	state = wdata->state.cmd_battery;
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	val->intval = state * 100 / 255;
+	return ret;
+}
+
+static int wiimod_battery_probe(const struct wiimod_ops *ops,
+				struct wiimote_data *wdata)
+{
+	int ret;
+
+	wdata->battery.properties = wiimod_battery_props;
+	wdata->battery.num_properties = ARRAY_SIZE(wiimod_battery_props);
+	wdata->battery.get_property = wiimod_battery_get_property;
+	wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+	wdata->battery.use_for_apm = 0;
+	wdata->battery.name = kasprintf(GFP_KERNEL, "wiimote_battery_%s",
+					wdata->hdev->uniq);
+	if (!wdata->battery.name)
+		return -ENOMEM;
+
+	ret = power_supply_register(&wdata->hdev->dev, &wdata->battery);
+	if (ret) {
+		hid_err(wdata->hdev, "cannot register battery device\n");
+		goto err_free;
+	}
+
+	power_supply_powers(&wdata->battery, &wdata->hdev->dev);
+	return 0;
+
+err_free:
+	kfree(wdata->battery.name);
+	wdata->battery.name = NULL;
+	return ret;
+}
+
+static void wiimod_battery_remove(const struct wiimod_ops *ops,
+				  struct wiimote_data *wdata)
+{
+	if (!wdata->battery.name)
+		return;
+
+	power_supply_unregister(&wdata->battery);
+	kfree(wdata->battery.name);
+	wdata->battery.name = NULL;
+}
+
+static const struct wiimod_ops wiimod_battery = {
+	.flags = 0,
+	.arg = 0,
+	.probe = wiimod_battery_probe,
+	.remove = wiimod_battery_remove,
+};
+
 /* module table */
 
 const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
 	[WIIMOD_KEYS] = &wiimod_keys,
 	[WIIMOD_RUMBLE] = &wiimod_rumble,
+	[WIIMOD_BATTERY] = &wiimod_battery,
 };
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 93c48fb..4151514 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -128,6 +128,7 @@ struct wiimote_data {
 enum wiimod_module {
 	WIIMOD_KEYS,
 	WIIMOD_RUMBLE,
+	WIIMOD_BATTERY,
 	WIIMOD_NUM,
 	WIIMOD_NULL = WIIMOD_NUM,
 };
@@ -184,6 +185,7 @@ enum wiiproto_reqs {
 
 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_status(struct wiimote_data *wdata);
 extern int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
 						const __u8 *wmem, __u8 size);
 extern ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset,
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 10/26] HID: wiimote: convert LEDS to modules
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (8 preceding siblings ...)
  2013-05-05 21:12 ` [PATCH 09/26] HID: wiimote: convert BATTERY to module David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-05-05 21:12 ` [PATCH 11/26] HID: wiimote: convert ACCEL to module David Herrmann
                   ` (16 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

Each of the 4 LEDs may be supported individually by devices. Therefore,
we need one module for each device. To avoid code-duplication, we simply
pass the LED ID as "arg" argument to the module loading code.

This just moves the code over to wiimote-module. The semantics stay the
same as before.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c    | 125 ++++------------------------------
 drivers/hid/hid-wiimote-modules.c | 140 ++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-wiimote.h         |   5 ++
 3 files changed, 158 insertions(+), 112 deletions(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index bb1b3e3..4f58c78 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -14,7 +14,6 @@
 #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/spinlock.h>
@@ -151,7 +150,7 @@ 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];
 
@@ -529,54 +528,6 @@ unlock:
 	return ret;
 }
 
-static enum led_brightness wiimote_leds_get(struct led_classdev *led_dev)
-{
-	struct wiimote_data *wdata;
-	struct device *dev = led_dev->dev->parent;
-	int i;
-	unsigned long flags;
-	bool value = false;
-
-	wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
-
-	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;
-		}
-	}
-
-	return value ? LED_FULL : LED_OFF;
-}
-
-static void wiimote_leds_set(struct led_classdev *led_dev,
-						enum led_brightness value)
-{
-	struct wiimote_data *wdata;
-	struct device *dev = led_dev->dev->parent;
-	int i;
-	unsigned long flags;
-	__u8 state, flag;
-
-	wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
-
-	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;
-		}
-	}
-}
-
 static int wiimote_accel_open(struct input_dev *dev)
 {
 	struct wiimote_data *wdata = input_get_drvdata(dev);
@@ -626,18 +577,30 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		WIIMOD_KEYS,
 		WIIMOD_RUMBLE,
 		WIIMOD_BATTERY,
+		WIIMOD_LED1,
+		WIIMOD_LED2,
+		WIIMOD_LED3,
+		WIIMOD_LED4,
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_GEN10] = (const __u8[]){
 		WIIMOD_KEYS,
 		WIIMOD_RUMBLE,
 		WIIMOD_BATTERY,
+		WIIMOD_LED1,
+		WIIMOD_LED2,
+		WIIMOD_LED3,
+		WIIMOD_LED4,
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_GEN20] = (const __u8[]){
 		WIIMOD_KEYS,
 		WIIMOD_RUMBLE,
 		WIIMOD_BATTERY,
+		WIIMOD_LED1,
+		WIIMOD_LED2,
+		WIIMOD_LED3,
+		WIIMOD_LED4,
 		WIIMOD_NULL,
 	},
 };
@@ -1159,58 +1122,6 @@ static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report,
 	return 0;
 }
 
-static void wiimote_leds_destroy(struct wiimote_data *wdata)
-{
-	int i;
-	struct led_classdev *led;
-
-	for (i = 0; i < 4; ++i) {
-		if (wdata->leds[i]) {
-			led = wdata->leds[i];
-			wdata->leds[i] = NULL;
-			led_classdev_unregister(led);
-			kfree(led);
-		}
-	}
-}
-
-static int wiimote_leds_create(struct wiimote_data *wdata)
-{
-	int i, ret;
-	struct device *dev = &wdata->hdev->dev;
-	size_t namesz = strlen(dev_name(dev)) + 9;
-	struct led_classdev *led;
-	char *name;
-
-	for (i = 0; i < 4; ++i) {
-		led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL);
-		if (!led) {
-			ret = -ENOMEM;
-			goto err;
-		}
-		name = (void*)&led[1];
-		snprintf(name, namesz, "%s:blue:p%d", dev_name(dev), i);
-		led->name = name;
-		led->brightness = 0;
-		led->max_brightness = 1;
-		led->brightness_get = wiimote_leds_get;
-		led->brightness_set = wiimote_leds_set;
-
-		ret = led_classdev_register(dev, led);
-		if (ret) {
-			kfree(led);
-			goto err;
-		}
-		wdata->leds[i] = led;
-	}
-
-	return 0;
-
-err:
-	wiimote_leds_destroy(wdata);
-	return ret;
-}
-
 static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 {
 	struct wiimote_data *wdata;
@@ -1300,7 +1211,6 @@ static void wiimote_destroy(struct wiimote_data *wdata)
 {
 	wiidebug_deinit(wdata);
 	wiiext_deinit(wdata);
-	wiimote_leds_destroy(wdata);
 
 	cancel_work_sync(&wdata->init_worker);
 	wiimote_modules_unload(wdata);
@@ -1357,10 +1267,6 @@ static int wiimote_hid_probe(struct hid_device *hdev,
 		goto err_ir;
 	}
 
-	ret = wiimote_leds_create(wdata);
-	if (ret)
-		goto err_free;
-
 	ret = wiiext_init(wdata);
 	if (ret)
 		goto err_free;
@@ -1371,11 +1277,6 @@ static int wiimote_hid_probe(struct hid_device *hdev,
 
 	hid_info(hdev, "New device registered\n");
 
-	/* by default set led1 after device initialization */
-	spin_lock_irq(&wdata->state.lock);
-	wiiproto_req_leds(wdata, WIIPROTO_FLAG_LED1);
-	spin_unlock_irq(&wdata->state.lock);
-
 	/* schedule device detection */
 	schedule_work(&wdata->init_worker);
 
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index 4cbbbe6..f96de15 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -268,10 +268,150 @@ static const struct wiimod_ops wiimod_battery = {
 	.remove = wiimod_battery_remove,
 };
 
+/*
+ * LED
+ * 0 to 4 player LEDs are supported by devices. The "arg" field of the
+ * wiimod_ops structure specifies which LED this module controls. This allows
+ * to register a limited number of LEDs.
+ * State is managed by wiimote core.
+ */
+
+static enum led_brightness wiimod_led_get(struct led_classdev *led_dev)
+{
+	struct wiimote_data *wdata;
+	struct device *dev = led_dev->dev->parent;
+	int i;
+	unsigned long flags;
+	bool value = false;
+
+	wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
+
+	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;
+		}
+	}
+
+	return value ? LED_FULL : LED_OFF;
+}
+
+static void wiimod_led_set(struct led_classdev *led_dev,
+			   enum led_brightness value)
+{
+	struct wiimote_data *wdata;
+	struct device *dev = led_dev->dev->parent;
+	int i;
+	unsigned long flags;
+	__u8 state, flag;
+
+	wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
+
+	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;
+		}
+	}
+}
+
+static int wiimod_led_probe(const struct wiimod_ops *ops,
+			    struct wiimote_data *wdata)
+{
+	struct device *dev = &wdata->hdev->dev;
+	size_t namesz = strlen(dev_name(dev)) + 9;
+	struct led_classdev *led;
+	unsigned long flags;
+	char *name;
+	int ret;
+
+	led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	name = (void*)&led[1];
+	snprintf(name, namesz, "%s:blue:p%lu", dev_name(dev), ops->arg);
+	led->name = name;
+	led->brightness = 0;
+	led->max_brightness = 1;
+	led->brightness_get = wiimod_led_get;
+	led->brightness_set = wiimod_led_set;
+
+	wdata->leds[ops->arg] = led;
+	ret = led_classdev_register(dev, led);
+	if (ret)
+		goto err_free;
+
+	/* enable LED1 to stop initial LED-blinking */
+	if (ops->arg == 0) {
+		spin_lock_irqsave(&wdata->state.lock, flags);
+		wiiproto_req_leds(wdata, WIIPROTO_FLAG_LED1);
+		spin_unlock_irqrestore(&wdata->state.lock, flags);
+	}
+
+	return 0;
+
+err_free:
+	wdata->leds[ops->arg] = NULL;
+	kfree(led);
+	return ret;
+}
+
+static void wiimod_led_remove(const struct wiimod_ops *ops,
+			      struct wiimote_data *wdata)
+{
+	if (!wdata->leds[ops->arg])
+		return;
+
+	led_classdev_unregister(wdata->leds[ops->arg]);
+	kfree(wdata->leds[ops->arg]);
+	wdata->leds[ops->arg] = NULL;
+}
+
+static const struct wiimod_ops wiimod_leds[4] = {
+	{
+		.flags = 0,
+		.arg = 0,
+		.probe = wiimod_led_probe,
+		.remove = wiimod_led_remove,
+	},
+	{
+		.flags = 0,
+		.arg = 1,
+		.probe = wiimod_led_probe,
+		.remove = wiimod_led_remove,
+	},
+	{
+		.flags = 0,
+		.arg = 2,
+		.probe = wiimod_led_probe,
+		.remove = wiimod_led_remove,
+	},
+	{
+		.flags = 0,
+		.arg = 3,
+		.probe = wiimod_led_probe,
+		.remove = wiimod_led_remove,
+	},
+};
+
 /* module table */
 
 const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
 	[WIIMOD_KEYS] = &wiimod_keys,
 	[WIIMOD_RUMBLE] = &wiimod_rumble,
 	[WIIMOD_BATTERY] = &wiimod_battery,
+	[WIIMOD_LED1] = &wiimod_leds[0],
+	[WIIMOD_LED2] = &wiimod_leds[1],
+	[WIIMOD_LED3] = &wiimod_leds[2],
+	[WIIMOD_LED4] = &wiimod_leds[3],
 };
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 4151514..66150a6 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -129,6 +129,10 @@ enum wiimod_module {
 	WIIMOD_KEYS,
 	WIIMOD_RUMBLE,
 	WIIMOD_BATTERY,
+	WIIMOD_LED1,
+	WIIMOD_LED2,
+	WIIMOD_LED3,
+	WIIMOD_LED4,
 	WIIMOD_NUM,
 	WIIMOD_NULL = WIIMOD_NUM,
 };
@@ -185,6 +189,7 @@ enum wiiproto_reqs {
 
 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);
 extern void wiiproto_req_status(struct wiimote_data *wdata);
 extern int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
 						const __u8 *wmem, __u8 size);
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 11/26] HID: wiimote: convert ACCEL to module
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (9 preceding siblings ...)
  2013-05-05 21:12 ` [PATCH 10/26] HID: wiimote: convert LEDS to modules David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-05-05 21:12 ` [PATCH 12/26] HID: wiimote: convert IR " David Herrmann
                   ` (15 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

Accelerometer data is very similar to KEYS handling. Therefore, convert
all ACCEL related handling into a sub-device module similar to KEYS.

This doesn't change any semantics but only moves code over to
wiimote-modules.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c    | 101 +++++--------------------------
 drivers/hid/hid-wiimote-modules.c | 123 ++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-wiimote.h         |   2 +
 3 files changed, 140 insertions(+), 86 deletions(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 4f58c78..7c703e1 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -236,7 +236,7 @@ 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))
@@ -528,28 +528,6 @@ unlock:
 	return ret;
 }
 
-static int wiimote_accel_open(struct input_dev *dev)
-{
-	struct wiimote_data *wdata = input_get_drvdata(dev);
-	unsigned long flags;
-
-	spin_lock_irqsave(&wdata->state.lock, flags);
-	wiiproto_req_accel(wdata, true);
-	spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-	return 0;
-}
-
-static void wiimote_accel_close(struct input_dev *dev)
-{
-	struct wiimote_data *wdata = input_get_drvdata(dev);
-	unsigned long flags;
-
-	spin_lock_irqsave(&wdata->state.lock, flags);
-	wiiproto_req_accel(wdata, false);
-	spin_unlock_irqrestore(&wdata->state.lock, flags);
-}
-
 static int wiimote_ir_open(struct input_dev *dev)
 {
 	struct wiimote_data *wdata = input_get_drvdata(dev);
@@ -581,6 +559,7 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		WIIMOD_LED2,
 		WIIMOD_LED3,
 		WIIMOD_LED4,
+		WIIMOD_ACCEL,
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_GEN10] = (const __u8[]){
@@ -591,6 +570,7 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		WIIMOD_LED2,
 		WIIMOD_LED3,
 		WIIMOD_LED4,
+		WIIMOD_ACCEL,
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_GEN20] = (const __u8[]){
@@ -601,6 +581,7 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		WIIMOD_LED2,
 		WIIMOD_LED3,
 		WIIMOD_LED4,
+		WIIMOD_ACCEL,
 		WIIMOD_NULL,
 	},
 };
@@ -818,35 +799,17 @@ static void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
 
 static void handler_accel(struct wiimote_data *wdata, const __u8 *payload)
 {
-	__u16 x, y, z;
-
-	if (!(wdata->state.flags & WIIPROTO_FLAG_ACCEL))
-		return;
-
-	/*
-	 * payload is: BB BB XX YY ZZ
-	 * Accelerometer data is encoded into 3 10bit values. XX, YY and ZZ
-	 * contain the upper 8 bits of each value. The lower 2 bits are
-	 * contained in the buttons data BB BB.
-	 * Bits 6 and 7 of the first buttons byte BB is the lower 2 bits of the
-	 * X accel value. Bit 5 of the second buttons byte is the 2nd bit of Y
-	 * accel value and bit 6 is the second bit of the Z value.
-	 * The first bit of Y and Z values is not available and always set to 0.
-	 * 0x200 is returned on no movement.
-	 */
-
-	x = payload[2] << 2;
-	y = payload[3] << 2;
-	z = payload[4] << 2;
-
-	x |= (payload[0] >> 5) & 0x3;
-	y |= (payload[1] >> 4) & 0x2;
-	z |= (payload[1] >> 5) & 0x2;
+	const __u8 *iter, *mods;
+	const struct wiimod_ops *ops;
 
-	input_report_abs(wdata->accel, ABS_RX, x - 0x200);
-	input_report_abs(wdata->accel, ABS_RY, y - 0x200);
-	input_report_abs(wdata->accel, ABS_RZ, z - 0x200);
-	input_sync(wdata->accel);
+	mods = wiimote_devtype_mods[wdata->state.devtype];
+	for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
+		ops = wiimod_table[*iter];
+		if (ops->in_accel) {
+			ops->in_accel(wdata, payload);
+			break;
+		}
+	}
 }
 
 #define ir_to_input0(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
@@ -1133,31 +1096,9 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 	wdata->hdev = hdev;
 	hid_set_drvdata(hdev, wdata);
 
-	wdata->accel = input_allocate_device();
-	if (!wdata->accel)
-		goto err;
-
-	input_set_drvdata(wdata->accel, wdata);
-	wdata->accel->open = wiimote_accel_open;
-	wdata->accel->close = wiimote_accel_close;
-	wdata->accel->dev.parent = &wdata->hdev->dev;
-	wdata->accel->id.bustype = wdata->hdev->bus;
-	wdata->accel->id.vendor = wdata->hdev->vendor;
-	wdata->accel->id.product = wdata->hdev->product;
-	wdata->accel->id.version = wdata->hdev->version;
-	wdata->accel->name = WIIMOTE_NAME " Accelerometer";
-
-	set_bit(EV_ABS, wdata->accel->evbit);
-	set_bit(ABS_RX, wdata->accel->absbit);
-	set_bit(ABS_RY, wdata->accel->absbit);
-	set_bit(ABS_RZ, wdata->accel->absbit);
-	input_set_abs_params(wdata->accel, ABS_RX, -500, 500, 2, 4);
-	input_set_abs_params(wdata->accel, ABS_RY, -500, 500, 2, 4);
-	input_set_abs_params(wdata->accel, ABS_RZ, -500, 500, 2, 4);
-
 	wdata->ir = input_allocate_device();
 	if (!wdata->ir)
-		goto err_ir;
+		goto err;
 
 	input_set_drvdata(wdata->ir, wdata);
 	wdata->ir->open = wiimote_ir_open;
@@ -1200,8 +1141,6 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 
 	return wdata;
 
-err_ir:
-	input_free_device(wdata->accel);
 err:
 	kfree(wdata);
 	return NULL;
@@ -1214,7 +1153,6 @@ static void wiimote_destroy(struct wiimote_data *wdata)
 
 	cancel_work_sync(&wdata->init_worker);
 	wiimote_modules_unload(wdata);
-	input_unregister_device(wdata->accel);
 	input_unregister_device(wdata->ir);
 	cancel_work_sync(&wdata->queue.worker);
 	hid_hw_close(wdata->hdev);
@@ -1255,12 +1193,6 @@ static int wiimote_hid_probe(struct hid_device *hdev,
 		goto err_stop;
 	}
 
-	ret = input_register_device(wdata->accel);
-	if (ret) {
-		hid_err(hdev, "Cannot register input device\n");
-		goto err_close;
-	}
-
 	ret = input_register_device(wdata->ir);
 	if (ret) {
 		hid_err(hdev, "Cannot register input device\n");
@@ -1287,9 +1219,6 @@ err_free:
 	return ret;
 
 err_ir:
-	input_unregister_device(wdata->accel);
-	wdata->accel = NULL;
-err_close:
 	hid_hw_close(hdev);
 err_stop:
 	hid_hw_stop(hdev);
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index f96de15..fbc09c8 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -404,6 +404,128 @@ static const struct wiimod_ops wiimod_leds[4] = {
 	},
 };
 
+/*
+ * Accelerometer
+ * 3 axis accelerometer data is part of nearly all DRMs. If not supported by a
+ * device, it's mostly cleared to 0. This module parses this data and provides
+ * it via a separate input device.
+ */
+
+static void wiimod_accel_in_accel(struct wiimote_data *wdata,
+				  const __u8 *accel)
+{
+	__u16 x, y, z;
+
+	if (!(wdata->state.flags & WIIPROTO_FLAG_ACCEL))
+		return;
+
+	/*
+	 * payload is: BB BB XX YY ZZ
+	 * Accelerometer data is encoded into 3 10bit values. XX, YY and ZZ
+	 * contain the upper 8 bits of each value. The lower 2 bits are
+	 * contained in the buttons data BB BB.
+	 * Bits 6 and 7 of the first buttons byte BB is the lower 2 bits of the
+	 * X accel value. Bit 5 of the second buttons byte is the 2nd bit of Y
+	 * accel value and bit 6 is the second bit of the Z value.
+	 * The first bit of Y and Z values is not available and always set to 0.
+	 * 0x200 is returned on no movement.
+	 */
+
+	x = accel[2] << 2;
+	y = accel[3] << 2;
+	z = accel[4] << 2;
+
+	x |= (accel[0] >> 5) & 0x3;
+	y |= (accel[1] >> 4) & 0x2;
+	z |= (accel[1] >> 5) & 0x2;
+
+	input_report_abs(wdata->accel, ABS_RX, x - 0x200);
+	input_report_abs(wdata->accel, ABS_RY, y - 0x200);
+	input_report_abs(wdata->accel, ABS_RZ, z - 0x200);
+	input_sync(wdata->accel);
+}
+
+static int wiimod_accel_open(struct input_dev *dev)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wiiproto_req_accel(wdata, true);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	return 0;
+}
+
+static void wiimod_accel_close(struct input_dev *dev)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wiiproto_req_accel(wdata, false);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+static int wiimod_accel_probe(const struct wiimod_ops *ops,
+			      struct wiimote_data *wdata)
+{
+	int ret;
+
+	wdata->accel = input_allocate_device();
+	if (!wdata->accel)
+		return -ENOMEM;
+
+	input_set_drvdata(wdata->accel, wdata);
+	wdata->accel->open = wiimod_accel_open;
+	wdata->accel->close = wiimod_accel_close;
+	wdata->accel->dev.parent = &wdata->hdev->dev;
+	wdata->accel->id.bustype = wdata->hdev->bus;
+	wdata->accel->id.vendor = wdata->hdev->vendor;
+	wdata->accel->id.product = wdata->hdev->product;
+	wdata->accel->id.version = wdata->hdev->version;
+	wdata->accel->name = WIIMOTE_NAME " Accelerometer";
+
+	set_bit(EV_ABS, wdata->accel->evbit);
+	set_bit(ABS_RX, wdata->accel->absbit);
+	set_bit(ABS_RY, wdata->accel->absbit);
+	set_bit(ABS_RZ, wdata->accel->absbit);
+	input_set_abs_params(wdata->accel, ABS_RX, -500, 500, 2, 4);
+	input_set_abs_params(wdata->accel, ABS_RY, -500, 500, 2, 4);
+	input_set_abs_params(wdata->accel, ABS_RZ, -500, 500, 2, 4);
+
+	ret = input_register_device(wdata->accel);
+	if (ret) {
+		hid_err(wdata->hdev, "cannot register input device\n");
+		goto err_free;
+	}
+
+	return 0;
+
+err_free:
+	input_free_device(wdata->accel);
+	wdata->accel = NULL;
+	return ret;
+}
+
+static void wiimod_accel_remove(const struct wiimod_ops *ops,
+				struct wiimote_data *wdata)
+{
+	if (!wdata->accel)
+		return;
+
+	input_unregister_device(wdata->accel);
+	wdata->accel = NULL;
+}
+
+static const struct wiimod_ops wiimod_accel = {
+	.flags = 0,
+	.arg = 0,
+	.probe = wiimod_accel_probe,
+	.remove = wiimod_accel_remove,
+	.in_accel = wiimod_accel_in_accel,
+};
+
 /* module table */
 
 const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
@@ -414,4 +536,5 @@ const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
 	[WIIMOD_LED2] = &wiimod_leds[1],
 	[WIIMOD_LED3] = &wiimod_leds[2],
 	[WIIMOD_LED4] = &wiimod_leds[3],
+	[WIIMOD_ACCEL] = &wiimod_accel,
 };
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 66150a6..8c242e6 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -133,6 +133,7 @@ enum wiimod_module {
 	WIIMOD_LED2,
 	WIIMOD_LED3,
 	WIIMOD_LED4,
+	WIIMOD_ACCEL,
 	WIIMOD_NUM,
 	WIIMOD_NULL = WIIMOD_NUM,
 };
@@ -191,6 +192,7 @@ 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);
 extern void wiiproto_req_status(struct wiimote_data *wdata);
+extern void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel);
 extern int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
 						const __u8 *wmem, __u8 size);
 extern ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset,
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 12/26] HID: wiimote: convert IR to module
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (10 preceding siblings ...)
  2013-05-05 21:12 ` [PATCH 11/26] HID: wiimote: convert ACCEL to module David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-05-05 21:12 ` [PATCH 13/26] HID: wiimote: add extension hotplug support David Herrmann
                   ` (14 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

IR is the last piece that still is handled natively. This patch converts
it into a sub-device module like all other sub-devices. It mainly moves
code and doesn't change semantics.

We also implicitly sync IR data on ir_to_input3 now so the explicit
input_sync() calls are no longer needed.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c    | 233 +++------------------------------
 drivers/hid/hid-wiimote-modules.c | 263 ++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-wiimote.h         |   3 +
 3 files changed, 287 insertions(+), 212 deletions(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 7c703e1..1ea70c8 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -250,7 +250,7 @@ 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];
 
@@ -261,7 +261,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];
 
@@ -416,132 +416,6 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata)
 	return WIIMOTE_EXT_UNKNOWN;
 }
 
-static int wiimote_init_ir(struct wiimote_data *wdata, __u16 mode)
-{
-	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);
-
-	if (mode == (wdata->state.flags & WIIPROTO_FLAGS_IR)) {
-		spin_unlock_irqrestore(&wdata->state.lock, flags);
-		return 0;
-	}
-
-	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;
-	}
-
-	spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-	ret = wiimote_cmd_acquire(wdata);
-	if (ret)
-		return ret;
-
-	/* 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);
-
-	ret = wiimote_cmd_wait(wdata);
-	if (ret)
-		goto unlock;
-	if (wdata->state.cmd_err) {
-		ret = -EIO;
-		goto unlock;
-	}
-
-	/* 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);
-
-	ret = wiimote_cmd_wait(wdata);
-	if (ret)
-		goto unlock;
-	if (wdata->state.cmd_err) {
-		ret = -EIO;
-		goto unlock;
-	}
-
-	/* 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;
-
-	/* write first sensitivity block */
-	ret = wiimote_cmd_write(wdata, 0xb00000, data_sens1,
-							sizeof(data_sens1));
-	if (ret)
-		goto unlock;
-
-	/* write second sensitivity block */
-	ret = wiimote_cmd_write(wdata, 0xb0001a, data_sens2,
-							sizeof(data_sens2));
-	if (ret)
-		goto unlock;
-
-	/* 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;
-	}
-	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;
-
-	/* 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);
-	spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-unlock:
-	wiimote_cmd_release(wdata);
-	return ret;
-}
-
-static int wiimote_ir_open(struct input_dev *dev)
-{
-	struct wiimote_data *wdata = input_get_drvdata(dev);
-
-	return wiimote_init_ir(wdata, WIIPROTO_FLAG_IR_BASIC);
-}
-
-static void wiimote_ir_close(struct input_dev *dev)
-{
-	struct wiimote_data *wdata = input_get_drvdata(dev);
-
-	wiimote_init_ir(wdata, 0);
-}
-
 /* device module handling */
 
 static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
@@ -560,6 +434,7 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		WIIMOD_LED3,
 		WIIMOD_LED4,
 		WIIMOD_ACCEL,
+		WIIMOD_IR,
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_GEN10] = (const __u8[]){
@@ -571,6 +446,7 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		WIIMOD_LED3,
 		WIIMOD_LED4,
 		WIIMOD_ACCEL,
+		WIIMOD_IR,
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_GEN20] = (const __u8[]){
@@ -582,6 +458,7 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		WIIMOD_LED3,
 		WIIMOD_LED4,
 		WIIMOD_ACCEL,
+		WIIMOD_IR,
 		WIIMOD_NULL,
 	},
 };
@@ -812,43 +689,25 @@ static void handler_accel(struct wiimote_data *wdata, const __u8 *payload)
 	}
 }
 
-#define ir_to_input0(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
-							ABS_HAT0X, ABS_HAT0Y)
-#define ir_to_input1(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
-							ABS_HAT1X, ABS_HAT1Y)
-#define ir_to_input2(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
-							ABS_HAT2X, ABS_HAT2Y)
-#define ir_to_input3(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
-							ABS_HAT3X, ABS_HAT3Y)
-
-static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir,
-						bool packed, __u8 xid, __u8 yid)
-{
-	__u16 x, y;
-
-	if (!(wdata->state.flags & WIIPROTO_FLAGS_IR))
-		return;
+#define ir_to_input0(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 0)
+#define ir_to_input1(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 1)
+#define ir_to_input2(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 2)
+#define ir_to_input3(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 3)
 
-	/*
-	 * Basic IR data is encoded into 3 bytes. The first two bytes are the
-	 * lower 8 bit of the X/Y data, the 3rd byte contains the upper 2 bits
-	 * of both.
-	 * If data is packed, then the 3rd byte is put first and slightly
-	 * reordered. This allows to interleave packed and non-packed data to
-	 * have two IR sets in 5 bytes instead of 6.
-	 * The resulting 10bit X/Y values are passed to the ABS_HATXY input dev.
-	 */
+static void handler_ir(struct wiimote_data *wdata, const __u8 *payload,
+		       bool packed, unsigned int id)
+{
+	const __u8 *iter, *mods;
+	const struct wiimod_ops *ops;
 
-	if (packed) {
-		x = ir[1] | ((ir[0] & 0x03) << 8);
-		y = ir[2] | ((ir[0] & 0x0c) << 6);
-	} else {
-		x = ir[0] | ((ir[2] & 0x30) << 4);
-		y = ir[1] | ((ir[2] & 0xc0) << 2);
+	mods = wiimote_devtype_mods[wdata->state.devtype];
+	for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
+		ops = wiimod_table[*iter];
+		if (ops->in_ir) {
+			ops->in_ir(wdata, payload, packed, id);
+			break;
+		}
 	}
-
-	input_report_abs(wdata->ir, xid, x);
-	input_report_abs(wdata->ir, yid, y);
 }
 
 /* reduced status report with "BB BB" key data only */
@@ -943,7 +802,6 @@ static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload)
 	ir_to_input1(wdata, &payload[8], false);
 	ir_to_input2(wdata, &payload[11], false);
 	ir_to_input3(wdata, &payload[14], false);
-	input_sync(wdata->ir);
 }
 
 static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload)
@@ -959,7 +817,6 @@ static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload)
 	ir_to_input1(wdata, &payload[4], true);
 	ir_to_input2(wdata, &payload[7], false);
 	ir_to_input3(wdata, &payload[9], true);
-	input_sync(wdata->ir);
 	wiiext_handle(wdata, &payload[12]);
 }
 
@@ -978,7 +835,6 @@ static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload)
 	ir_to_input1(wdata, &payload[7], true);
 	ir_to_input2(wdata, &payload[10], false);
 	ir_to_input3(wdata, &payload[12], true);
-	input_sync(wdata->ir);
 	wiiext_handle(wdata, &payload[15]);
 }
 
@@ -997,7 +853,6 @@ static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload)
 
 	ir_to_input0(wdata, &payload[3], false);
 	ir_to_input1(wdata, &payload[12], false);
-	input_sync(wdata->ir);
 }
 
 static void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload)
@@ -1018,7 +873,6 @@ static void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload)
 
 	ir_to_input2(wdata, &payload[3], false);
 	ir_to_input3(wdata, &payload[12], false);
-	input_sync(wdata->ir);
 }
 
 struct wiiproto_handler {
@@ -1096,38 +950,6 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 	wdata->hdev = hdev;
 	hid_set_drvdata(hdev, wdata);
 
-	wdata->ir = input_allocate_device();
-	if (!wdata->ir)
-		goto err;
-
-	input_set_drvdata(wdata->ir, wdata);
-	wdata->ir->open = wiimote_ir_open;
-	wdata->ir->close = wiimote_ir_close;
-	wdata->ir->dev.parent = &wdata->hdev->dev;
-	wdata->ir->id.bustype = wdata->hdev->bus;
-	wdata->ir->id.vendor = wdata->hdev->vendor;
-	wdata->ir->id.product = wdata->hdev->product;
-	wdata->ir->id.version = wdata->hdev->version;
-	wdata->ir->name = WIIMOTE_NAME " IR";
-
-	set_bit(EV_ABS, wdata->ir->evbit);
-	set_bit(ABS_HAT0X, wdata->ir->absbit);
-	set_bit(ABS_HAT0Y, wdata->ir->absbit);
-	set_bit(ABS_HAT1X, wdata->ir->absbit);
-	set_bit(ABS_HAT1Y, wdata->ir->absbit);
-	set_bit(ABS_HAT2X, wdata->ir->absbit);
-	set_bit(ABS_HAT2Y, wdata->ir->absbit);
-	set_bit(ABS_HAT3X, wdata->ir->absbit);
-	set_bit(ABS_HAT3Y, wdata->ir->absbit);
-	input_set_abs_params(wdata->ir, ABS_HAT0X, 0, 1023, 2, 4);
-	input_set_abs_params(wdata->ir, ABS_HAT0Y, 0, 767, 2, 4);
-	input_set_abs_params(wdata->ir, ABS_HAT1X, 0, 1023, 2, 4);
-	input_set_abs_params(wdata->ir, ABS_HAT1Y, 0, 767, 2, 4);
-	input_set_abs_params(wdata->ir, ABS_HAT2X, 0, 1023, 2, 4);
-	input_set_abs_params(wdata->ir, ABS_HAT2Y, 0, 767, 2, 4);
-	input_set_abs_params(wdata->ir, ABS_HAT3X, 0, 1023, 2, 4);
-	input_set_abs_params(wdata->ir, ABS_HAT3Y, 0, 767, 2, 4);
-
 	spin_lock_init(&wdata->queue.lock);
 	INIT_WORK(&wdata->queue.worker, wiimote_queue_worker);
 
@@ -1140,10 +962,6 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 	INIT_WORK(&wdata->init_worker, wiimote_init_worker);
 
 	return wdata;
-
-err:
-	kfree(wdata);
-	return NULL;
 }
 
 static void wiimote_destroy(struct wiimote_data *wdata)
@@ -1153,7 +971,6 @@ static void wiimote_destroy(struct wiimote_data *wdata)
 
 	cancel_work_sync(&wdata->init_worker);
 	wiimote_modules_unload(wdata);
-	input_unregister_device(wdata->ir);
 	cancel_work_sync(&wdata->queue.worker);
 	hid_hw_close(wdata->hdev);
 	hid_hw_stop(wdata->hdev);
@@ -1193,12 +1010,6 @@ static int wiimote_hid_probe(struct hid_device *hdev,
 		goto err_stop;
 	}
 
-	ret = input_register_device(wdata->ir);
-	if (ret) {
-		hid_err(hdev, "Cannot register input device\n");
-		goto err_ir;
-	}
-
 	ret = wiiext_init(wdata);
 	if (ret)
 		goto err_free;
@@ -1218,8 +1029,6 @@ err_free:
 	wiimote_destroy(wdata);
 	return ret;
 
-err_ir:
-	hid_hw_close(hdev);
 err_stop:
 	hid_hw_stop(hdev);
 err:
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index fbc09c8..5cc593a 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -526,6 +526,268 @@ static const struct wiimod_ops wiimod_accel = {
 	.in_accel = wiimod_accel_in_accel,
 };
 
+/*
+ * IR Cam
+ * Up to 4 IR sources can be tracked by a normal Wii Remote. The IR cam needs
+ * to be initialized with a fairly complex procedure and consumes a lot of
+ * power. Therefore, as long as no application uses the IR input device, it is
+ * kept offline.
+ * Nearly no other device than the normal Wii Remotes supports the IR cam so
+ * you can disable this module for these devices.
+ */
+
+static void wiimod_ir_in_ir(struct wiimote_data *wdata, const __u8 *ir,
+			    bool packed, unsigned int id)
+{
+	__u16 x, y;
+	__u8 xid, yid;
+	bool sync = false;
+
+	if (!(wdata->state.flags & WIIPROTO_FLAGS_IR))
+		return;
+
+	switch (id) {
+	case 0:
+		xid = ABS_HAT0X;
+		yid = ABS_HAT0Y;
+		break;
+	case 1:
+		xid = ABS_HAT1X;
+		yid = ABS_HAT1Y;
+		break;
+	case 2:
+		xid = ABS_HAT2X;
+		yid = ABS_HAT2Y;
+		break;
+	case 3:
+		xid = ABS_HAT3X;
+		yid = ABS_HAT3Y;
+		sync = true;
+		break;
+	default:
+		return;
+	};
+
+	/*
+	 * Basic IR data is encoded into 3 bytes. The first two bytes are the
+	 * lower 8 bit of the X/Y data, the 3rd byte contains the upper 2 bits
+	 * of both.
+	 * If data is packed, then the 3rd byte is put first and slightly
+	 * reordered. This allows to interleave packed and non-packed data to
+	 * have two IR sets in 5 bytes instead of 6.
+	 * The resulting 10bit X/Y values are passed to the ABS_HAT? input dev.
+	 */
+
+	if (packed) {
+		x = ir[1] | ((ir[0] & 0x03) << 8);
+		y = ir[2] | ((ir[0] & 0x0c) << 6);
+	} else {
+		x = ir[0] | ((ir[2] & 0x30) << 4);
+		y = ir[1] | ((ir[2] & 0xc0) << 2);
+	}
+
+	input_report_abs(wdata->ir, xid, x);
+	input_report_abs(wdata->ir, yid, y);
+
+	if (sync)
+		input_sync(wdata->ir);
+}
+
+static int wiimod_ir_change(struct wiimote_data *wdata, __u16 mode)
+{
+	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);
+
+	if (mode == (wdata->state.flags & WIIPROTO_FLAGS_IR)) {
+		spin_unlock_irqrestore(&wdata->state.lock, flags);
+		return 0;
+	}
+
+	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;
+	}
+
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	ret = wiimote_cmd_acquire(wdata);
+	if (ret)
+		return ret;
+
+	/* 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);
+
+	ret = wiimote_cmd_wait(wdata);
+	if (ret)
+		goto unlock;
+	if (wdata->state.cmd_err) {
+		ret = -EIO;
+		goto unlock;
+	}
+
+	/* 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);
+
+	ret = wiimote_cmd_wait(wdata);
+	if (ret)
+		goto unlock;
+	if (wdata->state.cmd_err) {
+		ret = -EIO;
+		goto unlock;
+	}
+
+	/* 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;
+
+	/* write first sensitivity block */
+	ret = wiimote_cmd_write(wdata, 0xb00000, data_sens1,
+							sizeof(data_sens1));
+	if (ret)
+		goto unlock;
+
+	/* write second sensitivity block */
+	ret = wiimote_cmd_write(wdata, 0xb0001a, data_sens2,
+							sizeof(data_sens2));
+	if (ret)
+		goto unlock;
+
+	/* 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;
+	}
+	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;
+
+	/* 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);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+unlock:
+	wiimote_cmd_release(wdata);
+	return ret;
+}
+
+static int wiimod_ir_open(struct input_dev *dev)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+
+	return wiimod_ir_change(wdata, WIIPROTO_FLAG_IR_BASIC);
+}
+
+static void wiimod_ir_close(struct input_dev *dev)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+
+	wiimod_ir_change(wdata, 0);
+}
+
+static int wiimod_ir_probe(const struct wiimod_ops *ops,
+			   struct wiimote_data *wdata)
+{
+	int ret;
+
+	wdata->ir = input_allocate_device();
+	if (!wdata->ir)
+		return -ENOMEM;
+
+	input_set_drvdata(wdata->ir, wdata);
+	wdata->ir->open = wiimod_ir_open;
+	wdata->ir->close = wiimod_ir_close;
+	wdata->ir->dev.parent = &wdata->hdev->dev;
+	wdata->ir->id.bustype = wdata->hdev->bus;
+	wdata->ir->id.vendor = wdata->hdev->vendor;
+	wdata->ir->id.product = wdata->hdev->product;
+	wdata->ir->id.version = wdata->hdev->version;
+	wdata->ir->name = WIIMOTE_NAME " IR";
+
+	set_bit(EV_ABS, wdata->ir->evbit);
+	set_bit(ABS_HAT0X, wdata->ir->absbit);
+	set_bit(ABS_HAT0Y, wdata->ir->absbit);
+	set_bit(ABS_HAT1X, wdata->ir->absbit);
+	set_bit(ABS_HAT1Y, wdata->ir->absbit);
+	set_bit(ABS_HAT2X, wdata->ir->absbit);
+	set_bit(ABS_HAT2Y, wdata->ir->absbit);
+	set_bit(ABS_HAT3X, wdata->ir->absbit);
+	set_bit(ABS_HAT3Y, wdata->ir->absbit);
+	input_set_abs_params(wdata->ir, ABS_HAT0X, 0, 1023, 2, 4);
+	input_set_abs_params(wdata->ir, ABS_HAT0Y, 0, 767, 2, 4);
+	input_set_abs_params(wdata->ir, ABS_HAT1X, 0, 1023, 2, 4);
+	input_set_abs_params(wdata->ir, ABS_HAT1Y, 0, 767, 2, 4);
+	input_set_abs_params(wdata->ir, ABS_HAT2X, 0, 1023, 2, 4);
+	input_set_abs_params(wdata->ir, ABS_HAT2Y, 0, 767, 2, 4);
+	input_set_abs_params(wdata->ir, ABS_HAT3X, 0, 1023, 2, 4);
+	input_set_abs_params(wdata->ir, ABS_HAT3Y, 0, 767, 2, 4);
+
+	ret = input_register_device(wdata->ir);
+	if (ret) {
+		hid_err(wdata->hdev, "cannot register input device\n");
+		goto err_free;
+	}
+
+	return 0;
+
+err_free:
+	input_free_device(wdata->ir);
+	wdata->ir = NULL;
+	return ret;
+}
+
+static void wiimod_ir_remove(const struct wiimod_ops *ops,
+			     struct wiimote_data *wdata)
+{
+	if (!wdata->ir)
+		return;
+
+	input_unregister_device(wdata->ir);
+	wdata->ir = NULL;
+}
+
+static const struct wiimod_ops wiimod_ir = {
+	.flags = 0,
+	.arg = 0,
+	.probe = wiimod_ir_probe,
+	.remove = wiimod_ir_remove,
+	.in_ir = wiimod_ir_in_ir,
+};
+
 /* module table */
 
 const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
@@ -537,4 +799,5 @@ const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
 	[WIIMOD_LED3] = &wiimod_leds[2],
 	[WIIMOD_LED4] = &wiimod_leds[3],
 	[WIIMOD_ACCEL] = &wiimod_accel,
+	[WIIMOD_IR] = &wiimod_ir,
 };
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 8c242e6..3a2d3a1 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -134,6 +134,7 @@ enum wiimod_module {
 	WIIMOD_LED3,
 	WIIMOD_LED4,
 	WIIMOD_ACCEL,
+	WIIMOD_IR,
 	WIIMOD_NUM,
 	WIIMOD_NULL = WIIMOD_NUM,
 };
@@ -193,6 +194,8 @@ extern void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble);
 extern void wiiproto_req_leds(struct wiimote_data *wdata, int leds);
 extern void wiiproto_req_status(struct wiimote_data *wdata);
 extern void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel);
+extern void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags);
+extern void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags);
 extern int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
 						const __u8 *wmem, __u8 size);
 extern ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset,
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 13/26] HID: wiimote: add extension hotplug support
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (11 preceding siblings ...)
  2013-05-05 21:12 ` [PATCH 12/26] HID: wiimote: convert IR " David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-05-23 10:53   ` David Herrmann
  2013-05-05 21:12 ` [PATCH 14/26] HID: wiimote: add Balance Board support David Herrmann
                   ` (13 subsequent siblings)
  26 siblings, 1 reply; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

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: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c    | 669 ++++++++++++++++++++++++++++++++++++--
 drivers/hid/hid-wiimote-modules.c |  16 +
 drivers/hid/hid-wiimote.h         |  49 +++
 3 files changed, 709 insertions(+), 25 deletions(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 1ea70c8..836611e 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -179,17 +179,38 @@ 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;
+
+	ext = (wdata->state.flags & WIIPROTO_FLAG_EXT_USED) ||
+	      (wdata->state.flags & WIIPROTO_FLAG_MP_USED);
 
 	if (ir == WIIPROTO_FLAG_IR_BASIC) {
-		if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
-			return WIIPROTO_REQ_DRM_KAIE;
-		else
+		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) {
@@ -202,7 +223,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;
 		}
@@ -399,9 +420,8 @@ static int wiimote_cmd_init_ext(struct wiimote_data *wdata)
 }
 
 /* requires the cmd-mutex to be held */
-static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata)
+static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem)
 {
-	__u8 rmem[6];
 	int ret;
 
 	/* read extension ID */
@@ -409,6 +429,9 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata)
 	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;
@@ -416,6 +439,92 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata)
 	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;
+
+	/* 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) {
+	default:
+		wmem = 0x04;
+		break;
+	}
+
+	return wiimote_cmd_write(wdata, 0xa600fe, &wmem, sizeof(wmem));
+}
+
+/* requires the cmd-mutex to be held */
+static bool wiimote_cmd_read_mp(struct wiimote_data *wdata, __u8 *rmem)
+{
+	int ret;
+
+	/* read motion plus ID */
+	ret = wiimote_cmd_read(wdata, 0xa600fa, rmem, 6);
+	if (ret != 6)
+		return false;
+
+	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 (rmem[5] == 0x05)
+		return true;
+
+	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]);
+
+	return false;
+}
+
+/* requires the cmd-mutex to be held */
+static __u8 wiimote_cmd_read_mp_mapped(struct wiimote_data *wdata)
+{
+	int ret;
+	__u8 rmem[6];
+
+	/* 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] = {
@@ -561,6 +670,81 @@ static void wiimote_modules_unload(struct wiimote_data *wdata)
 	}
 }
 
+/* device extension handling */
+
+static void wiimote_ext_load(struct wiimote_data *wdata, unsigned int ext)
+{
+	unsigned long flags;
+	const struct wiimod_ops *ops;
+	int ret;
+
+	ops = wiimod_ext_table[ext];
+
+	if (ops->probe) {
+		ret = ops->probe(ops, wdata);
+		if (ret)
+			ext = WIIMOTE_EXT_UNKNOWN;
+	}
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.exttype = ext;
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+static void wiimote_ext_unload(struct wiimote_data *wdata)
+{
+	unsigned long flags;
+	const struct wiimod_ops *ops;
+
+	ops = wiimod_ext_table[wdata->state.exttype];
+
+	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 void wiimote_mp_unload(struct wiimote_data *wdata)
+{
+	unsigned long flags;
+	const struct wiimod_ops *ops;
+
+	if (wdata->state.mp < 2)
+		return;
+
+	ops = &wiimod_mp;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.mp = 0;
+	wdata->state.flags &= ~WIIPROTO_FLAG_MP_USED;
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	if (ops->remove)
+		ops->remove(ops, wdata);
+}
+
 /* device (re-)initialization and detection */
 
 static const char *wiimote_devtype_names[WIIMOTE_DEV_NUM] = {
@@ -617,7 +801,7 @@ done:
 
 static void wiimote_init_detect(struct wiimote_data *wdata)
 {
-	__u8 exttype = WIIMOTE_EXT_NONE;
+	__u8 exttype = WIIMOTE_EXT_NONE, extdata[6];
 	bool ext;
 	int ret;
 
@@ -641,11 +825,301 @@ static void wiimote_init_detect(struct wiimote_data *wdata)
 		goto out_release;
 
 	wiimote_cmd_init_ext(wdata);
-	exttype = wiimote_cmd_read_ext(wdata);
+	exttype = wiimote_cmd_read_ext(wdata, extdata);
 
 out_release:
 	wiimote_cmd_release(wdata);
 	wiimote_init_set_type(wdata, exttype);
+	/* schedule MP timer */
+	mod_timer(&wdata->timer, jiffies + HZ * 4);
+}
+
+/*
+ * 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)
+{
+	bool mp;
+	__u8 mpdata[6];
+
+	wiimote_cmd_acquire_noint(wdata);
+	wiimote_cmd_init_mp(wdata);
+	mp = wiimote_cmd_read_mp(wdata, mpdata);
+	wiimote_cmd_release(wdata);
+
+	/* 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);
+}
+
+/*
+ * 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)
+{
+	__u32 flags;
+	__u8 type, data[6];
+	bool ret, poll_mp;
+
+	spin_lock_irq(&wdata->state.lock);
+	flags = wdata->state.flags;
+	spin_unlock_irq(&wdata->state.lock);
+
+	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;
+	}
+
+	/* 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 */
+	ret = false;
+
+out_release:
+	wiimote_cmd_release(wdata);
+
+	/* only poll for MP if requested and if state didn't change */
+	if (ret && poll_mp)
+		wiimote_init_poll_mp(wdata);
+
+	return ret;
+}
+
+static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = {
+	[WIIMOTE_EXT_NONE] = "None",
+	[WIIMOTE_EXT_UNKNOWN] = "Unknown",
+};
+
+/*
+ * Handle hotplug events
+ * If we receive an hotplug event and the device-check failed, we deinitialize
+ * the extension ports, re-read all extension IDs and set the device into
+ * the desired state. This involves mapping MP into the main extension
+ * registers, setting up extension passthrough modes and initializing the
+ * requested extensions.
+ */
+static void wiimote_init_hotplug(struct wiimote_data *wdata)
+{
+	__u8 exttype, extdata[6], mpdata[6];
+	__u32 flags;
+	bool mp;
+
+	hid_dbg(wdata->hdev, "detect extensions..\n");
+
+	wiimote_cmd_acquire_noint(wdata);
+
+	spin_lock_irq(&wdata->state.lock);
+
+	/* get state snapshot that we will then work on */
+	flags = wdata->state.flags;
+
+	/* disable event forwarding temporarily */
+	wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE;
+	wdata->state.flags &= ~WIIPROTO_FLAG_MP_ACTIVE;
+
+	spin_unlock_irq(&wdata->state.lock);
+
+	/* init extension and MP (deactivates current extension or MP) */
+	wiimote_cmd_init_ext(wdata);
+	wiimote_cmd_init_mp(wdata);
+	mp = wiimote_cmd_read_mp(wdata, mpdata);
+	exttype = wiimote_cmd_read_ext(wdata, extdata);
+
+	wiimote_cmd_release(wdata);
+
+	/* load/unload extension module if it changed */
+	if (exttype != wdata->state.exttype) {
+		/* unload previous extension */
+		wiimote_ext_unload(wdata);
+
+		if (exttype == WIIMOTE_EXT_UNKNOWN) {
+			hid_info(wdata->hdev, "cannot detect extension; %02x:%02x %02x:%02x %02x:%02x\n",
+				 extdata[0], extdata[1], extdata[2],
+				 extdata[3], extdata[4], extdata[5]);
+		} else if (exttype == WIIMOTE_EXT_NONE) {
+			spin_lock_irq(&wdata->state.lock);
+			wdata->state.exttype = WIIMOTE_EXT_NONE;
+			spin_unlock_irq(&wdata->state.lock);
+		} else {
+			hid_info(wdata->hdev, "detected extension: %s\n",
+				 wiimote_exttype_names[exttype]);
+			/* try loading new extension */
+			wiimote_ext_load(wdata, exttype);
+		}
+	}
+
+	/* 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);
+	}
+
+	/* if MP is not used, do not map or activate it */
+	if (!(flags & WIIPROTO_FLAG_MP_USED))
+		mp = false;
+
+	/* map MP into main extension registers if used */
+	if (mp) {
+		wiimote_cmd_acquire_noint(wdata);
+		wiimote_cmd_map_mp(wdata, exttype);
+		wiimote_cmd_release(wdata);
+
+		/* delete MP hotplug timer */
+		del_timer_sync(&wdata->timer);
+	} else {
+		/* reschedule MP hotplug timer */
+		mod_timer(&wdata->timer, jiffies + HZ * 4);
+	}
+
+	spin_lock_irq(&wdata->state.lock);
+
+	/* enable data forwarding again and set expected hotplug state */
+	if (mp) {
+		wdata->state.flags |= WIIPROTO_FLAG_MP_ACTIVE;
+		if (wdata->state.exttype == WIIMOTE_EXT_NONE) {
+			wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
+			wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED;
+		} else {
+			wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
+			wdata->state.flags |= WIIPROTO_FLAG_MP_PLUGGED;
+			wdata->state.flags |= WIIPROTO_FLAG_EXT_ACTIVE;
+		}
+	} else if (wdata->state.exttype != WIIMOTE_EXT_NONE) {
+		wdata->state.flags |= WIIPROTO_FLAG_EXT_ACTIVE;
+	}
+
+	/* request status report for hotplug state updates */
+	wiiproto_req_status(wdata);
+
+	spin_unlock_irq(&wdata->state.lock);
+
+	hid_dbg(wdata->hdev, "detected extensions: MP: %d EXT: %d\n",
+		wdata->state.mp, wdata->state.exttype);
 }
 
 static void wiimote_init_worker(struct work_struct *work)
@@ -655,6 +1129,30 @@ static void wiimote_init_worker(struct work_struct *work)
 
 	if (wdata->state.devtype == WIIMOTE_DEV_PENDING)
 		wiimote_init_detect(wdata);
+	if (!wiimote_init_check(wdata))
+		wiimote_init_hotplug(wdata);
+}
+
+void __wiimote_schedule(struct wiimote_data *wdata)
+{
+	if (!(wdata->state.flags & WIIPROTO_FLAG_EXITING))
+		schedule_work(&wdata->init_worker);
+}
+
+static void wiimote_schedule(struct wiimote_data *wdata)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	__wiimote_schedule(wdata);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+static void wiimote_init_timeout(unsigned long arg)
+{
+	struct wiimote_data *wdata = (void*)arg;
+
+	wiimote_schedule(wdata);
 }
 
 /* protocol handlers */
@@ -664,6 +1162,12 @@ static void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
 	const __u8 *iter, *mods;
 	const struct wiimod_ops *ops;
 
+	ops = wiimod_ext_table[wdata->state.exttype];
+	if (ops->in_keys) {
+		ops->in_keys(wdata, payload);
+		return;
+	}
+
 	mods = wiimote_devtype_mods[wdata->state.devtype];
 	for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
 		ops = wiimod_table[*iter];
@@ -679,6 +1183,12 @@ static void handler_accel(struct wiimote_data *wdata, const __u8 *payload)
 	const __u8 *iter, *mods;
 	const struct wiimod_ops *ops;
 
+	ops = wiimod_ext_table[wdata->state.exttype];
+	if (ops->in_accel) {
+		ops->in_accel(wdata, payload);
+		return;
+	}
+
 	mods = wiimote_devtype_mods[wdata->state.devtype];
 	for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
 		ops = wiimod_table[*iter];
@@ -689,6 +1199,93 @@ static void handler_accel(struct wiimote_data *wdata, const __u8 *payload)
 	}
 }
 
+static bool valid_ext_handler(const struct wiimod_ops *ops, size_t len)
+{
+	if (!ops->in_ext)
+		return false;
+	if ((ops->flags & WIIMOD_FLAG_EXT8) && len < 8)
+		return false;
+	if ((ops->flags & WIIMOD_FLAG_EXT16) && len < 16)
+		return false;
+
+	return true;
+}
+
+static void handler_ext(struct wiimote_data *wdata, const __u8 *payload,
+			size_t len)
+{
+	const __u8 *iter, *mods;
+	const struct wiimod_ops *ops;
+	bool is_mp;
+
+	if (len < 6)
+		return;
+
+	/* if MP is active, track MP slot hotplugging */
+	if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
+		/* this bit is set for invalid events (eg. during hotplug) */
+		if (payload[5] & 0x01)
+			return;
+
+		if (payload[4] & 0x01) {
+			if (!(wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED)) {
+				hid_dbg(wdata->hdev, "MP hotplug: 1\n");
+				wdata->state.flags |= WIIPROTO_FLAG_MP_PLUGGED;
+				__wiimote_schedule(wdata);
+			}
+		} else {
+			if (wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED) {
+				hid_dbg(wdata->hdev, "MP hotplug: 0\n");
+				wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED;
+				wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE;
+				__wiimote_schedule(wdata);
+			}
+		}
+
+		/* detect MP data that is sent interleaved with EXT data */
+		is_mp = payload[5] & 0x02;
+	} else {
+		is_mp = false;
+	}
+
+	/* ignore EXT events if no extension is active */
+	if (!(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE) && !is_mp)
+		return;
+
+	/* try forwarding to extension handler, first */
+	ops = wiimod_ext_table[wdata->state.exttype];
+	if (is_mp && ops->in_mp) {
+		ops->in_mp(wdata, payload);
+		return;
+	} else if (!is_mp && valid_ext_handler(ops, len)) {
+		ops->in_ext(wdata, payload);
+		return;
+	}
+
+	/* try forwarding to MP handler */
+	ops = &wiimod_mp;
+	if (is_mp && ops->in_mp) {
+		ops->in_mp(wdata, payload);
+		return;
+	} else if (!is_mp && valid_ext_handler(ops, len)) {
+		ops->in_ext(wdata, payload);
+		return;
+	}
+
+	/* try forwarding to loaded modules */
+	mods = wiimote_devtype_mods[wdata->state.devtype];
+	for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
+		ops = wiimod_table[*iter];
+		if (is_mp && ops->in_mp) {
+			ops->in_mp(wdata, payload);
+			return;
+		} else if (!is_mp && valid_ext_handler(ops, len)) {
+			ops->in_ext(wdata, payload);
+			return;
+		}
+	}
+}
+
 #define ir_to_input0(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 0)
 #define ir_to_input1(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 1)
 #define ir_to_input2(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 2)
@@ -700,6 +1297,12 @@ static void handler_ir(struct wiimote_data *wdata, const __u8 *payload,
 	const __u8 *iter, *mods;
 	const struct wiimod_ops *ops;
 
+	ops = wiimod_ext_table[wdata->state.exttype];
+	if (ops->in_ir) {
+		ops->in_ir(wdata, payload, packed, id);
+		return;
+	}
+
 	mods = wiimote_devtype_mods[wdata->state.devtype];
 	for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
 		ops = wiimod_table[*iter];
@@ -727,11 +1330,20 @@ static void handler_status(struct wiimote_data *wdata, const __u8 *payload)
 
 	/* update extension status */
 	if (payload[2] & 0x02) {
-		wdata->state.flags |= WIIPROTO_FLAG_EXT_PLUGGED;
-		wiiext_event(wdata, true);
+		if (!(wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED)) {
+			hid_dbg(wdata->hdev, "EXT hotplug: 1\n");
+			wdata->state.flags |= WIIPROTO_FLAG_EXT_PLUGGED;
+			__wiimote_schedule(wdata);
+		}
 	} else {
-		wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
-		wiiext_event(wdata, false);
+		if (wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED) {
+			hid_dbg(wdata->hdev, "EXT hotplug: 0\n");
+			wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
+			wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED;
+			wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE;
+			wdata->state.flags &= ~WIIPROTO_FLAG_MP_ACTIVE;
+			__wiimote_schedule(wdata);
+		}
 	}
 
 	wdata->state.cmd_battery = payload[5];
@@ -791,7 +1403,7 @@ static void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload)
 static void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload)
 {
 	handler_keys(wdata, payload);
-	wiiext_handle(wdata, &payload[2]);
+	handler_ext(wdata, &payload[2], 8);
 }
 
 static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload)
@@ -807,7 +1419,7 @@ static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload)
 static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload)
 {
 	handler_keys(wdata, payload);
-	wiiext_handle(wdata, &payload[2]);
+	handler_ext(wdata, &payload[2], 19);
 }
 
 static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload)
@@ -817,14 +1429,14 @@ static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload)
 	ir_to_input1(wdata, &payload[4], true);
 	ir_to_input2(wdata, &payload[7], false);
 	ir_to_input3(wdata, &payload[9], true);
-	wiiext_handle(wdata, &payload[12]);
+	handler_ext(wdata, &payload[12], 9);
 }
 
 static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload)
 {
 	handler_keys(wdata, payload);
 	handler_accel(wdata, payload);
-	wiiext_handle(wdata, &payload[5]);
+	handler_ext(wdata, &payload[5], 16);
 }
 
 static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload)
@@ -835,12 +1447,12 @@ static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload)
 	ir_to_input1(wdata, &payload[7], true);
 	ir_to_input2(wdata, &payload[10], false);
 	ir_to_input3(wdata, &payload[12], true);
-	wiiext_handle(wdata, &payload[15]);
+	handler_ext(wdata, &payload[15], 6);
 }
 
 static void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload)
 {
-	wiiext_handle(wdata, payload);
+	handler_ext(wdata, payload, 21);
 }
 
 static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload)
@@ -960,16 +1572,27 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 	wdata->state.cmd_battery = 0xff;
 
 	INIT_WORK(&wdata->init_worker, wiimote_init_worker);
+	setup_timer(&wdata->timer, wiimote_init_timeout, (long)wdata);
 
 	return wdata;
 }
 
 static void wiimote_destroy(struct wiimote_data *wdata)
 {
+	unsigned long flags;
+
 	wiidebug_deinit(wdata);
-	wiiext_deinit(wdata);
+
+	/* prevent init_worker from being scheduled again */
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags |= WIIPROTO_FLAG_EXITING;
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
 
 	cancel_work_sync(&wdata->init_worker);
+	del_timer_sync(&wdata->timer);
+
+	wiimote_mp_unload(wdata);
+	wiimote_ext_unload(wdata);
 	wiimote_modules_unload(wdata);
 	cancel_work_sync(&wdata->queue.worker);
 	hid_hw_close(wdata->hdev);
@@ -1010,10 +1633,6 @@ static int wiimote_hid_probe(struct hid_device *hdev,
 		goto err_stop;
 	}
 
-	ret = wiiext_init(wdata);
-	if (ret)
-		goto err_free;
-
 	ret = wiidebug_init(wdata);
 	if (ret)
 		goto err_free;
@@ -1021,7 +1640,7 @@ static int wiimote_hid_probe(struct hid_device *hdev,
 	hid_info(hdev, "New device registered\n");
 
 	/* schedule device detection */
-	schedule_work(&wdata->init_worker);
+	wiimote_schedule(wdata);
 
 	return 0;
 
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index 5cc593a..d3eef77 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -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,
+};
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 3a2d3a1..0afc9f9 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -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);
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 14/26] HID: wiimote: add Balance Board support
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (12 preceding siblings ...)
  2013-05-05 21:12 ` [PATCH 13/26] HID: wiimote: add extension hotplug support David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-05-05 21:12 ` [PATCH 15/26] HID: wiimote: add Nunchuk support David Herrmann
                   ` (12 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

This adds Nintendo Wii Balance Board support to the new HOTPLUG capable
wiimote core. It is mostly copied from the old extension.

This also adds Balance Board device detection. Whenever we find a device
that supports the balance-board extension, we assume that it is a real
balance board and disable unsupported hardward like accelerometer, IR,
rumble and more.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c    |  26 +++++
 drivers/hid/hid-wiimote-modules.c | 211 ++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-wiimote.h         |   9 ++
 3 files changed, 246 insertions(+)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 836611e..90ea5a2 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -202,6 +202,14 @@ static __u8 select_drm(struct wiimote_data *wdata)
 	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)
@@ -436,6 +444,9 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem)
 	    rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff)
 		return WIIMOTE_EXT_NONE;
 
+	if (rmem[4] == 0x04 && rmem[5] == 0x02)
+		return WIIMOTE_EXT_BALANCE_BOARD;
+
 	return WIIMOTE_EXT_UNKNOWN;
 }
 
@@ -570,6 +581,11 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		WIIMOD_IR,
 		WIIMOD_NULL,
 	},
+	[WIIMOTE_DEV_BALANCE_BOARD] = (const __u8[]) {
+		WIIMOD_BATTERY,
+		WIIMOD_LED1,
+		WIIMOD_NULL,
+	},
 };
 
 static void wiimote_modules_load(struct wiimote_data *wdata,
@@ -753,6 +769,7 @@ static const char *wiimote_devtype_names[WIIMOTE_DEV_NUM] = {
 	[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",
 };
 
 /* Try to guess the device type based on all collected information. We
@@ -770,12 +787,20 @@ static void wiimote_init_set_type(struct wiimote_data *wdata,
 	product = wdata->hdev->product;
 	name = wdata->hdev->name;
 
+	if (exttype == WIIMOTE_EXT_BALANCE_BOARD) {
+		devtype = WIIMOTE_DEV_BALANCE_BOARD;
+		goto done;
+	}
+
 	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;
 	}
 
 	if (vendor == USB_VENDOR_ID_NINTENDO) {
@@ -1009,6 +1034,7 @@ out_release:
 static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = {
 	[WIIMOTE_EXT_NONE] = "None",
 	[WIIMOTE_EXT_UNKNOWN] = "Unknown",
+	[WIIMOTE_EXT_BALANCE_BOARD] = "Nintendo Wii Balance Board",
 };
 
 /*
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index d3eef77..6239cd8 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -789,6 +789,216 @@ static const struct wiimod_ops wiimod_ir = {
 };
 
 /*
+ * Balance Board Extension
+ * The Nintendo Wii Balance Board provides four hardware weight sensor plus a
+ * single push button. No other peripherals are available. However, the
+ * balance-board data is sent via a standard Wii Remote extension. All other
+ * data for non-present hardware is zeroed out.
+ * Some 3rd party devices react allergic if we try to access normal Wii Remote
+ * hardware, so this extension module should be the only module that is loaded
+ * on balance boards.
+ * The balance board needs 8 bytes extension data instead of basic 6 bytes so
+ * it needs the WIIMOD_FLAG_EXT8 flag.
+ */
+
+static void wiimod_bboard_in_keys(struct wiimote_data *wdata, const __u8 *keys)
+{
+	input_report_key(wdata->extension.input, BTN_A,
+			 !!(keys[1] & 0x08));
+	input_sync(wdata->extension.input);
+}
+
+static void wiimod_bboard_in_ext(struct wiimote_data *wdata,
+				 const __u8 *ext)
+{
+	__s32 val[4], tmp, div;
+	unsigned int i;
+	struct wiimote_state *s = &wdata->state;
+
+	/*
+	 * Balance board data layout:
+	 *
+	 *   Byte |  8  7  6  5  4  3  2  1  |
+	 *   -----+--------------------------+
+	 *    1   |    Top Right <15:8>      |
+	 *    2   |    Top Right  <7:0>      |
+	 *   -----+--------------------------+
+	 *    3   | Bottom Right <15:8>      |
+	 *    4   | Bottom Right  <7:0>      |
+	 *   -----+--------------------------+
+	 *    5   |     Top Left <15:8>      |
+	 *    6   |     Top Left  <7:0>      |
+	 *   -----+--------------------------+
+	 *    7   |  Bottom Left <15:8>      |
+	 *    8   |  Bottom Left  <7:0>      |
+	 *   -----+--------------------------+
+	 *
+	 * These values represent the weight-measurements of the Wii-balance
+	 * board with 16bit precision.
+	 *
+	 * The balance-board is never reported interleaved with motionp.
+	 */
+
+	val[0] = ext[0];
+	val[0] <<= 8;
+	val[0] |= ext[1];
+
+	val[1] = ext[2];
+	val[1] <<= 8;
+	val[1] |= ext[3];
+
+	val[2] = ext[4];
+	val[2] <<= 8;
+	val[2] |= ext[5];
+
+	val[3] = ext[6];
+	val[3] <<= 8;
+	val[3] |= ext[7];
+
+	/* apply calibration data */
+	for (i = 0; i < 4; i++) {
+		if (val[i] <= s->calib_bboard[i][0]) {
+			tmp = 0;
+		} else if (val[i] < s->calib_bboard[i][1]) {
+			tmp = val[i] - s->calib_bboard[i][0];
+			tmp *= 1700;
+			div = s->calib_bboard[i][1] - s->calib_bboard[i][0];
+			tmp /= div ? div : 1;
+		} else {
+			tmp = val[i] - s->calib_bboard[i][1];
+			tmp *= 1700;
+			div = s->calib_bboard[i][2] - s->calib_bboard[i][1];
+			tmp /= div ? div : 1;
+			tmp += 1700;
+		}
+		val[i] = tmp;
+	}
+
+	input_report_abs(wdata->extension.input, ABS_HAT0X, val[0]);
+	input_report_abs(wdata->extension.input, ABS_HAT0Y, val[1]);
+	input_report_abs(wdata->extension.input, ABS_HAT1X, val[2]);
+	input_report_abs(wdata->extension.input, ABS_HAT1Y, val[3]);
+	input_sync(wdata->extension.input);
+}
+
+static int wiimod_bboard_open(struct input_dev *dev)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags |= WIIPROTO_FLAG_EXT_USED;
+	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	return 0;
+}
+
+static void wiimod_bboard_close(struct input_dev *dev)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
+	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+static int wiimod_bboard_probe(const struct wiimod_ops *ops,
+			       struct wiimote_data *wdata)
+{
+	int ret, i, j;
+	__u8 buf[24], offs;
+
+	wiimote_cmd_acquire_noint(wdata);
+
+	ret = wiimote_cmd_read(wdata, 0xa40024, buf, 12);
+	if (ret != 12) {
+		wiimote_cmd_release(wdata);
+		return ret < 0 ? ret : -EIO;
+	}
+	ret = wiimote_cmd_read(wdata, 0xa40024 + 12, buf + 12, 12);
+	if (ret != 12) {
+		wiimote_cmd_release(wdata);
+		return ret < 0 ? ret : -EIO;
+	}
+
+	wiimote_cmd_release(wdata);
+
+	offs = 0;
+	for (i = 0; i < 3; ++i) {
+		for (j = 0; j < 4; ++j) {
+			wdata->state.calib_bboard[j][i] = buf[offs];
+			wdata->state.calib_bboard[j][i] <<= 8;
+			wdata->state.calib_bboard[j][i] |= buf[offs + 1];
+			offs += 2;
+		}
+	}
+
+	wdata->extension.input = input_allocate_device();
+	if (!wdata->extension.input)
+		return -ENOMEM;
+
+	input_set_drvdata(wdata->extension.input, wdata);
+	wdata->extension.input->open = wiimod_bboard_open;
+	wdata->extension.input->close = wiimod_bboard_close;
+	wdata->extension.input->dev.parent = &wdata->hdev->dev;
+	wdata->extension.input->id.bustype = wdata->hdev->bus;
+	wdata->extension.input->id.vendor = wdata->hdev->vendor;
+	wdata->extension.input->id.product = wdata->hdev->product;
+	wdata->extension.input->id.version = wdata->hdev->version;
+	wdata->extension.input->name = WIIMOTE_NAME " Balance Board";
+
+	set_bit(EV_KEY, wdata->extension.input->evbit);
+	set_bit(BTN_A, wdata->extension.input->keybit);
+
+	set_bit(EV_ABS, wdata->extension.input->evbit);
+	set_bit(ABS_HAT0X, wdata->extension.input->absbit);
+	set_bit(ABS_HAT0Y, wdata->extension.input->absbit);
+	set_bit(ABS_HAT1X, wdata->extension.input->absbit);
+	set_bit(ABS_HAT1Y, wdata->extension.input->absbit);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT0X, 0, 65535, 2, 4);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT0Y, 0, 65535, 2, 4);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT1X, 0, 65535, 2, 4);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT1Y, 0, 65535, 2, 4);
+
+	ret = input_register_device(wdata->extension.input);
+	if (ret)
+		goto err_free;
+
+	return 0;
+
+err_free:
+	input_free_device(wdata->extension.input);
+	wdata->extension.input = NULL;
+	return ret;
+}
+
+static void wiimod_bboard_remove(const struct wiimod_ops *ops,
+				 struct wiimote_data *wdata)
+{
+	if (!wdata->extension.input)
+		return;
+
+	input_unregister_device(wdata->extension.input);
+	wdata->extension.input = NULL;
+}
+
+static const struct wiimod_ops wiimod_bboard = {
+	.flags = WIIMOD_FLAG_EXT8,
+	.arg = 0,
+	.probe = wiimod_bboard_probe,
+	.remove = wiimod_bboard_remove,
+	.in_keys = wiimod_bboard_in_keys,
+	.in_ext = wiimod_bboard_in_ext,
+};
+
+/*
  * Motion Plus
  */
 
@@ -816,4 +1026,5 @@ const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
 const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = {
 	[WIIMOTE_EXT_NONE] = &wiimod_dummy,
 	[WIIMOTE_EXT_UNKNOWN] = &wiimod_dummy,
+	[WIIMOTE_EXT_BALANCE_BOARD] = &wiimod_bboard,
 };
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 0afc9f9..8d314ae 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -73,12 +73,14 @@ enum wiimote_devtype {
 	WIIMOTE_DEV_GENERIC,
 	WIIMOTE_DEV_GEN10,
 	WIIMOTE_DEV_GEN20,
+	WIIMOTE_DEV_BALANCE_BOARD,
 	WIIMOTE_DEV_NUM,
 };
 
 enum wiimote_exttype {
 	WIIMOTE_EXT_NONE,
 	WIIMOTE_EXT_UNKNOWN,
+	WIIMOTE_EXT_BALANCE_BOARD,
 	WIIMOTE_EXT_NUM,
 };
 
@@ -123,6 +125,9 @@ struct wiimote_state {
 	__u8 cmd_err;
 	__u8 *cmd_read_buf;
 	__u8 cmd_read_size;
+
+	/* calibration data */
+	__u16 calib_bboard[4][3];
 };
 
 struct wiimote_data {
@@ -136,6 +141,10 @@ struct wiimote_data {
 	struct wiimote_ext *ext;
 	struct wiimote_debug *debug;
 
+	union {
+		struct input_dev *input;
+	} extension;
+
 	struct wiimote_queue queue;
 	struct wiimote_state state;
 	struct work_struct init_worker;
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 15/26] HID: wiimote: add Nunchuk support
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (13 preceding siblings ...)
  2013-05-05 21:12 ` [PATCH 14/26] HID: wiimote: add Balance Board support David Herrmann
@ 2013-05-05 21:12 ` David Herrmann
  2013-05-05 21:13 ` [PATCH 16/26] HID: wiimote: add Classic Controller extension David Herrmann
                   ` (11 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:12 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

This moves the nunchuk parser over to an extension module. This allows to
make use of hotplugged Nunchuks instead of the old static parser.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c    |   6 ++
 drivers/hid/hid-wiimote-modules.c | 198 ++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-wiimote.h         |   1 +
 3 files changed, 205 insertions(+)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 90ea5a2..dedf3c8 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -444,6 +444,8 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem)
 	    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] == 0x04 && rmem[5] == 0x02)
 		return WIIMOTE_EXT_BALANCE_BOARD;
 
@@ -478,6 +480,9 @@ static bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype)
 
 	/* map MP with correct pass-through mode */
 	switch (exttype) {
+	case WIIMOTE_EXT_NUNCHUK:
+		wmem = 0x05;
+		break;
 	default:
 		wmem = 0x04;
 		break;
@@ -1034,6 +1039,7 @@ out_release:
 static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = {
 	[WIIMOTE_EXT_NONE] = "None",
 	[WIIMOTE_EXT_UNKNOWN] = "Unknown",
+	[WIIMOTE_EXT_NUNCHUK] = "Nintendo Wii Nunchuk",
 	[WIIMOTE_EXT_BALANCE_BOARD] = "Nintendo Wii Balance Board",
 };
 
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index 6239cd8..e4bcc09 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -789,6 +789,203 @@ static const struct wiimod_ops wiimod_ir = {
 };
 
 /*
+ * Nunchuk Extension
+ * The Nintendo Wii Nunchuk was the first official extension published by
+ * Nintendo. It provides two additional keys and a separate accelerometer. It
+ * can be hotplugged to standard Wii Remotes.
+ */
+
+enum wiimod_nunchuk_keys {
+	WIIMOD_NUNCHUK_KEY_C,
+	WIIMOD_NUNCHUK_KEY_Z,
+	WIIMOD_NUNCHUK_KEY_NUM,
+};
+
+static const __u16 wiimod_nunchuk_map[] = {
+	BTN_C,		/* WIIMOD_NUNCHUK_KEY_C */
+	BTN_Z,		/* WIIMOD_NUNCHUK_KEY_Z */
+};
+
+static void wiimod_nunchuk_in_ext(struct wiimote_data *wdata, const __u8 *ext)
+{
+	__s16 x, y, z, bx, by;
+
+	/*   Byte |   8    7 |  6    5 |  4    3 |  2 |  1  |
+	 *   -----+----------+---------+---------+----+-----+
+	 *    1   |              Button X <7:0>             |
+	 *    2   |              Button Y <7:0>             |
+	 *   -----+----------+---------+---------+----+-----+
+	 *    3   |               Speed X <9:2>             |
+	 *    4   |               Speed Y <9:2>             |
+	 *    5   |               Speed Z <9:2>             |
+	 *   -----+----------+---------+---------+----+-----+
+	 *    6   | Z <1:0>  | Y <1:0> | X <1:0> | BC | BZ  |
+	 *   -----+----------+---------+---------+----+-----+
+	 * Button X/Y is the analog stick. Speed X, Y and Z are the
+	 * accelerometer data in the same format as the wiimote's accelerometer.
+	 * The 6th byte contains the LSBs of the accelerometer data.
+	 * BC and BZ are the C and Z buttons: 0 means pressed
+	 *
+	 * If reported interleaved with motionp, then the layout changes. The
+	 * 5th and 6th byte changes to:
+	 *   -----+-----------------------------------+-----+
+	 *    5   |            Speed Z <9:3>          | EXT |
+	 *   -----+--------+-----+-----+----+----+----+-----+
+	 *    6   |Z <2:1> |Y <1>|X <1>| BC | BZ | 0  |  0  |
+	 *   -----+--------+-----+-----+----+----+----+-----+
+	 * All three accelerometer values lose their LSB. The other data is
+	 * still available but slightly moved.
+	 *
+	 * Center data for button values is 128. Center value for accelerometer
+	 * values it 512 / 0x200
+	 */
+
+	bx = ext[0];
+	by = ext[1];
+	bx -= 128;
+	by -= 128;
+
+	x = ext[2] << 2;
+	y = ext[3] << 2;
+	z = ext[4] << 2;
+
+	if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
+		x |= (ext[5] >> 3) & 0x02;
+		y |= (ext[5] >> 4) & 0x02;
+		z &= ~0x4;
+		z |= (ext[5] >> 5) & 0x06;
+	} else {
+		x |= (ext[5] >> 2) & 0x03;
+		y |= (ext[5] >> 4) & 0x03;
+		z |= (ext[5] >> 6) & 0x03;
+	}
+
+	x -= 0x200;
+	y -= 0x200;
+	z -= 0x200;
+
+	input_report_abs(wdata->extension.input, ABS_HAT0X, bx);
+	input_report_abs(wdata->extension.input, ABS_HAT0Y, by);
+
+	input_report_abs(wdata->extension.input, ABS_RX, x);
+	input_report_abs(wdata->extension.input, ABS_RY, y);
+	input_report_abs(wdata->extension.input, ABS_RZ, z);
+
+	if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
+		input_report_key(wdata->extension.input,
+			wiimod_nunchuk_map[WIIMOD_NUNCHUK_KEY_Z],
+			!(ext[5] & 0x04));
+		input_report_key(wdata->extension.input,
+			wiimod_nunchuk_map[WIIMOD_NUNCHUK_KEY_C],
+			!(ext[5] & 0x08));
+	} else {
+		input_report_key(wdata->extension.input,
+			wiimod_nunchuk_map[WIIMOD_NUNCHUK_KEY_Z],
+			!(ext[5] & 0x01));
+		input_report_key(wdata->extension.input,
+			wiimod_nunchuk_map[WIIMOD_NUNCHUK_KEY_C],
+			!(ext[5] & 0x02));
+	}
+
+	input_sync(wdata->extension.input);
+}
+
+static int wiimod_nunchuk_open(struct input_dev *dev)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags |= WIIPROTO_FLAG_EXT_USED;
+	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	return 0;
+}
+
+static void wiimod_nunchuk_close(struct input_dev *dev)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
+	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+static int wiimod_nunchuk_probe(const struct wiimod_ops *ops,
+				struct wiimote_data *wdata)
+{
+	int ret, i;
+
+	wdata->extension.input = input_allocate_device();
+	if (!wdata->extension.input)
+		return -ENOMEM;
+
+	input_set_drvdata(wdata->extension.input, wdata);
+	wdata->extension.input->open = wiimod_nunchuk_open;
+	wdata->extension.input->close = wiimod_nunchuk_close;
+	wdata->extension.input->dev.parent = &wdata->hdev->dev;
+	wdata->extension.input->id.bustype = wdata->hdev->bus;
+	wdata->extension.input->id.vendor = wdata->hdev->vendor;
+	wdata->extension.input->id.product = wdata->hdev->product;
+	wdata->extension.input->id.version = wdata->hdev->version;
+	wdata->extension.input->name = WIIMOTE_NAME " Nunchuk";
+
+	set_bit(EV_KEY, wdata->extension.input->evbit);
+	for (i = 0; i < WIIMOD_NUNCHUK_KEY_NUM; ++i)
+		set_bit(wiimod_nunchuk_map[i],
+			wdata->extension.input->keybit);
+
+	set_bit(EV_ABS, wdata->extension.input->evbit);
+	set_bit(ABS_HAT0X, wdata->extension.input->absbit);
+	set_bit(ABS_HAT0Y, wdata->extension.input->absbit);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT0X, -120, 120, 2, 4);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT0Y, -120, 120, 2, 4);
+	set_bit(ABS_RX, wdata->extension.input->absbit);
+	set_bit(ABS_RY, wdata->extension.input->absbit);
+	set_bit(ABS_RZ, wdata->extension.input->absbit);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_RX, -500, 500, 2, 4);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_RY, -500, 500, 2, 4);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_RZ, -500, 500, 2, 4);
+
+	ret = input_register_device(wdata->extension.input);
+	if (ret)
+		goto err_free;
+
+	return 0;
+
+err_free:
+	input_free_device(wdata->extension.input);
+	wdata->extension.input = NULL;
+	return ret;
+}
+
+static void wiimod_nunchuk_remove(const struct wiimod_ops *ops,
+				  struct wiimote_data *wdata)
+{
+	if (!wdata->extension.input)
+		return;
+
+	input_unregister_device(wdata->extension.input);
+	wdata->extension.input = NULL;
+}
+
+static const struct wiimod_ops wiimod_nunchuk = {
+	.flags = 0,
+	.arg = 0,
+	.probe = wiimod_nunchuk_probe,
+	.remove = wiimod_nunchuk_remove,
+	.in_ext = wiimod_nunchuk_in_ext,
+};
+
+/*
  * Balance Board Extension
  * The Nintendo Wii Balance Board provides four hardware weight sensor plus a
  * single push button. No other peripherals are available. However, the
@@ -1026,5 +1223,6 @@ const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
 const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = {
 	[WIIMOTE_EXT_NONE] = &wiimod_dummy,
 	[WIIMOTE_EXT_UNKNOWN] = &wiimod_dummy,
+	[WIIMOTE_EXT_NUNCHUK] = &wiimod_nunchuk,
 	[WIIMOTE_EXT_BALANCE_BOARD] = &wiimod_bboard,
 };
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 8d314ae..3414e4c 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -80,6 +80,7 @@ enum wiimote_devtype {
 enum wiimote_exttype {
 	WIIMOTE_EXT_NONE,
 	WIIMOTE_EXT_UNKNOWN,
+	WIIMOTE_EXT_NUNCHUK,
 	WIIMOTE_EXT_BALANCE_BOARD,
 	WIIMOTE_EXT_NUM,
 };
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 16/26] HID: wiimote: add Classic Controller extension
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (14 preceding siblings ...)
  2013-05-05 21:12 ` [PATCH 15/26] HID: wiimote: add Nunchuk support David Herrmann
@ 2013-05-05 21:13 ` David Herrmann
  2013-05-06  0:39   ` Todd Showalter
  2013-05-05 21:13 ` [PATCH 17/26] HID: wiimote: add Motion Plus extension module David Herrmann
                   ` (10 subsequent siblings)
  26 siblings, 1 reply; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:13 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

Add a new extension module for the classic controller so we get hotplug
support for this device. It is mostly the same as the old static classic
controller parser.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c    |   6 +
 drivers/hid/hid-wiimote-modules.c | 279 ++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-wiimote.h         |   1 +
 3 files changed, 286 insertions(+)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index dedf3c8..f6c4773 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -446,6 +446,8 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem)
 
 	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;
 
@@ -480,6 +482,9 @@ static bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype)
 
 	/* map MP with correct pass-through mode */
 	switch (exttype) {
+	case WIIMOTE_EXT_CLASSIC_CONTROLLER:
+		wmem = 0x07;
+		break;
 	case WIIMOTE_EXT_NUNCHUK:
 		wmem = 0x05;
 		break;
@@ -1040,6 +1045,7 @@ static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = {
 	[WIIMOTE_EXT_NONE] = "None",
 	[WIIMOTE_EXT_UNKNOWN] = "Unknown",
 	[WIIMOTE_EXT_NUNCHUK] = "Nintendo Wii Nunchuk",
+	[WIIMOTE_EXT_CLASSIC_CONTROLLER] = "Nintendo Wii Classic Controller",
 	[WIIMOTE_EXT_BALANCE_BOARD] = "Nintendo Wii Balance Board",
 };
 
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index e4bcc09..daeb679 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -986,6 +986,284 @@ static const struct wiimod_ops wiimod_nunchuk = {
 };
 
 /*
+ * Classic Controller
+ * Another official extension from Nintendo. It provides a classic
+ * gamecube-like controller that can be hotplugged on the Wii Remote.
+ * It has several hardware buttons and switches that are all reported via
+ * a normal extension device.
+ */
+
+enum wiimod_classic_keys {
+	WIIMOD_CLASSIC_KEY_A,
+	WIIMOD_CLASSIC_KEY_B,
+	WIIMOD_CLASSIC_KEY_X,
+	WIIMOD_CLASSIC_KEY_Y,
+	WIIMOD_CLASSIC_KEY_ZL,
+	WIIMOD_CLASSIC_KEY_ZR,
+	WIIMOD_CLASSIC_KEY_PLUS,
+	WIIMOD_CLASSIC_KEY_MINUS,
+	WIIMOD_CLASSIC_KEY_HOME,
+	WIIMOD_CLASSIC_KEY_LEFT,
+	WIIMOD_CLASSIC_KEY_RIGHT,
+	WIIMOD_CLASSIC_KEY_UP,
+	WIIMOD_CLASSIC_KEY_DOWN,
+	WIIMOD_CLASSIC_KEY_LT,
+	WIIMOD_CLASSIC_KEY_RT,
+	WIIMOD_CLASSIC_KEY_NUM,
+};
+
+static const __u16 wiimod_classic_map[] = {
+	BTN_A,		/* WIIMOD_CLASSIC_KEY_A */
+	BTN_B,		/* WIIMOD_CLASSIC_KEY_B */
+	BTN_X,		/* WIIMOD_CLASSIC_KEY_X */
+	BTN_Y,		/* WIIMOD_CLASSIC_KEY_Y */
+	BTN_TL2,	/* WIIMOD_CLASSIC_KEY_ZL */
+	BTN_TR2,	/* WIIMOD_CLASSIC_KEY_ZR */
+	KEY_NEXT,	/* WIIMOD_CLASSIC_KEY_PLUS */
+	KEY_PREVIOUS,	/* WIIMOD_CLASSIC_KEY_MINUS */
+	BTN_MODE,	/* WIIMOD_CLASSIC_KEY_HOME */
+	KEY_LEFT,	/* WIIMOD_CLASSIC_KEY_LEFT */
+	KEY_RIGHT,	/* WIIMOD_CLASSIC_KEY_RIGHT */
+	KEY_UP,		/* WIIMOD_CLASSIC_KEY_UP */
+	KEY_DOWN,	/* WIIMOD_CLASSIC_KEY_DOWN */
+	BTN_TL,		/* WIIMOD_CLASSIC_KEY_LT */
+	BTN_TR,		/* WIIMOD_CLASSIC_KEY_RT */
+};
+
+static void wiimod_classic_in_ext(struct wiimote_data *wdata, const __u8 *ext)
+{
+	__s8 rx, ry, lx, ly, lt, rt;
+
+	/*   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
+	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+	 *    1   | RX <5:4>  |              LX <5:0>             |
+	 *    2   | RX <3:2>  |              LY <5:0>             |
+	 *   -----+-----+-----+-----+-----------------------------+
+	 *    3   |RX<1>| LT <5:4>  |         RY <5:1>            |
+	 *   -----+-----+-----------+-----------------------------+
+	 *    4   |     LT <3:1>    |         RT <5:1>            |
+	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+	 *    5   | BDR | BDD | BLT | B-  | BH  | B+  | BRT |  1  |
+	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+	 *    6   | BZL | BB  | BY  | BA  | BX  | BZR | BDL | BDU |
+	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+	 * All buttons are 0 if pressed
+	 * RX and RY are right analog stick
+	 * LX and LY are left analog stick
+	 * LT is left trigger, RT is right trigger
+	 * BLT is 0 if left trigger is fully pressed
+	 * BRT is 0 if right trigger is fully pressed
+	 * BDR, BDD, BDL, BDU form the D-Pad with right, down, left, up buttons
+	 * BZL is left Z button and BZR is right Z button
+	 * B-, BH, B+ are +, HOME and - buttons
+	 * BB, BY, BA, BX are A, B, X, Y buttons
+	 * LSB of RX, RY, LT, and RT are not transmitted and always 0.
+	 *
+	 * With motionp enabled it changes slightly to this:
+	 *   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
+	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+	 *    1   | RX <4:3>  |          LX <5:1>           | BDU |
+	 *    2   | RX <2:1>  |          LY <5:1>           | BDL |
+	 *   -----+-----+-----+-----+-----------------------+-----+
+	 *    3   |RX<0>| LT <4:3>  |         RY <4:0>            |
+	 *   -----+-----+-----------+-----------------------------+
+	 *    4   |     LT <2:0>    |         RT <4:0>            |
+	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+	 *    5   | BDR | BDD | BLT | B-  | BH  | B+  | BRT | EXT |
+	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+	 *    6   | BZL | BB  | BY  | BA  | BX  | BZR |  0  |  0  |
+	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+	 * Only the LSBs of LX and LY are lost. BDU and BDL are moved, the rest
+	 * is the same as before.
+	 */
+
+	if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
+		lx = ext[0] & 0x3e;
+		ly = ext[0] & 0x3e;
+	} else {
+		lx = ext[0] & 0x3f;
+		ly = ext[0] & 0x3f;
+	}
+
+	rx = (ext[0] >> 3) & 0x14;
+	rx |= (ext[1] >> 5) & 0x06;
+	rx |= (ext[2] >> 7) & 0x01;
+	ry = ext[2] & 0x1f;
+
+	rt = ext[3] & 0x1f;
+	lt = (ext[2] >> 2) & 0x18;
+	lt |= (ext[3] >> 5) & 0x07;
+
+	rx <<= 1;
+	ry <<= 1;
+	rt <<= 1;
+	lt <<= 1;
+
+	input_report_abs(wdata->extension.input, ABS_HAT1X, lx - 0x20);
+	input_report_abs(wdata->extension.input, ABS_HAT1Y, ly - 0x20);
+	input_report_abs(wdata->extension.input, ABS_HAT2X, rx - 0x20);
+	input_report_abs(wdata->extension.input, ABS_HAT2Y, ry - 0x20);
+	input_report_abs(wdata->extension.input, ABS_HAT3X, rt - 0x20);
+	input_report_abs(wdata->extension.input, ABS_HAT3Y, lt - 0x20);
+
+	input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_RIGHT],
+			 !(ext[4] & 0x80));
+	input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_DOWN],
+			 !(ext[4] & 0x40));
+	input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_LT],
+			 !(ext[4] & 0x20));
+	input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_MINUS],
+			 !(ext[4] & 0x10));
+	input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_HOME],
+			 !(ext[4] & 0x08));
+	input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_PLUS],
+			 !(ext[4] & 0x04));
+	input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_RT],
+			 !(ext[4] & 0x02));
+	input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_ZL],
+			 !(ext[5] & 0x80));
+	input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_B],
+			 !(ext[5] & 0x40));
+	input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_Y],
+			 !(ext[5] & 0x20));
+	input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_A],
+			 !(ext[5] & 0x10));
+	input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_X],
+			 !(ext[5] & 0x08));
+	input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_ZR],
+			 !(ext[5] & 0x04));
+
+	if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
+		input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_LEFT],
+			 !(ext[1] & 0x01));
+		input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_UP],
+			 !(ext[0] & 0x01));
+	} else {
+		input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_LEFT],
+			 !(ext[5] & 0x02));
+		input_report_key(wdata->extension.input,
+			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_UP],
+			 !(ext[5] & 0x01));
+	}
+
+	input_sync(wdata->extension.input);
+}
+
+static int wiimod_classic_open(struct input_dev *dev)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags |= WIIPROTO_FLAG_EXT_USED;
+	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	return 0;
+}
+
+static void wiimod_classic_close(struct input_dev *dev)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
+	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+static int wiimod_classic_probe(const struct wiimod_ops *ops,
+				struct wiimote_data *wdata)
+{
+	int ret, i;
+
+	wdata->extension.input = input_allocate_device();
+	if (!wdata->extension.input)
+		return -ENOMEM;
+
+	input_set_drvdata(wdata->extension.input, wdata);
+	wdata->extension.input->open = wiimod_classic_open;
+	wdata->extension.input->close = wiimod_classic_close;
+	wdata->extension.input->dev.parent = &wdata->hdev->dev;
+	wdata->extension.input->id.bustype = wdata->hdev->bus;
+	wdata->extension.input->id.vendor = wdata->hdev->vendor;
+	wdata->extension.input->id.product = wdata->hdev->product;
+	wdata->extension.input->id.version = wdata->hdev->version;
+	wdata->extension.input->name = WIIMOTE_NAME " Classic Controller";
+
+	set_bit(EV_KEY, wdata->extension.input->evbit);
+	for (i = 0; i < WIIMOD_CLASSIC_KEY_NUM; ++i)
+		set_bit(wiimod_classic_map[i],
+			wdata->extension.input->keybit);
+
+	set_bit(EV_ABS, wdata->extension.input->evbit);
+	set_bit(ABS_HAT1X, wdata->extension.input->absbit);
+	set_bit(ABS_HAT1Y, wdata->extension.input->absbit);
+	set_bit(ABS_HAT2X, wdata->extension.input->absbit);
+	set_bit(ABS_HAT2Y, wdata->extension.input->absbit);
+	set_bit(ABS_HAT3X, wdata->extension.input->absbit);
+	set_bit(ABS_HAT3Y, wdata->extension.input->absbit);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT1X, -30, 30, 1, 1);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT1Y, -30, 30, 1, 1);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT2X, -30, 30, 1, 1);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT2Y, -30, 30, 1, 1);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT3X, -30, 30, 1, 1);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT3Y, -30, 30, 1, 1);
+
+	ret = input_register_device(wdata->extension.input);
+	if (ret)
+		goto err_free;
+
+	return 0;
+
+err_free:
+	input_free_device(wdata->extension.input);
+	wdata->extension.input = NULL;
+	return ret;
+}
+
+static void wiimod_classic_remove(const struct wiimod_ops *ops,
+				  struct wiimote_data *wdata)
+{
+	if (!wdata->extension.input)
+		return;
+
+	input_unregister_device(wdata->extension.input);
+	wdata->extension.input = NULL;
+}
+
+static const struct wiimod_ops wiimod_classic = {
+	.flags = 0,
+	.arg = 0,
+	.probe = wiimod_classic_probe,
+	.remove = wiimod_classic_remove,
+	.in_ext = wiimod_classic_in_ext,
+};
+
+/*
  * Balance Board Extension
  * The Nintendo Wii Balance Board provides four hardware weight sensor plus a
  * single push button. No other peripherals are available. However, the
@@ -1224,5 +1502,6 @@ const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = {
 	[WIIMOTE_EXT_NONE] = &wiimod_dummy,
 	[WIIMOTE_EXT_UNKNOWN] = &wiimod_dummy,
 	[WIIMOTE_EXT_NUNCHUK] = &wiimod_nunchuk,
+	[WIIMOTE_EXT_CLASSIC_CONTROLLER] = &wiimod_classic,
 	[WIIMOTE_EXT_BALANCE_BOARD] = &wiimod_bboard,
 };
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 3414e4c..118520a 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -81,6 +81,7 @@ enum wiimote_exttype {
 	WIIMOTE_EXT_NONE,
 	WIIMOTE_EXT_UNKNOWN,
 	WIIMOTE_EXT_NUNCHUK,
+	WIIMOTE_EXT_CLASSIC_CONTROLLER,
 	WIIMOTE_EXT_BALANCE_BOARD,
 	WIIMOTE_EXT_NUM,
 };
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 17/26] HID: wiimote: add Motion Plus extension module
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (15 preceding siblings ...)
  2013-05-05 21:13 ` [PATCH 16/26] HID: wiimote: add Classic Controller extension David Herrmann
@ 2013-05-05 21:13 ` David Herrmann
  2013-05-05 21:13 ` [PATCH 18/26] HID: wiimote: fix ctx pointer in debugfs DRM-write David Herrmann
                   ` (9 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:13 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

Add parsers for motion plus data so we can hotplug motion plus extensions
and make use of them. This is mostly the same as the old static motion
plus parser.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-modules.c | 144 ++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-wiimote.h         |   1 +
 2 files changed, 145 insertions(+)

diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index daeb679..aee1b2c 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -1475,11 +1475,155 @@ static const struct wiimod_ops wiimod_bboard = {
 
 /*
  * Motion Plus
+ * The Motion Plus extension provides rotation sensors (gyro) as a small
+ * extension device for Wii Remotes. Many devices have them built-in so
+ * you cannot see them from the outside.
+ * Motion Plus extensions are special because they are on a separate extension
+ * port and allow other extensions to be used simultaneously. This is all
+ * handled by the Wiimote Core so we don't have to deal with it.
  */
 
+static void wiimod_mp_in_mp(struct wiimote_data *wdata, const __u8 *ext)
+{
+	__s32 x, y, z;
+
+	/*        |   8    7    6    5    4    3 |  2  |  1  |
+	 *   -----+------------------------------+-----+-----+
+	 *    1   |               Yaw Speed <7:0>            |
+	 *    2   |              Roll Speed <7:0>            |
+	 *    3   |             Pitch Speed <7:0>            |
+	 *   -----+------------------------------+-----+-----+
+	 *    4   |       Yaw Speed <13:8>       | Yaw |Pitch|
+	 *   -----+------------------------------+-----+-----+
+	 *    5   |      Roll Speed <13:8>       |Roll | Ext |
+	 *   -----+------------------------------+-----+-----+
+	 *    6   |     Pitch Speed <13:8>       |  1  |  0  |
+	 *   -----+------------------------------+-----+-----+
+	 * The single bits Yaw, Roll, Pitch in the lower right corner specify
+	 * whether the wiimote is rotating fast (0) or slow (1). Speed for slow
+	 * roation is 440 deg/s and for fast rotation 2000 deg/s. To get a
+	 * linear scale we multiply by 2000/440 = ~4.5454 which is 18 for fast
+	 * and 9 for slow.
+	 * If the wiimote is not rotating the sensor reports 2^13 = 8192.
+	 * Ext specifies whether an extension is connected to the motionp.
+	 * which is parsed by wiimote-core.
+	 */
+
+	x = ext[0];
+	y = ext[1];
+	z = ext[2];
+
+	x |= (((__u16)ext[3]) << 6) & 0xff00;
+	y |= (((__u16)ext[4]) << 6) & 0xff00;
+	z |= (((__u16)ext[5]) << 6) & 0xff00;
+
+	x -= 8192;
+	y -= 8192;
+	z -= 8192;
+
+	if (!(ext[3] & 0x02))
+		x *= 18;
+	else
+		x *= 9;
+	if (!(ext[4] & 0x02))
+		y *= 18;
+	else
+		y *= 9;
+	if (!(ext[3] & 0x01))
+		z *= 18;
+	else
+		z *= 9;
+
+	input_report_abs(wdata->mp, ABS_RX, x);
+	input_report_abs(wdata->mp, ABS_RY, y);
+	input_report_abs(wdata->mp, ABS_RZ, z);
+	input_sync(wdata->mp);
+}
+
+static int wiimod_mp_open(struct input_dev *dev)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags |= WIIPROTO_FLAG_MP_USED;
+	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+	__wiimote_schedule(wdata);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	return 0;
+}
+
+static void wiimod_mp_close(struct input_dev *dev)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags &= ~WIIPROTO_FLAG_MP_USED;
+	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+	__wiimote_schedule(wdata);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+static int wiimod_mp_probe(const struct wiimod_ops *ops,
+			   struct wiimote_data *wdata)
+{
+	int ret;
+
+	wdata->mp = input_allocate_device();
+	if (!wdata->mp)
+		return -ENOMEM;
+
+	input_set_drvdata(wdata->mp, wdata);
+	wdata->mp->open = wiimod_mp_open;
+	wdata->mp->close = wiimod_mp_close;
+	wdata->mp->dev.parent = &wdata->hdev->dev;
+	wdata->mp->id.bustype = wdata->hdev->bus;
+	wdata->mp->id.vendor = wdata->hdev->vendor;
+	wdata->mp->id.product = wdata->hdev->product;
+	wdata->mp->id.version = wdata->hdev->version;
+	wdata->mp->name = WIIMOTE_NAME " Motion Plus";
+
+	set_bit(EV_ABS, wdata->mp->evbit);
+	set_bit(ABS_RX, wdata->mp->absbit);
+	set_bit(ABS_RY, wdata->mp->absbit);
+	set_bit(ABS_RZ, wdata->mp->absbit);
+	input_set_abs_params(wdata->mp,
+			     ABS_RX, -16000, 16000, 4, 8);
+	input_set_abs_params(wdata->mp,
+			     ABS_RY, -16000, 16000, 4, 8);
+	input_set_abs_params(wdata->mp,
+			     ABS_RZ, -16000, 16000, 4, 8);
+
+	ret = input_register_device(wdata->mp);
+	if (ret)
+		goto err_free;
+
+	return 0;
+
+err_free:
+	input_free_device(wdata->mp);
+	wdata->mp = NULL;
+	return ret;
+}
+
+static void wiimod_mp_remove(const struct wiimod_ops *ops,
+			     struct wiimote_data *wdata)
+{
+	if (!wdata->mp)
+		return;
+
+	input_unregister_device(wdata->mp);
+	wdata->mp = NULL;
+}
+
 const struct wiimod_ops wiimod_mp = {
 	.flags = 0,
 	.arg = 0,
+	.probe = wiimod_mp_probe,
+	.remove = wiimod_mp_remove,
+	.in_mp = wiimod_mp_in_mp,
 };
 
 /* module table */
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 118520a..3a8bdb9 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -139,6 +139,7 @@ struct wiimote_data {
 	struct input_dev *accel;
 	struct input_dev *ir;
 	struct power_supply battery;
+	struct input_dev *mp;
 	struct timer_list timer;
 	struct wiimote_ext *ext;
 	struct wiimote_debug *debug;
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 18/26] HID: wiimote: fix ctx pointer in debugfs DRM-write
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (16 preceding siblings ...)
  2013-05-05 21:13 ` [PATCH 17/26] HID: wiimote: add Motion Plus extension module David Herrmann
@ 2013-05-05 21:13 ` David Herrmann
  2013-05-05 21:13 ` [PATCH 19/26] HID: wiimote: lock DRM mode during debugfs overwrite David Herrmann
                   ` (8 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:13 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

single_open() stores the seq_file pointer in file->private_data. It stores
our ctx pointer in seq_file->private.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-debug.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-wiimote-debug.c b/drivers/hid/hid-wiimote-debug.c
index fdd30dd..0c0deaa 100644
--- a/drivers/hid/hid-wiimote-debug.c
+++ b/drivers/hid/hid-wiimote-debug.c
@@ -127,7 +127,8 @@ static int wiidebug_drm_open(struct inode *i, struct file *f)
 static ssize_t wiidebug_drm_write(struct file *f, const char __user *u,
 							size_t s, loff_t *off)
 {
-	struct wiimote_debug *dbg = f->private_data;
+	struct seq_file *sf = f->private_data;
+	struct wiimote_debug *dbg = sf->private;
 	unsigned long flags;
 	char buf[16];
 	ssize_t len;
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 19/26] HID: wiimote: lock DRM mode during debugfs overwrite
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (17 preceding siblings ...)
  2013-05-05 21:13 ` [PATCH 18/26] HID: wiimote: fix ctx pointer in debugfs DRM-write David Herrmann
@ 2013-05-05 21:13 ` David Herrmann
  2013-05-05 21:13 ` [PATCH 20/26] HID: wiimote: add sysfs extension/device-type attrs David Herrmann
                   ` (7 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:13 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

If we write a DRM mode via debugfs, we shouldn't allow normal operations
to overwrite this DRM mode. This is important if we want to debug
3rd-party devices and we want to see what data is sent on each mode.

If we write NULL/0 as DRM, the lock is removed again so the best matching
DRM is chosen by wiimote core.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c  | 4 +++-
 drivers/hid/hid-wiimote-debug.c | 3 +++
 drivers/hid/hid-wiimote.h       | 1 +
 3 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index f6c4773..13963b4 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -242,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;
diff --git a/drivers/hid/hid-wiimote-debug.c b/drivers/hid/hid-wiimote-debug.c
index 0c0deaa..6e76a2c 100644
--- a/drivers/hid/hid-wiimote-debug.c
+++ b/drivers/hid/hid-wiimote-debug.c
@@ -154,7 +154,10 @@ static ssize_t wiidebug_drm_write(struct file *f, const char __user *u,
 		i = simple_strtoul(buf, NULL, 10);
 
 	spin_lock_irqsave(&dbg->wdata->state.lock, flags);
+	dbg->wdata->state.flags &= ~WIIPROTO_FLAG_DRM_LOCKED;
 	wiiproto_req_drm(dbg->wdata, (__u8) i);
+	if (i != WIIPROTO_REQ_NULL)
+		dbg->wdata->state.flags |= WIIPROTO_FLAG_DRM_LOCKED;
 	spin_unlock_irqrestore(&dbg->wdata->state.lock, flags);
 
 	return len;
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 3a8bdb9..9f857c1 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -43,6 +43,7 @@
 #define WIIPROTO_FLAG_MP_USED		0x1000
 #define WIIPROTO_FLAG_MP_ACTIVE		0x2000
 #define WIIPROTO_FLAG_EXITING		0x4000
+#define WIIPROTO_FLAG_DRM_LOCKED	0x8000
 
 #define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
 					WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 20/26] HID: wiimote: add sysfs extension/device-type attrs
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (18 preceding siblings ...)
  2013-05-05 21:13 ` [PATCH 19/26] HID: wiimote: lock DRM mode during debugfs overwrite David Herrmann
@ 2013-05-05 21:13 ` David Herrmann
  2013-05-05 21:13 ` [PATCH 21/26] HID: wiimote: add "bboard_calib" attribute David Herrmann
                   ` (6 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:13 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

Two new attributes, "extension" and "devtype" now allow user-space to read
the extension type and device type. As device detection is asynchronous,
we send a CHANGED event after it is done. This also allows user-space to
wait for a device to settle before opening its input event devices.

The "extension" device is compatible with the old "extension" sysfs field
(which was registered by the static extension support code).

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 Documentation/ABI/testing/sysfs-driver-hid-wiimote |  24 ++++-
 drivers/hid/hid-wiimote-core.c                     | 106 ++++++++++++++++++++-
 2 files changed, 128 insertions(+), 2 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-driver-hid-wiimote b/Documentation/ABI/testing/sysfs-driver-hid-wiimote
index 3d98009..e8f6b16 100644
--- a/Documentation/ABI/testing/sysfs-driver-hid-wiimote
+++ b/Documentation/ABI/testing/sysfs-driver-hid-wiimote
@@ -12,7 +12,7 @@ Description:	Make it possible to set/get current led state. Reading from it
 What:		/sys/bus/hid/drivers/wiimote/<dev>/extension
 Date:		August 2011
 KernelVersion:	3.2
-Contact:	David Herrmann <dh.herrmann@googlemail.com>
+Contact:	David Herrmann <dh.herrmann@gmail.com>
 Description:	This file contains the currently connected and initialized
 		extensions. It can be one of: none, motionp, nunchuck, classic,
 		motionp+nunchuck, motionp+classic
@@ -20,3 +20,25 @@ Description:	This file contains the currently connected and initialized
 		the official Nintendo Nunchuck extension and classic is the
 		Nintendo Classic Controller extension. The motionp extension can
 		be combined with the other two.
+		Starting with kernel-version 3.11 Motion Plus hotplugging is
+		supported and if detected, it's no longer reported as static
+		extension. You will get uevent notifications for the motion-plus
+		device then.
+
+What:		/sys/bus/hid/drivers/wiimote/<dev>/devtype
+Date:		May 2013
+KernelVersion:	3.11
+Contact:	David Herrmann <dh.herrmann@gmail.com>
+Description:	While a device is initialized by the wiimote driver, we perform
+		a device detection and signal a "change" uevent after it is
+		done. This file shows the detected device type. "pending" means
+		that the detection is still ongoing, "unknown" means, that the
+		device couldn't be detected or loaded. "generic" means, that the
+		device couldn't be detected but supports basic Wii Remote
+		features and can be used.
+		Other strings for each device-type are available and may be
+		added if new device-specific detections are added.
+		Currently supported are:
+			gen10: First Wii Remote generation
+			gen20: Second Wii Remote Plus generation (builtin MP)
+			balanceboard: Wii Balance Board
diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 13963b4..fa58045 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -1166,11 +1166,18 @@ static void wiimote_init_worker(struct work_struct *work)
 {
 	struct wiimote_data *wdata = container_of(work, struct wiimote_data,
 						  init_worker);
+	bool changed = false;
 
-	if (wdata->state.devtype == WIIMOTE_DEV_PENDING)
+	if (wdata->state.devtype == WIIMOTE_DEV_PENDING) {
 		wiimote_init_detect(wdata);
+		changed = true;
+	}
+
 	if (!wiimote_init_check(wdata))
 		wiimote_init_hotplug(wdata);
+
+	if (changed)
+		kobject_uevent(&wdata->hdev->dev.kobj, KOBJ_CHANGE);
 }
 
 void __wiimote_schedule(struct wiimote_data *wdata)
@@ -1591,6 +1598,84 @@ static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report,
 	return 0;
 }
 
+static ssize_t wiimote_ext_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct wiimote_data *wdata = dev_to_wii(dev);
+	__u8 type;
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	type = wdata->state.exttype;
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	switch (type) {
+	case WIIMOTE_EXT_NONE:
+		return sprintf(buf, "none\n");
+	case WIIMOTE_EXT_NUNCHUK:
+		return sprintf(buf, "nunchuk\n");
+	case WIIMOTE_EXT_CLASSIC_CONTROLLER:
+		return sprintf(buf, "classic\n");
+	case WIIMOTE_EXT_BALANCE_BOARD:
+		return sprintf(buf, "balanceboard\n");
+	case WIIMOTE_EXT_UNKNOWN:
+		/* fallthrough */
+	default:
+		return sprintf(buf, "unknown\n");
+	}
+}
+
+static ssize_t wiimote_ext_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct wiimote_data *wdata = dev_to_wii(dev);
+
+	if (!strcmp(buf, "scan")) {
+		wiimote_schedule(wdata);
+	} else {
+		return -EINVAL;
+	}
+
+	return strnlen(buf, PAGE_SIZE);
+}
+
+static DEVICE_ATTR(extension, S_IRUGO | S_IWUSR | S_IWGRP, wiimote_ext_show,
+		   wiimote_ext_store);
+
+static ssize_t wiimote_dev_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct wiimote_data *wdata = dev_to_wii(dev);
+	__u8 type;
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	type = wdata->state.devtype;
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	switch (type) {
+	case WIIMOTE_DEV_GENERIC:
+		return sprintf(buf, "generic\n");
+	case WIIMOTE_DEV_GEN10:
+		return sprintf(buf, "gen10\n");
+	case WIIMOTE_DEV_GEN20:
+		return sprintf(buf, "gen20\n");
+	case WIIMOTE_DEV_BALANCE_BOARD:
+		return sprintf(buf, "balanceboard\n");
+	case WIIMOTE_DEV_PENDING:
+		return sprintf(buf, "pending\n");
+	case WIIMOTE_DEV_UNKNOWN:
+		/* fallthrough */
+	default:
+		return sprintf(buf, "unknown\n");
+	}
+}
+
+static DEVICE_ATTR(devtype, S_IRUGO, wiimote_dev_show, NULL);
+
 static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 {
 	struct wiimote_data *wdata;
@@ -1631,6 +1716,9 @@ static void wiimote_destroy(struct wiimote_data *wdata)
 	cancel_work_sync(&wdata->init_worker);
 	del_timer_sync(&wdata->timer);
 
+	device_remove_file(&wdata->hdev->dev, &dev_attr_devtype);
+	device_remove_file(&wdata->hdev->dev, &dev_attr_extension);
+
 	wiimote_mp_unload(wdata);
 	wiimote_ext_unload(wdata);
 	wiimote_modules_unload(wdata);
@@ -1673,6 +1761,18 @@ static int wiimote_hid_probe(struct hid_device *hdev,
 		goto err_stop;
 	}
 
+	ret = device_create_file(&hdev->dev, &dev_attr_extension);
+	if (ret) {
+		hid_err(hdev, "cannot create sysfs attribute\n");
+		goto err_close;
+	}
+
+	ret = device_create_file(&hdev->dev, &dev_attr_devtype);
+	if (ret) {
+		hid_err(hdev, "cannot create sysfs attribute\n");
+		goto err_ext;
+	}
+
 	ret = wiidebug_init(wdata);
 	if (ret)
 		goto err_free;
@@ -1688,6 +1788,10 @@ err_free:
 	wiimote_destroy(wdata);
 	return ret;
 
+err_ext:
+	device_remove_file(&wdata->hdev->dev, &dev_attr_extension);
+err_close:
+	hid_hw_close(hdev);
 err_stop:
 	hid_hw_stop(hdev);
 err:
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 21/26] HID: wiimote: add "bboard_calib" attribute
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (19 preceding siblings ...)
  2013-05-05 21:13 ` [PATCH 20/26] HID: wiimote: add sysfs extension/device-type attrs David Herrmann
@ 2013-05-05 21:13 ` David Herrmann
  2013-05-05 21:13 ` [PATCH 22/26] HID: wiimote: remove old static extension support David Herrmann
                   ` (5 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:13 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

Balance-Boards provide 3 16bit calibration values for each of the 4
sensors. We provide these now as 192bit value via a new "bboard_calib"
sysfs attribute.
We also re-read the calibration data from the device whenever user-space
attempts to read this file. On normal Nintendo boards, this always
produces the same results, however, on some 3rd party devices these values
change until the device is fully initialized. As I have currently no idea
how long to wait until it's ready (sometimes takes up to 10s?) we provide
a simple workaround for users by reading this file.

If we, at some point, figure out how it works, we can implement it in the
kernel and provide offline data via "bboard_calib". This won't break
user-space then.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 Documentation/ABI/testing/sysfs-driver-hid-wiimote | 15 +++++
 drivers/hid/hid-wiimote-modules.c                  | 68 +++++++++++++++++++++-
 2 files changed, 82 insertions(+), 1 deletion(-)

diff --git a/Documentation/ABI/testing/sysfs-driver-hid-wiimote b/Documentation/ABI/testing/sysfs-driver-hid-wiimote
index e8f6b16..ed5dd56 100644
--- a/Documentation/ABI/testing/sysfs-driver-hid-wiimote
+++ b/Documentation/ABI/testing/sysfs-driver-hid-wiimote
@@ -42,3 +42,18 @@ Description:	While a device is initialized by the wiimote driver, we perform
 			gen10: First Wii Remote generation
 			gen20: Second Wii Remote Plus generation (builtin MP)
 			balanceboard: Wii Balance Board
+
+What:		/sys/bus/hid/drivers/wiimote/<dev>/bboard_calib
+Date:		May 2013
+KernelVersion:	3.11
+Contact:	David Herrmann <dh.herrmann@gmail.com>
+Description:	This attribute is only provided if the device was detected as a
+		balance board. It provides a single line with 3 calibration
+		values for all 4 sensors. The values are separated by colons and
+		are each 2 bytes long (encoded as 4 digit hexadecimal value).
+		First, 0kg values for all 4 sensors are written, followed by the
+		17kg values for all 4 sensors and last the 34kg values for all 4
+		sensors.
+		Calibration data is already applied by the kernel to all input
+		values but may be used by user-space to perform other
+		transformations.
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index aee1b2c..19b8b1c 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -1380,6 +1380,60 @@ static void wiimod_bboard_close(struct input_dev *dev)
 	spin_unlock_irqrestore(&wdata->state.lock, flags);
 }
 
+static ssize_t wiimod_bboard_calib_show(struct device *dev,
+					struct device_attribute *attr,
+					char *out)
+{
+	struct wiimote_data *wdata = dev_to_wii(dev);
+	int i, j, ret;
+	__u16 val;
+	__u8 buf[24], offs;
+
+	ret = wiimote_cmd_acquire(wdata);
+	if (ret)
+		return ret;
+
+	ret = wiimote_cmd_read(wdata, 0xa40024, buf, 12);
+	if (ret != 12) {
+		wiimote_cmd_release(wdata);
+		return ret < 0 ? ret : -EIO;
+	}
+	ret = wiimote_cmd_read(wdata, 0xa40024 + 12, buf + 12, 12);
+	if (ret != 12) {
+		wiimote_cmd_release(wdata);
+		return ret < 0 ? ret : -EIO;
+	}
+
+	wiimote_cmd_release(wdata);
+
+	spin_lock_irq(&wdata->state.lock);
+	offs = 0;
+	for (i = 0; i < 3; ++i) {
+		for (j = 0; j < 4; ++j) {
+			wdata->state.calib_bboard[j][i] = buf[offs];
+			wdata->state.calib_bboard[j][i] <<= 8;
+			wdata->state.calib_bboard[j][i] |= buf[offs + 1];
+			offs += 2;
+		}
+	}
+	spin_unlock_irq(&wdata->state.lock);
+
+	ret = 0;
+	for (i = 0; i < 3; ++i) {
+		for (j = 0; j < 4; ++j) {
+			val = wdata->state.calib_bboard[j][i];
+			if (i == 2 && j == 3)
+				ret += sprintf(&out[ret], "%04x\n", val);
+			else
+				ret += sprintf(&out[ret], "%04x:", val);
+		}
+	}
+
+	return ret;
+}
+
+static DEVICE_ATTR(bboard_calib, S_IRUGO, wiimod_bboard_calib_show, NULL);
+
 static int wiimod_bboard_probe(const struct wiimod_ops *ops,
 			       struct wiimote_data *wdata)
 {
@@ -1415,6 +1469,13 @@ static int wiimod_bboard_probe(const struct wiimod_ops *ops,
 	if (!wdata->extension.input)
 		return -ENOMEM;
 
+	ret = device_create_file(&wdata->hdev->dev,
+				 &dev_attr_bboard_calib);
+	if (ret) {
+		hid_err(wdata->hdev, "cannot create sysfs attribute\n");
+		goto err_free;
+	}
+
 	input_set_drvdata(wdata->extension.input, wdata);
 	wdata->extension.input->open = wiimod_bboard_open;
 	wdata->extension.input->close = wiimod_bboard_close;
@@ -1444,10 +1505,13 @@ static int wiimod_bboard_probe(const struct wiimod_ops *ops,
 
 	ret = input_register_device(wdata->extension.input);
 	if (ret)
-		goto err_free;
+		goto err_file;
 
 	return 0;
 
+err_file:
+	device_remove_file(&wdata->hdev->dev,
+			   &dev_attr_bboard_calib);
 err_free:
 	input_free_device(wdata->extension.input);
 	wdata->extension.input = NULL;
@@ -1462,6 +1526,8 @@ static void wiimod_bboard_remove(const struct wiimod_ops *ops,
 
 	input_unregister_device(wdata->extension.input);
 	wdata->extension.input = NULL;
+	device_remove_file(&wdata->hdev->dev,
+			   &dev_attr_bboard_calib);
 }
 
 static const struct wiimod_ops wiimod_bboard = {
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 22/26] HID: wiimote: remove old static extension support
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (20 preceding siblings ...)
  2013-05-05 21:13 ` [PATCH 21/26] HID: wiimote: add "bboard_calib" attribute David Herrmann
@ 2013-05-05 21:13 ` David Herrmann
  2013-05-05 21:13 ` [PATCH 23/26] HID: wiimote: add MP quirks David Herrmann
                   ` (4 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:13 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

We now have dynamic hotplug support so the old static extensions are no
longer needed nor used. Remove it along CONFIG_HID_WIIMOTE_EXT.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/Kconfig           |   9 -
 drivers/hid/Makefile          |   3 -
 drivers/hid/hid-wiimote-ext.c | 837 ------------------------------------------
 drivers/hid/hid-wiimote.h     |  19 -
 4 files changed, 868 deletions(-)
 delete mode 100644 drivers/hid/hid-wiimote-ext.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index e05dda3..e8ef86c 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -720,15 +720,6 @@ config HID_WIIMOTE
 	To compile this driver as a module, choose M here: the
 	module will be called hid-wiimote.
 
-config HID_WIIMOTE_EXT
-	bool "Nintendo Wii Remote Extension support"
-	depends on HID_WIIMOTE
-	default HID_WIIMOTE
-	---help---
-	Support for extension controllers of the Nintendo Wii Remote. Say yes
-	here if you want to use the Nintendo Motion+, Nunchuck or Classic
-	extension controllers with your Wii Remote.
-
 config HID_ZEROPLUS
 	tristate "Zeroplus based game controller support"
 	depends on HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 6015af5..8b7106b 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -29,9 +29,6 @@ ifdef CONFIG_LOGIWHEELS_FF
 endif
 
 hid-wiimote-y		:= hid-wiimote-core.o hid-wiimote-modules.o
-ifdef CONFIG_HID_WIIMOTE_EXT
-	hid-wiimote-y	+= hid-wiimote-ext.o
-endif
 ifdef CONFIG_DEBUG_FS
 	hid-wiimote-y	+= hid-wiimote-debug.o
 endif
diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c
deleted file mode 100644
index 267c89c..0000000
--- a/drivers/hid/hid-wiimote-ext.c
+++ /dev/null
@@ -1,837 +0,0 @@
-/*
- * HID driver for Nintendo Wii / Wii U peripheral extensions
- * 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.
- */
-
-#include <linux/atomic.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/workqueue.h>
-#include "hid-wiimote.h"
-
-struct wiimote_ext {
-	struct wiimote_data *wdata;
-	struct work_struct worker;
-	struct input_dev *input;
-	struct input_dev *mp_input;
-
-	atomic_t opened;
-	atomic_t mp_opened;
-	bool plugged;
-	bool mp_plugged;
-	bool motionp;
-	__u8 ext_type;
-	__u16 calib[4][3];
-};
-
-enum wiiext_type {
-	WIIEXT_NONE,		/* placeholder */
-	WIIEXT_CLASSIC,		/* Nintendo classic controller */
-	WIIEXT_NUNCHUCK,	/* Nintendo nunchuck controller */
-	WIIEXT_BALANCE_BOARD,	/* Nintendo balance board controller */
-};
-
-enum wiiext_keys {
-	WIIEXT_KEY_C,
-	WIIEXT_KEY_Z,
-	WIIEXT_KEY_A,
-	WIIEXT_KEY_B,
-	WIIEXT_KEY_X,
-	WIIEXT_KEY_Y,
-	WIIEXT_KEY_ZL,
-	WIIEXT_KEY_ZR,
-	WIIEXT_KEY_PLUS,
-	WIIEXT_KEY_MINUS,
-	WIIEXT_KEY_HOME,
-	WIIEXT_KEY_LEFT,
-	WIIEXT_KEY_RIGHT,
-	WIIEXT_KEY_UP,
-	WIIEXT_KEY_DOWN,
-	WIIEXT_KEY_LT,
-	WIIEXT_KEY_RT,
-	WIIEXT_KEY_COUNT
-};
-
-static __u16 wiiext_keymap[] = {
-	BTN_C,		/* WIIEXT_KEY_C */
-	BTN_Z,		/* WIIEXT_KEY_Z */
-	BTN_A,		/* WIIEXT_KEY_A */
-	BTN_B,		/* WIIEXT_KEY_B */
-	BTN_X,		/* WIIEXT_KEY_X */
-	BTN_Y,		/* WIIEXT_KEY_Y */
-	BTN_TL2,	/* WIIEXT_KEY_ZL */
-	BTN_TR2,	/* WIIEXT_KEY_ZR */
-	KEY_NEXT,	/* WIIEXT_KEY_PLUS */
-	KEY_PREVIOUS,	/* WIIEXT_KEY_MINUS */
-	BTN_MODE,	/* WIIEXT_KEY_HOME */
-	KEY_LEFT,	/* WIIEXT_KEY_LEFT */
-	KEY_RIGHT,	/* WIIEXT_KEY_RIGHT */
-	KEY_UP,		/* WIIEXT_KEY_UP */
-	KEY_DOWN,	/* WIIEXT_KEY_DOWN */
-	BTN_TL,		/* WIIEXT_KEY_LT */
-	BTN_TR,		/* WIIEXT_KEY_RT */
-};
-
-/* disable all extensions */
-static void ext_disable(struct wiimote_ext *ext)
-{
-	unsigned long flags;
-	__u8 wmem = 0x55;
-
-	if (!wiimote_cmd_acquire(ext->wdata)) {
-		wiimote_cmd_write(ext->wdata, 0xa400f0, &wmem, sizeof(wmem));
-		wiimote_cmd_release(ext->wdata);
-	}
-
-	spin_lock_irqsave(&ext->wdata->state.lock, flags);
-	ext->motionp = false;
-	ext->ext_type = WIIEXT_NONE;
-	wiiproto_req_drm(ext->wdata, WIIPROTO_REQ_NULL);
-	spin_unlock_irqrestore(&ext->wdata->state.lock, flags);
-}
-
-static bool motionp_read(struct wiimote_ext *ext)
-{
-	__u8 rmem[2], wmem;
-	ssize_t ret;
-	bool avail = false;
-
-	if (!atomic_read(&ext->mp_opened))
-		return false;
-
-	if (wiimote_cmd_acquire(ext->wdata))
-		return false;
-
-	/* initialize motion plus */
-	wmem = 0x55;
-	ret = wiimote_cmd_write(ext->wdata, 0xa600f0, &wmem, sizeof(wmem));
-	if (ret)
-		goto error;
-
-	/* read motion plus ID */
-	ret = wiimote_cmd_read(ext->wdata, 0xa600fe, rmem, 2);
-	if (ret == 2 || rmem[1] == 0x5)
-		avail = true;
-
-error:
-	wiimote_cmd_release(ext->wdata);
-	return avail;
-}
-
-static __u8 ext_read(struct wiimote_ext *ext)
-{
-	ssize_t ret;
-	__u8 buf[24], i, j, offs = 0;
-	__u8 rmem[2], wmem;
-	__u8 type = WIIEXT_NONE;
-
-	if (!ext->plugged || !atomic_read(&ext->opened))
-		return WIIEXT_NONE;
-
-	if (wiimote_cmd_acquire(ext->wdata))
-		return WIIEXT_NONE;
-
-	/* initialize extension */
-	wmem = 0x55;
-	ret = wiimote_cmd_write(ext->wdata, 0xa400f0, &wmem, sizeof(wmem));
-	if (!ret) {
-		/* disable encryption */
-		wmem = 0x0;
-		wiimote_cmd_write(ext->wdata, 0xa400fb, &wmem, sizeof(wmem));
-	}
-
-	/* read extension ID */
-	ret = wiimote_cmd_read(ext->wdata, 0xa400fe, rmem, 2);
-	if (ret == 2) {
-		if (rmem[0] == 0 && rmem[1] == 0)
-			type = WIIEXT_NUNCHUCK;
-		else if (rmem[0] == 0x01 && rmem[1] == 0x01)
-			type = WIIEXT_CLASSIC;
-		else if (rmem[0] == 0x04 && rmem[1] == 0x02)
-			type = WIIEXT_BALANCE_BOARD;
-	}
-
-	/* get balance board calibration data */
-	if (type == WIIEXT_BALANCE_BOARD) {
-		ret = wiimote_cmd_read(ext->wdata, 0xa40024, buf, 12);
-		ret += wiimote_cmd_read(ext->wdata, 0xa40024 + 12,
-					buf + 12, 12);
-
-		if (ret != 24) {
-			type = WIIEXT_NONE;
-		} else {
-			for (i = 0; i < 3; i++) {
-				for (j = 0; j < 4; j++) {
-					ext->calib[j][i] = buf[offs];
-					ext->calib[j][i] <<= 8;
-					ext->calib[j][i] |= buf[offs + 1];
-					offs += 2;
-				}
-			}
-		}
-	}
-
-	wiimote_cmd_release(ext->wdata);
-
-	return type;
-}
-
-static void ext_enable(struct wiimote_ext *ext, bool motionp, __u8 ext_type)
-{
-	unsigned long flags;
-	__u8 wmem;
-	int ret;
-
-	if (motionp) {
-		if (wiimote_cmd_acquire(ext->wdata))
-			return;
-
-		if (ext_type == WIIEXT_CLASSIC)
-			wmem = 0x07;
-		else if (ext_type == WIIEXT_NUNCHUCK)
-			wmem = 0x05;
-		else
-			wmem = 0x04;
-
-		ret = wiimote_cmd_write(ext->wdata, 0xa600fe, &wmem, sizeof(wmem));
-		wiimote_cmd_release(ext->wdata);
-		if (ret)
-			return;
-	}
-
-	spin_lock_irqsave(&ext->wdata->state.lock, flags);
-	ext->motionp = motionp;
-	ext->ext_type = ext_type;
-	wiiproto_req_drm(ext->wdata, WIIPROTO_REQ_NULL);
-	spin_unlock_irqrestore(&ext->wdata->state.lock, flags);
-}
-
-static void wiiext_worker(struct work_struct *work)
-{
-	struct wiimote_ext *ext = container_of(work, struct wiimote_ext,
-									worker);
-	bool motionp;
-	__u8 ext_type;
-
-	ext_disable(ext);
-	motionp = motionp_read(ext);
-	ext_type = ext_read(ext);
-	ext_enable(ext, motionp, ext_type);
-}
-
-/* schedule work only once, otherwise mark for reschedule */
-static void wiiext_schedule(struct wiimote_ext *ext)
-{
-	schedule_work(&ext->worker);
-}
-
-/*
- * Reacts on extension port events
- * Whenever the driver gets an event from the wiimote that an extension has been
- * plugged or unplugged, this funtion shall be called. It checks what extensions
- * are connected and initializes and activates them.
- * This can be called in atomic context. The initialization is done in a
- * separate worker thread. The state.lock spinlock must be held by the caller.
- */
-void wiiext_event(struct wiimote_data *wdata, bool plugged)
-{
-	if (!wdata->ext)
-		return;
-
-	if (wdata->ext->plugged == plugged)
-		return;
-
-	wdata->ext->plugged = plugged;
-
-	if (!plugged)
-		wdata->ext->mp_plugged = false;
-
-	/*
-	 * We need to call wiiext_schedule(wdata->ext) here, however, the
-	 * extension initialization logic is not fully understood and so
-	 * automatic initialization is not supported, yet.
-	 */
-}
-
-/*
- * Returns true if the current DRM mode should contain extension data and false
- * if there is no interest in extension data.
- * All supported extensions send 6 byte extension data so any DRM that contains
- * extension bytes is fine.
- * The caller must hold the state.lock spinlock.
- */
-bool wiiext_active(struct wiimote_data *wdata)
-{
-	if (!wdata->ext)
-		return false;
-
-	return wdata->ext->motionp || wdata->ext->ext_type;
-}
-
-static void handler_motionp(struct wiimote_ext *ext, const __u8 *payload)
-{
-	__s32 x, y, z;
-	bool plugged;
-
-	/*        |   8    7    6    5    4    3 |  2  |  1  |
-	 *   -----+------------------------------+-----+-----+
-	 *    1   |               Yaw Speed <7:0>            |
-	 *    2   |              Roll Speed <7:0>            |
-	 *    3   |             Pitch Speed <7:0>            |
-	 *   -----+------------------------------+-----+-----+
-	 *    4   |       Yaw Speed <13:8>       | Yaw |Pitch|
-	 *   -----+------------------------------+-----+-----+
-	 *    5   |      Roll Speed <13:8>       |Roll | Ext |
-	 *   -----+------------------------------+-----+-----+
-	 *    6   |     Pitch Speed <13:8>       |  1  |  0  |
-	 *   -----+------------------------------+-----+-----+
-	 * The single bits Yaw, Roll, Pitch in the lower right corner specify
-	 * whether the wiimote is rotating fast (0) or slow (1). Speed for slow
-	 * roation is 440 deg/s and for fast rotation 2000 deg/s. To get a
-	 * linear scale we multiply by 2000/440 = ~4.5454 which is 18 for fast
-	 * and 9 for slow.
-	 * If the wiimote is not rotating the sensor reports 2^13 = 8192.
-	 * Ext specifies whether an extension is connected to the motionp.
-	 */
-
-	x = payload[0];
-	y = payload[1];
-	z = payload[2];
-
-	x |= (((__u16)payload[3]) << 6) & 0xff00;
-	y |= (((__u16)payload[4]) << 6) & 0xff00;
-	z |= (((__u16)payload[5]) << 6) & 0xff00;
-
-	x -= 8192;
-	y -= 8192;
-	z -= 8192;
-
-	if (!(payload[3] & 0x02))
-		x *= 18;
-	else
-		x *= 9;
-	if (!(payload[4] & 0x02))
-		y *= 18;
-	else
-		y *= 9;
-	if (!(payload[3] & 0x01))
-		z *= 18;
-	else
-		z *= 9;
-
-	input_report_abs(ext->mp_input, ABS_RX, x);
-	input_report_abs(ext->mp_input, ABS_RY, y);
-	input_report_abs(ext->mp_input, ABS_RZ, z);
-	input_sync(ext->mp_input);
-
-	plugged = payload[5] & 0x01;
-	if (plugged != ext->mp_plugged)
-		ext->mp_plugged = plugged;
-}
-
-static void handler_nunchuck(struct wiimote_ext *ext, const __u8 *payload)
-{
-	__s16 x, y, z, bx, by;
-
-	/*   Byte |   8    7 |  6    5 |  4    3 |  2 |  1  |
-	 *   -----+----------+---------+---------+----+-----+
-	 *    1   |              Button X <7:0>             |
-	 *    2   |              Button Y <7:0>             |
-	 *   -----+----------+---------+---------+----+-----+
-	 *    3   |               Speed X <9:2>             |
-	 *    4   |               Speed Y <9:2>             |
-	 *    5   |               Speed Z <9:2>             |
-	 *   -----+----------+---------+---------+----+-----+
-	 *    6   | Z <1:0>  | Y <1:0> | X <1:0> | BC | BZ  |
-	 *   -----+----------+---------+---------+----+-----+
-	 * Button X/Y is the analog stick. Speed X, Y and Z are the
-	 * accelerometer data in the same format as the wiimote's accelerometer.
-	 * The 6th byte contains the LSBs of the accelerometer data.
-	 * BC and BZ are the C and Z buttons: 0 means pressed
-	 *
-	 * If reported interleaved with motionp, then the layout changes. The
-	 * 5th and 6th byte changes to:
-	 *   -----+-----------------------------------+-----+
-	 *    5   |            Speed Z <9:3>          | EXT |
-	 *   -----+--------+-----+-----+----+----+----+-----+
-	 *    6   |Z <2:1> |Y <1>|X <1>| BC | BZ | 0  |  0  |
-	 *   -----+--------+-----+-----+----+----+----+-----+
-	 * All three accelerometer values lose their LSB. The other data is
-	 * still available but slightly moved.
-	 *
-	 * Center data for button values is 128. Center value for accelerometer
-	 * values it 512 / 0x200
-	 */
-
-	bx = payload[0];
-	by = payload[1];
-	bx -= 128;
-	by -= 128;
-
-	x = payload[2] << 2;
-	y = payload[3] << 2;
-	z = payload[4] << 2;
-
-	if (ext->motionp) {
-		x |= (payload[5] >> 3) & 0x02;
-		y |= (payload[5] >> 4) & 0x02;
-		z &= ~0x4;
-		z |= (payload[5] >> 5) & 0x06;
-	} else {
-		x |= (payload[5] >> 2) & 0x03;
-		y |= (payload[5] >> 4) & 0x03;
-		z |= (payload[5] >> 6) & 0x03;
-	}
-
-	x -= 0x200;
-	y -= 0x200;
-	z -= 0x200;
-
-	input_report_abs(ext->input, ABS_HAT0X, bx);
-	input_report_abs(ext->input, ABS_HAT0Y, by);
-
-	input_report_abs(ext->input, ABS_RX, x);
-	input_report_abs(ext->input, ABS_RY, y);
-	input_report_abs(ext->input, ABS_RZ, z);
-
-	if (ext->motionp) {
-		input_report_key(ext->input,
-			wiiext_keymap[WIIEXT_KEY_Z], !(payload[5] & 0x04));
-		input_report_key(ext->input,
-			wiiext_keymap[WIIEXT_KEY_C], !(payload[5] & 0x08));
-	} else {
-		input_report_key(ext->input,
-			wiiext_keymap[WIIEXT_KEY_Z], !(payload[5] & 0x01));
-		input_report_key(ext->input,
-			wiiext_keymap[WIIEXT_KEY_C], !(payload[5] & 0x02));
-	}
-
-	input_sync(ext->input);
-}
-
-static void handler_classic(struct wiimote_ext *ext, const __u8 *payload)
-{
-	__s8 rx, ry, lx, ly, lt, rt;
-
-	/*   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
-	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
-	 *    1   | RX <5:4>  |              LX <5:0>             |
-	 *    2   | RX <3:2>  |              LY <5:0>             |
-	 *   -----+-----+-----+-----+-----------------------------+
-	 *    3   |RX<1>| LT <5:4>  |         RY <5:1>            |
-	 *   -----+-----+-----------+-----------------------------+
-	 *    4   |     LT <3:1>    |         RT <5:1>            |
-	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
-	 *    5   | BDR | BDD | BLT | B-  | BH  | B+  | BRT |  1  |
-	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
-	 *    6   | BZL | BB  | BY  | BA  | BX  | BZR | BDL | BDU |
-	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
-	 * All buttons are 0 if pressed
-	 * RX and RY are right analog stick
-	 * LX and LY are left analog stick
-	 * LT is left trigger, RT is right trigger
-	 * BLT is 0 if left trigger is fully pressed
-	 * BRT is 0 if right trigger is fully pressed
-	 * BDR, BDD, BDL, BDU form the D-Pad with right, down, left, up buttons
-	 * BZL is left Z button and BZR is right Z button
-	 * B-, BH, B+ are +, HOME and - buttons
-	 * BB, BY, BA, BX are A, B, X, Y buttons
-	 * LSB of RX, RY, LT, and RT are not transmitted and always 0.
-	 *
-	 * With motionp enabled it changes slightly to this:
-	 *   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
-	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
-	 *    1   | RX <4:3>  |          LX <5:1>           | BDU |
-	 *    2   | RX <2:1>  |          LY <5:1>           | BDL |
-	 *   -----+-----+-----+-----+-----------------------+-----+
-	 *    3   |RX<0>| LT <4:3>  |         RY <4:0>            |
-	 *   -----+-----+-----------+-----------------------------+
-	 *    4   |     LT <2:0>    |         RT <4:0>            |
-	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
-	 *    5   | BDR | BDD | BLT | B-  | BH  | B+  | BRT | EXT |
-	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
-	 *    6   | BZL | BB  | BY  | BA  | BX  | BZR |  0  |  0  |
-	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
-	 * Only the LSBs of LX and LY are lost. BDU and BDL are moved, the rest
-	 * is the same as before.
-	 */
-
-	if (ext->motionp) {
-		lx = payload[0] & 0x3e;
-		ly = payload[0] & 0x3e;
-	} else {
-		lx = payload[0] & 0x3f;
-		ly = payload[0] & 0x3f;
-	}
-
-	rx = (payload[0] >> 3) & 0x14;
-	rx |= (payload[1] >> 5) & 0x06;
-	rx |= (payload[2] >> 7) & 0x01;
-	ry = payload[2] & 0x1f;
-
-	rt = payload[3] & 0x1f;
-	lt = (payload[2] >> 2) & 0x18;
-	lt |= (payload[3] >> 5) & 0x07;
-
-	rx <<= 1;
-	ry <<= 1;
-	rt <<= 1;
-	lt <<= 1;
-
-	input_report_abs(ext->input, ABS_HAT1X, lx - 0x20);
-	input_report_abs(ext->input, ABS_HAT1Y, ly - 0x20);
-	input_report_abs(ext->input, ABS_HAT2X, rx - 0x20);
-	input_report_abs(ext->input, ABS_HAT2Y, ry - 0x20);
-	input_report_abs(ext->input, ABS_HAT3X, rt - 0x20);
-	input_report_abs(ext->input, ABS_HAT3Y, lt - 0x20);
-
-	input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_RIGHT],
-							!!(payload[4] & 0x80));
-	input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_DOWN],
-							!!(payload[4] & 0x40));
-	input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LT],
-							!!(payload[4] & 0x20));
-	input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_MINUS],
-							!!(payload[4] & 0x10));
-	input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_HOME],
-							!!(payload[4] & 0x08));
-	input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_PLUS],
-							!!(payload[4] & 0x04));
-	input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_RT],
-							!!(payload[4] & 0x02));
-	input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_ZL],
-							!!(payload[5] & 0x80));
-	input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_B],
-							!!(payload[5] & 0x40));
-	input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_Y],
-							!!(payload[5] & 0x20));
-	input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_A],
-							!!(payload[5] & 0x10));
-	input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_X],
-							!!(payload[5] & 0x08));
-	input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_ZR],
-							!!(payload[5] & 0x04));
-
-	if (ext->motionp) {
-		input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_UP],
-							!!(payload[0] & 0x01));
-		input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LEFT],
-							!!(payload[1] & 0x01));
-	} else {
-		input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_UP],
-							!!(payload[5] & 0x01));
-		input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LEFT],
-							!!(payload[5] & 0x02));
-	}
-
-	input_sync(ext->input);
-}
-
-static void handler_balance_board(struct wiimote_ext *ext, const __u8 *payload)
-{
-	__s32 val[4], tmp;
-	unsigned int i;
-
-	/*   Byte |  8  7  6  5  4  3  2  1  |
-	 *   -----+--------------------------+
-	 *    1   |    Top Right <15:8>      |
-	 *    2   |    Top Right  <7:0>      |
-	 *   -----+--------------------------+
-	 *    3   | Bottom Right <15:8>      |
-	 *    4   | Bottom Right  <7:0>      |
-	 *   -----+--------------------------+
-	 *    5   |     Top Left <15:8>      |
-	 *    6   |     Top Left  <7:0>      |
-	 *   -----+--------------------------+
-	 *    7   |  Bottom Left <15:8>      |
-	 *    8   |  Bottom Left  <7:0>      |
-	 *   -----+--------------------------+
-	 *
-	 * These values represent the weight-measurements of the Wii-balance
-	 * board with 16bit precision.
-	 *
-	 * The balance-board is never reported interleaved with motionp.
-	 */
-
-	val[0] = payload[0];
-	val[0] <<= 8;
-	val[0] |= payload[1];
-
-	val[1] = payload[2];
-	val[1] <<= 8;
-	val[1] |= payload[3];
-
-	val[2] = payload[4];
-	val[2] <<= 8;
-	val[2] |= payload[5];
-
-	val[3] = payload[6];
-	val[3] <<= 8;
-	val[3] |= payload[7];
-
-	/* apply calibration data */
-	for (i = 0; i < 4; i++) {
-		if (val[i] < ext->calib[i][1]) {
-			tmp = val[i] - ext->calib[i][0];
-			tmp *= 1700;
-			tmp /= ext->calib[i][1] - ext->calib[i][0];
-		} else {
-			tmp = val[i] - ext->calib[i][1];
-			tmp *= 1700;
-			tmp /= ext->calib[i][2] - ext->calib[i][1];
-			tmp += 1700;
-		}
-		val[i] = tmp;
-	}
-
-	input_report_abs(ext->input, ABS_HAT0X, val[0]);
-	input_report_abs(ext->input, ABS_HAT0Y, val[1]);
-	input_report_abs(ext->input, ABS_HAT1X, val[2]);
-	input_report_abs(ext->input, ABS_HAT1Y, val[3]);
-
-	input_sync(ext->input);
-}
-
-/* call this with state.lock spinlock held */
-void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload)
-{
-	struct wiimote_ext *ext = wdata->ext;
-
-	if (!ext)
-		return;
-
-	if (ext->motionp && (payload[5] & 0x02)) {
-		handler_motionp(ext, payload);
-	} else if (ext->ext_type == WIIEXT_NUNCHUCK) {
-		handler_nunchuck(ext, payload);
-	} else if (ext->ext_type == WIIEXT_CLASSIC) {
-		handler_classic(ext, payload);
-	} else if (ext->ext_type == WIIEXT_BALANCE_BOARD) {
-		handler_balance_board(ext, payload);
-	}
-}
-
-static ssize_t wiiext_show(struct device *dev, struct device_attribute *attr,
-								char *buf)
-{
-	struct wiimote_data *wdata = dev_to_wii(dev);
-	__u8 type = WIIEXT_NONE;
-	bool motionp = false;
-	unsigned long flags;
-
-	spin_lock_irqsave(&wdata->state.lock, flags);
-	if (wdata->ext) {
-		motionp = wdata->ext->motionp;
-		type = wdata->ext->ext_type;
-	}
-	spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-	if (type == WIIEXT_NUNCHUCK) {
-		if (motionp)
-			return sprintf(buf, "motionp+nunchuck\n");
-		else
-			return sprintf(buf, "nunchuck\n");
-	} else if (type == WIIEXT_CLASSIC) {
-		if (motionp)
-			return sprintf(buf, "motionp+classic\n");
-		else
-			return sprintf(buf, "classic\n");
-	} else if (type == WIIEXT_BALANCE_BOARD) {
-		if (motionp)
-			return sprintf(buf, "motionp+balanceboard\n");
-		else
-			return sprintf(buf, "balanceboard\n");
-	} else {
-		if (motionp)
-			return sprintf(buf, "motionp\n");
-		else
-			return sprintf(buf, "none\n");
-	}
-}
-
-static DEVICE_ATTR(extension, S_IRUGO, wiiext_show, NULL);
-
-static int wiiext_input_open(struct input_dev *dev)
-{
-	struct wiimote_ext *ext = input_get_drvdata(dev);
-
-	atomic_inc(&ext->opened);
-	wiiext_schedule(ext);
-
-	return 0;
-}
-
-static void wiiext_input_close(struct input_dev *dev)
-{
-	struct wiimote_ext *ext = input_get_drvdata(dev);
-
-	atomic_dec(&ext->opened);
-	wiiext_schedule(ext);
-}
-
-static int wiiext_mp_open(struct input_dev *dev)
-{
-	struct wiimote_ext *ext = input_get_drvdata(dev);
-
-	atomic_inc(&ext->mp_opened);
-	wiiext_schedule(ext);
-
-	return 0;
-}
-
-static void wiiext_mp_close(struct input_dev *dev)
-{
-	struct wiimote_ext *ext = input_get_drvdata(dev);
-
-	atomic_dec(&ext->mp_opened);
-	wiiext_schedule(ext);
-}
-
-/* Initializes the extension driver of a wiimote */
-int wiiext_init(struct wiimote_data *wdata)
-{
-	struct wiimote_ext *ext;
-	unsigned long flags;
-	int ret, i;
-
-	ext = kzalloc(sizeof(*ext), GFP_KERNEL);
-	if (!ext)
-		return -ENOMEM;
-
-	ext->wdata = wdata;
-	INIT_WORK(&ext->worker, wiiext_worker);
-
-	ext->input = input_allocate_device();
-	if (!ext->input) {
-		ret = -ENOMEM;
-		goto err_input;
-	}
-
-	input_set_drvdata(ext->input, ext);
-	ext->input->open = wiiext_input_open;
-	ext->input->close = wiiext_input_close;
-	ext->input->dev.parent = &wdata->hdev->dev;
-	ext->input->id.bustype = wdata->hdev->bus;
-	ext->input->id.vendor = wdata->hdev->vendor;
-	ext->input->id.product = wdata->hdev->product;
-	ext->input->id.version = wdata->hdev->version;
-	ext->input->name = WIIMOTE_NAME " Extension";
-
-	set_bit(EV_KEY, ext->input->evbit);
-	for (i = 0; i < WIIEXT_KEY_COUNT; ++i)
-		set_bit(wiiext_keymap[i], ext->input->keybit);
-
-	set_bit(EV_ABS, ext->input->evbit);
-	set_bit(ABS_HAT0X, ext->input->absbit);
-	set_bit(ABS_HAT0Y, ext->input->absbit);
-	set_bit(ABS_HAT1X, ext->input->absbit);
-	set_bit(ABS_HAT1Y, ext->input->absbit);
-	set_bit(ABS_HAT2X, ext->input->absbit);
-	set_bit(ABS_HAT2Y, ext->input->absbit);
-	set_bit(ABS_HAT3X, ext->input->absbit);
-	set_bit(ABS_HAT3Y, ext->input->absbit);
-	input_set_abs_params(ext->input, ABS_HAT0X, -120, 120, 2, 4);
-	input_set_abs_params(ext->input, ABS_HAT0Y, -120, 120, 2, 4);
-	input_set_abs_params(ext->input, ABS_HAT1X, -30, 30, 1, 1);
-	input_set_abs_params(ext->input, ABS_HAT1Y, -30, 30, 1, 1);
-	input_set_abs_params(ext->input, ABS_HAT2X, -30, 30, 1, 1);
-	input_set_abs_params(ext->input, ABS_HAT2Y, -30, 30, 1, 1);
-	input_set_abs_params(ext->input, ABS_HAT3X, -30, 30, 1, 1);
-	input_set_abs_params(ext->input, ABS_HAT3Y, -30, 30, 1, 1);
-	set_bit(ABS_RX, ext->input->absbit);
-	set_bit(ABS_RY, ext->input->absbit);
-	set_bit(ABS_RZ, ext->input->absbit);
-	input_set_abs_params(ext->input, ABS_RX, -500, 500, 2, 4);
-	input_set_abs_params(ext->input, ABS_RY, -500, 500, 2, 4);
-	input_set_abs_params(ext->input, ABS_RZ, -500, 500, 2, 4);
-
-	ret = input_register_device(ext->input);
-	if (ret) {
-		input_free_device(ext->input);
-		goto err_input;
-	}
-
-	ext->mp_input = input_allocate_device();
-	if (!ext->mp_input) {
-		ret = -ENOMEM;
-		goto err_mp;
-	}
-
-	input_set_drvdata(ext->mp_input, ext);
-	ext->mp_input->open = wiiext_mp_open;
-	ext->mp_input->close = wiiext_mp_close;
-	ext->mp_input->dev.parent = &wdata->hdev->dev;
-	ext->mp_input->id.bustype = wdata->hdev->bus;
-	ext->mp_input->id.vendor = wdata->hdev->vendor;
-	ext->mp_input->id.product = wdata->hdev->product;
-	ext->mp_input->id.version = wdata->hdev->version;
-	ext->mp_input->name = WIIMOTE_NAME " Motion+";
-
-	set_bit(EV_ABS, ext->mp_input->evbit);
-	set_bit(ABS_RX, ext->mp_input->absbit);
-	set_bit(ABS_RY, ext->mp_input->absbit);
-	set_bit(ABS_RZ, ext->mp_input->absbit);
-	input_set_abs_params(ext->mp_input, ABS_RX, -160000, 160000, 4, 8);
-	input_set_abs_params(ext->mp_input, ABS_RY, -160000, 160000, 4, 8);
-	input_set_abs_params(ext->mp_input, ABS_RZ, -160000, 160000, 4, 8);
-
-	ret = input_register_device(ext->mp_input);
-	if (ret) {
-		input_free_device(ext->mp_input);
-		goto err_mp;
-	}
-
-	ret = device_create_file(&wdata->hdev->dev, &dev_attr_extension);
-	if (ret)
-		goto err_dev;
-
-	spin_lock_irqsave(&wdata->state.lock, flags);
-	wdata->ext = ext;
-	spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-	return 0;
-
-err_dev:
-	input_unregister_device(ext->mp_input);
-err_mp:
-	input_unregister_device(ext->input);
-err_input:
-	kfree(ext);
-	return ret;
-}
-
-/* Deinitializes the extension driver of a wiimote */
-void wiiext_deinit(struct wiimote_data *wdata)
-{
-	struct wiimote_ext *ext = wdata->ext;
-	unsigned long flags;
-
-	if (!ext)
-		return;
-
-	/*
-	 * We first unset wdata->ext to avoid further input from the wiimote
-	 * core. The worker thread does not access this pointer so it is not
-	 * affected by this.
-	 * We kill the worker after this so it does not get respawned during
-	 * deinitialization.
-	 */
-
-	spin_lock_irqsave(&wdata->state.lock, flags);
-	wdata->ext = NULL;
-	spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-	device_remove_file(&wdata->hdev->dev, &dev_attr_extension);
-	input_unregister_device(ext->mp_input);
-	input_unregister_device(ext->input);
-
-	cancel_work_sync(&ext->worker);
-	kfree(ext);
-}
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 9f857c1..d406a39 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -142,7 +142,6 @@ struct wiimote_data {
 	struct power_supply battery;
 	struct input_dev *mp;
 	struct timer_list timer;
-	struct wiimote_ext *ext;
 	struct wiimote_debug *debug;
 
 	union {
@@ -270,24 +269,6 @@ extern ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset,
 extern void wiiproto_req_rmem(struct wiimote_data *wdata, bool eeprom,
 						__u32 offset, __u16 size);
 
-#ifdef CONFIG_HID_WIIMOTE_EXT
-
-extern int wiiext_init(struct wiimote_data *wdata);
-extern void wiiext_deinit(struct wiimote_data *wdata);
-extern void wiiext_event(struct wiimote_data *wdata, bool plugged);
-extern bool wiiext_active(struct wiimote_data *wdata);
-extern void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload);
-
-#else
-
-static inline int wiiext_init(void *u) { return 0; }
-static inline void wiiext_deinit(void *u) { }
-static inline void wiiext_event(void *u, bool p) { }
-static inline bool wiiext_active(void *u) { return false; }
-static inline void wiiext_handle(void *u, const __u8 *p) { }
-
-#endif
-
 #ifdef CONFIG_DEBUG_FS
 
 extern int wiidebug_init(struct wiimote_data *wdata);
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 23/26] HID: wiimote: add MP quirks
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (21 preceding siblings ...)
  2013-05-05 21:13 ` [PATCH 22/26] HID: wiimote: remove old static extension support David Herrmann
@ 2013-05-05 21:13 ` David Herrmann
  2013-05-05 21:13 ` [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller David Herrmann
                   ` (3 subsequent siblings)
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:13 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

Devices which have built-in motion plus ports don't need MP detection
logic. The new WIIMOD_BUILTIN_MP modules sets the WIIPROTO_FLAG_BUILTIN_MP
flag which disables polling for MP.

Some other devices erroneously report that they support motion-plus. For
these devices and all devices without extension ports, we load
WIIMOD_NO_MP which sets WIIPROTO_FLAG_NO_MP. This effectively disables all
MP detection logic.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c    | 25 ++++++++++---
 drivers/hid/hid-wiimote-modules.c | 74 +++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-wiimote.h         |  4 +++
 3 files changed, 98 insertions(+), 5 deletions(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index fa58045..3e69656 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -555,6 +555,7 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_UNKNOWN] = (const __u8[]){
+		WIIMOD_NO_MP,
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_GENERIC] = (const __u8[]){
@@ -591,11 +592,13 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		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,
 	},
 };
@@ -867,8 +870,13 @@ static void wiimote_init_detect(struct wiimote_data *wdata)
 out_release:
 	wiimote_cmd_release(wdata);
 	wiimote_init_set_type(wdata, exttype);
+
 	/* schedule MP timer */
-	mod_timer(&wdata->timer, jiffies + HZ * 4);
+	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);
 }
 
 /*
@@ -1037,7 +1045,8 @@ out_release:
 	wiimote_cmd_release(wdata);
 
 	/* only poll for MP if requested and if state didn't change */
-	if (ret && poll_mp)
+	if (ret && poll_mp && !(flags & WIIPROTO_FLAG_BUILTIN_MP) &&
+	    !(flags & WIIPROTO_FLAG_NO_MP))
 		wiimote_init_poll_mp(wdata);
 
 	return ret;
@@ -1082,8 +1091,12 @@ static void wiimote_init_hotplug(struct wiimote_data *wdata)
 
 	/* init extension and MP (deactivates current extension or MP) */
 	wiimote_cmd_init_ext(wdata);
-	wiimote_cmd_init_mp(wdata);
-	mp = wiimote_cmd_read_mp(wdata, mpdata);
+	if (flags & WIIPROTO_FLAG_NO_MP) {
+		mp = false;
+	} else {
+		wiimote_cmd_init_mp(wdata);
+		mp = wiimote_cmd_read_mp(wdata, mpdata);
+	}
 	exttype = wiimote_cmd_read_ext(wdata, extdata);
 
 	wiimote_cmd_release(wdata);
@@ -1133,7 +1146,9 @@ static void wiimote_init_hotplug(struct wiimote_data *wdata)
 		del_timer_sync(&wdata->timer);
 	} else {
 		/* reschedule MP hotplug timer */
-		mod_timer(&wdata->timer, jiffies + HZ * 4);
+		if (!(flags & WIIPROTO_FLAG_BUILTIN_MP) &&
+		    !(flags & WIIPROTO_FLAG_NO_MP))
+			mod_timer(&wdata->timer, jiffies + HZ * 4);
 	}
 
 	spin_lock_irq(&wdata->state.lock);
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index 19b8b1c..e2afe06 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -1540,6 +1540,78 @@ static const struct wiimod_ops wiimod_bboard = {
 };
 
 /*
+ * Builtin Motion Plus
+ * This module simply sets the WIIPROTO_FLAG_BUILTIN_MP protocol flag which
+ * disables polling for Motion-Plus. This should be set only for devices which
+ * don't allow MP hotplugging.
+ */
+
+static int wiimod_builtin_mp_probe(const struct wiimod_ops *ops,
+				   struct wiimote_data *wdata)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags |= WIIPROTO_FLAG_BUILTIN_MP;
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	return 0;
+}
+
+static void wiimod_builtin_mp_remove(const struct wiimod_ops *ops,
+				     struct wiimote_data *wdata)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags |= WIIPROTO_FLAG_BUILTIN_MP;
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+static const struct wiimod_ops wiimod_builtin_mp = {
+	.flags = 0,
+	.arg = 0,
+	.probe = wiimod_builtin_mp_probe,
+	.remove = wiimod_builtin_mp_remove,
+};
+
+/*
+ * No Motion Plus
+ * This module simply sets the WIIPROTO_FLAG_NO_MP protocol flag which
+ * disables motion-plus. This is needed for devices that advertise this but we
+ * don't know how to use it (or whether it is actually present).
+ */
+
+static int wiimod_no_mp_probe(const struct wiimod_ops *ops,
+			      struct wiimote_data *wdata)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags |= WIIPROTO_FLAG_NO_MP;
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	return 0;
+}
+
+static void wiimod_no_mp_remove(const struct wiimod_ops *ops,
+				struct wiimote_data *wdata)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags |= WIIPROTO_FLAG_NO_MP;
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+static const struct wiimod_ops wiimod_no_mp = {
+	.flags = 0,
+	.arg = 0,
+	.probe = wiimod_no_mp_probe,
+	.remove = wiimod_no_mp_remove,
+};
+
+/*
  * Motion Plus
  * The Motion Plus extension provides rotation sensors (gyro) as a small
  * extension device for Wii Remotes. Many devices have them built-in so
@@ -1706,6 +1778,8 @@ const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
 	[WIIMOD_LED4] = &wiimod_leds[3],
 	[WIIMOD_ACCEL] = &wiimod_accel,
 	[WIIMOD_IR] = &wiimod_ir,
+	[WIIMOD_BUILTIN_MP] = &wiimod_builtin_mp,
+	[WIIMOD_NO_MP] = &wiimod_no_mp,
 };
 
 const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = {
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index d406a39..5cf8bcb 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -44,6 +44,8 @@
 #define WIIPROTO_FLAG_MP_ACTIVE		0x2000
 #define WIIPROTO_FLAG_EXITING		0x4000
 #define WIIPROTO_FLAG_DRM_LOCKED	0x8000
+#define WIIPROTO_FLAG_BUILTIN_MP	0x010000
+#define WIIPROTO_FLAG_NO_MP		0x020000
 
 #define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
 					WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
@@ -165,6 +167,8 @@ enum wiimod_module {
 	WIIMOD_LED4,
 	WIIMOD_ACCEL,
 	WIIMOD_IR,
+	WIIMOD_BUILTIN_MP,
+	WIIMOD_NO_MP,
 	WIIMOD_NUM,
 	WIIMOD_NULL = WIIMOD_NUM,
 };
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (22 preceding siblings ...)
  2013-05-05 21:13 ` [PATCH 23/26] HID: wiimote: add MP quirks David Herrmann
@ 2013-05-05 21:13 ` David Herrmann
  2013-05-06  0:43   ` Todd Showalter
  2013-05-05 21:13 ` [PATCH 25/26] HID: wiimote: fix DRM debug-attr to correctly parse input David Herrmann
                   ` (2 subsequent siblings)
  26 siblings, 1 reply; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:13 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

The Wii U Pro Controller is a new Nintendo remote device that looks very
similar to the XBox controller. It has nearly the same features and uses
the same protocol as the Wii Remote.

We add a new wiimote extension device so the Pro Controller is properly
detected and supported.

The device reports MP support, which is odd and I couldn't get it working,
yet. Hence, we disable MP registers for now. Further investigation is
needed to see what extra capabilities are provided.

There are some other unknown bits in the extension reports that I couldn't
figure out what they do. You can use hidraw to access these if you're
interested.

We might want to hook up the "charging" and "USB" bits to the battery
device so user-space can query whether it is currently charged via USB.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c    |  23 +++
 drivers/hid/hid-wiimote-modules.c | 295 ++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-wiimote.h         |   2 +
 3 files changed, 320 insertions(+)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 3e69656..279a07d 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -452,6 +452,8 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem)
 		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;
 }
@@ -601,6 +603,15 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		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,
@@ -785,6 +796,7 @@ static const char *wiimote_devtype_names[WIIMOTE_DEV_NUM] = {
 	[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",
 };
 
 /* Try to guess the device type based on all collected information. We
@@ -805,6 +817,9 @@ static void wiimote_init_set_type(struct wiimote_data *wdata,
 	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;
 	}
 
 	if (!strcmp(name, "Nintendo RVL-CNT-01")) {
@@ -816,6 +831,9 @@ static void wiimote_init_set_type(struct wiimote_data *wdata,
 	} 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) {
@@ -1058,6 +1076,7 @@ static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = {
 	[WIIMOTE_EXT_NUNCHUK] = "Nintendo Wii Nunchuk",
 	[WIIMOTE_EXT_CLASSIC_CONTROLLER] = "Nintendo Wii Classic Controller",
 	[WIIMOTE_EXT_BALANCE_BOARD] = "Nintendo Wii Balance Board",
+	[WIIMOTE_EXT_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller",
 };
 
 /*
@@ -1634,6 +1653,8 @@ static ssize_t wiimote_ext_show(struct device *dev,
 		return sprintf(buf, "classic\n");
 	case WIIMOTE_EXT_BALANCE_BOARD:
 		return sprintf(buf, "balanceboard\n");
+	case WIIMOTE_EXT_PRO_CONTROLLER:
+		return sprintf(buf, "procontroller\n");
 	case WIIMOTE_EXT_UNKNOWN:
 		/* fallthrough */
 	default:
@@ -1680,6 +1701,8 @@ static ssize_t wiimote_dev_show(struct device *dev,
 		return sprintf(buf, "gen20\n");
 	case WIIMOTE_DEV_BALANCE_BOARD:
 		return sprintf(buf, "balanceboard\n");
+	case WIIMOTE_DEV_PRO_CONTROLLER:
+		return sprintf(buf, "procontroller\n");
 	case WIIMOTE_DEV_PENDING:
 		return sprintf(buf, "pending\n");
 	case WIIMOTE_DEV_UNKNOWN:
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index e2afe06..ab25a64 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -1540,6 +1540,300 @@ static const struct wiimod_ops wiimod_bboard = {
 };
 
 /*
+ * Pro Controller
+ * Released with the Wii U was the Nintendo Wii U Pro Controller. It does not
+ * work together with the classic Wii, but only with the new Wii U. However, it
+ * uses the same protocol and provides a builtin "classic controller pro"
+ * extension, few standard buttons, a rumble motor, 4 LEDs and a battery.
+ * We provide all these via a standard extension device as the device doesn't
+ * feature an extension port.
+ */
+
+enum wiimod_pro_keys {
+	WIIMOD_PRO_KEY_A,
+	WIIMOD_PRO_KEY_B,
+	WIIMOD_PRO_KEY_X,
+	WIIMOD_PRO_KEY_Y,
+	WIIMOD_PRO_KEY_PLUS,
+	WIIMOD_PRO_KEY_MINUS,
+	WIIMOD_PRO_KEY_HOME,
+	WIIMOD_PRO_KEY_LEFT,
+	WIIMOD_PRO_KEY_RIGHT,
+	WIIMOD_PRO_KEY_UP,
+	WIIMOD_PRO_KEY_DOWN,
+	WIIMOD_PRO_KEY_TL,
+	WIIMOD_PRO_KEY_TR,
+	WIIMOD_PRO_KEY_ZL,
+	WIIMOD_PRO_KEY_ZR,
+	WIIMOD_PRO_KEY_THUMBL,
+	WIIMOD_PRO_KEY_THUMBR,
+	WIIMOD_PRO_KEY_NUM,
+};
+
+static const __u16 wiimod_pro_map[] = {
+	BTN_A,		/* WIIMOD_PRO_KEY_A */
+	BTN_B,		/* WIIMOD_PRO_KEY_B */
+	BTN_X,		/* WIIMOD_PRO_KEY_X */
+	BTN_Y,		/* WIIMOD_PRO_KEY_Y */
+	BTN_START,	/* WIIMOD_PRO_KEY_PLUS */
+	BTN_SELECT,	/* WIIMOD_PRO_KEY_MINUS */
+	BTN_MODE,	/* WIIMOD_PRO_KEY_HOME */
+	KEY_LEFT,	/* WIIMOD_PRO_KEY_LEFT */
+	KEY_RIGHT,	/* WIIMOD_PRO_KEY_RIGHT */
+	KEY_UP,		/* WIIMOD_PRO_KEY_UP */
+	KEY_DOWN,	/* WIIMOD_PRO_KEY_DOWN */
+	BTN_TL,		/* WIIMOD_PRO_KEY_TL */
+	BTN_TR,		/* WIIMOD_PRO_KEY_TR */
+	BTN_TL2,	/* WIIMOD_PRO_KEY_ZL */
+	BTN_TR2,	/* WIIMOD_PRO_KEY_ZR */
+	BTN_THUMBL,	/* WIIMOD_PRO_KEY_THUMBL */
+	BTN_THUMBR,	/* WIIMOD_PRO_KEY_THUMBR */
+};
+
+static void wiimod_pro_in_ext(struct wiimote_data *wdata, const __u8 *ext)
+{
+	__s16 rx, ry, lx, ly;
+
+	/*   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
+	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+	 *    1   |                   LX <7:0>                    |
+	 *   -----+-----------------------+-----------------------+
+	 *    2   |  0     0     0     0  |       LX <11:8>       |
+	 *   -----+-----------------------+-----------------------+
+	 *    3   |                   RX <7:0>                    |
+	 *   -----+-----------------------+-----------------------+
+	 *    4   |  0     0     0     0  |       RX <11:8>       |
+	 *   -----+-----------------------+-----------------------+
+	 *    5   |                   LY <7:0>                    |
+	 *   -----+-----------------------+-----------------------+
+	 *    6   |  0     0     0     0  |       LY <11:8>       |
+	 *   -----+-----------------------+-----------------------+
+	 *    7   |                   RY <7:0>                    |
+	 *   -----+-----------------------+-----------------------+
+	 *    8   |  0     0     0     0  |       RY <11:8>       |
+	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+	 *    9   | BDR | BDD | BLT | B-  | BH  | B+  | BRT |  1  |
+	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+	 *   10   | BZL | BB  | BY  | BA  | BX  | BZR | BDL | BDU |
+	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+	 *   11   |  1  |     BATTERY     | USB |CHARG|LTHUM|RTHUM|
+	 *   -----+-----+-----------------+-----------+-----+-----+
+	 * All buttons are low-active (0 if pressed)
+	 * RX and RY are right analog stick
+	 * LX and LY are left analog stick
+	 * BLT is left trigger, BRT is right trigger.
+	 * BDR, BDD, BDL, BDU form the D-Pad with right, down, left, up buttons
+	 * BZL is left Z button and BZR is right Z button
+	 * B-, BH, B+ are +, HOME and - buttons
+	 * BB, BY, BA, BX are A, B, X, Y buttons
+	 *
+	 * Bits marked as 0/1 are unknown and never changed during tests.
+	 *
+	 * Not entirely verified:
+	 *   CHARG: 1 if uncharging, 0 if charging
+	 *   USB: 1 if not connected, 0 if connected via USB
+	 *   BATTERY: battery capacity from 000 (empty) to 100 (full)
+	 */
+
+	lx = (ext[0] & 0xff) | ((ext[1] & 0x0f) << 8);
+	rx = (ext[2] & 0xff) | ((ext[3] & 0x0f) << 8);
+	ly = (ext[4] & 0xff) | ((ext[5] & 0x0f) << 8);
+	ry = (ext[6] & 0xff) | ((ext[7] & 0x0f) << 8);
+
+	input_report_abs(wdata->extension.input, ABS_HAT0X, lx - 0x800);
+	input_report_abs(wdata->extension.input, ABS_HAT0Y, ly - 0x800);
+	input_report_abs(wdata->extension.input, ABS_HAT1X, rx - 0x800);
+	input_report_abs(wdata->extension.input, ABS_HAT1Y, ry - 0x800);
+
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_RIGHT],
+			 !(ext[8] & 0x80));
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_DOWN],
+			 !(ext[8] & 0x40));
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_TL],
+			 !(ext[8] & 0x20));
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_MINUS],
+			 !(ext[8] & 0x10));
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_HOME],
+			 !(ext[8] & 0x08));
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_PLUS],
+			 !(ext[8] & 0x04));
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_TR],
+			 !(ext[8] & 0x02));
+
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_ZL],
+			 !(ext[9] & 0x80));
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_B],
+			 !(ext[9] & 0x40));
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_Y],
+			 !(ext[9] & 0x20));
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_A],
+			 !(ext[9] & 0x10));
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_X],
+			 !(ext[9] & 0x08));
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_ZR],
+			 !(ext[9] & 0x04));
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_LEFT],
+			 !(ext[9] & 0x02));
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_UP],
+			 !(ext[9] & 0x01));
+
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_THUMBL],
+			 !(ext[10] & 0x02));
+	input_report_key(wdata->extension.input,
+			 wiimod_pro_map[WIIMOD_PRO_KEY_THUMBR],
+			 !(ext[10] & 0x01));
+
+	input_sync(wdata->extension.input);
+}
+
+static int wiimod_pro_open(struct input_dev *dev)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags |= WIIPROTO_FLAG_EXT_USED;
+	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	return 0;
+}
+
+static void wiimod_pro_close(struct input_dev *dev)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
+	wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+static int wiimod_pro_play(struct input_dev *dev, void *data,
+			   struct ff_effect *eff)
+{
+	struct wiimote_data *wdata = input_get_drvdata(dev);
+	__u8 value;
+	unsigned long flags;
+
+	/*
+	 * 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 (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude)
+		value = 1;
+	else
+		value = 0;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wiiproto_req_rumble(wdata, value);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	return 0;
+}
+
+static int wiimod_pro_probe(const struct wiimod_ops *ops,
+			    struct wiimote_data *wdata)
+{
+	int ret, i;
+
+	wdata->extension.input = input_allocate_device();
+	if (!wdata->extension.input)
+		return -ENOMEM;
+
+	set_bit(FF_RUMBLE, wdata->extension.input->ffbit);
+	input_set_drvdata(wdata->extension.input, wdata);
+
+	if (input_ff_create_memless(wdata->extension.input, NULL,
+				    wiimod_pro_play)) {
+		ret = -ENOMEM;
+		goto err_free;
+	}
+
+	wdata->extension.input->open = wiimod_pro_open;
+	wdata->extension.input->close = wiimod_pro_close;
+	wdata->extension.input->dev.parent = &wdata->hdev->dev;
+	wdata->extension.input->id.bustype = wdata->hdev->bus;
+	wdata->extension.input->id.vendor = wdata->hdev->vendor;
+	wdata->extension.input->id.product = wdata->hdev->product;
+	wdata->extension.input->id.version = wdata->hdev->version;
+	wdata->extension.input->name = WIIMOTE_NAME " Pro Controller";
+
+	set_bit(EV_KEY, wdata->extension.input->evbit);
+	for (i = 0; i < WIIMOD_PRO_KEY_NUM; ++i)
+		set_bit(wiimod_pro_map[i],
+			wdata->extension.input->keybit);
+
+	set_bit(EV_ABS, wdata->extension.input->evbit);
+	set_bit(ABS_HAT0X, wdata->extension.input->absbit);
+	set_bit(ABS_HAT0Y, wdata->extension.input->absbit);
+	set_bit(ABS_HAT1X, wdata->extension.input->absbit);
+	set_bit(ABS_HAT1Y, wdata->extension.input->absbit);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT0X, -0x800, 0x800, 2, 4);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT0Y, -0x800, 0x800, 2, 4);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT1X, -0x800, 0x800, 2, 4);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT1Y, -0x800, 0x800, 2, 4);
+
+	ret = input_register_device(wdata->extension.input);
+	if (ret)
+		goto err_free;
+
+	return 0;
+
+err_free:
+	input_free_device(wdata->extension.input);
+	wdata->extension.input = NULL;
+	return ret;
+}
+
+static void wiimod_pro_remove(const struct wiimod_ops *ops,
+			      struct wiimote_data *wdata)
+{
+	unsigned long flags;
+
+	if (!wdata->extension.input)
+		return;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	wiiproto_req_rumble(wdata, 0);
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	input_unregister_device(wdata->extension.input);
+	wdata->extension.input = NULL;
+}
+
+static const struct wiimod_ops wiimod_pro = {
+	.flags = WIIMOD_FLAG_EXT16,
+	.arg = 0,
+	.probe = wiimod_pro_probe,
+	.remove = wiimod_pro_remove,
+	.in_ext = wiimod_pro_in_ext,
+};
+
+/*
  * Builtin Motion Plus
  * This module simply sets the WIIPROTO_FLAG_BUILTIN_MP protocol flag which
  * disables polling for Motion-Plus. This should be set only for devices which
@@ -1788,4 +2082,5 @@ const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = {
 	[WIIMOTE_EXT_NUNCHUK] = &wiimod_nunchuk,
 	[WIIMOTE_EXT_CLASSIC_CONTROLLER] = &wiimod_classic,
 	[WIIMOTE_EXT_BALANCE_BOARD] = &wiimod_bboard,
+	[WIIMOTE_EXT_PRO_CONTROLLER] = &wiimod_pro,
 };
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 5cf8bcb..f1474f3 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -77,6 +77,7 @@ enum wiimote_devtype {
 	WIIMOTE_DEV_GEN10,
 	WIIMOTE_DEV_GEN20,
 	WIIMOTE_DEV_BALANCE_BOARD,
+	WIIMOTE_DEV_PRO_CONTROLLER,
 	WIIMOTE_DEV_NUM,
 };
 
@@ -86,6 +87,7 @@ enum wiimote_exttype {
 	WIIMOTE_EXT_NUNCHUK,
 	WIIMOTE_EXT_CLASSIC_CONTROLLER,
 	WIIMOTE_EXT_BALANCE_BOARD,
+	WIIMOTE_EXT_PRO_CONTROLLER,
 	WIIMOTE_EXT_NUM,
 };
 
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 25/26] HID: wiimote: fix DRM debug-attr to correctly parse input
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (23 preceding siblings ...)
  2013-05-05 21:13 ` [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller David Herrmann
@ 2013-05-05 21:13 ` David Herrmann
  2013-05-05 21:13 ` [RFC 26/26] HID/ALSA: wiimote: add speaker support David Herrmann
  2013-05-26 20:55 ` [PATCH 27/26] HID: wiimote: init EXT/MP during device detection David Herrmann
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:13 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

We need to correctly zero-terminate the input to parse it. Otherwise, we
always end up interpreting it as numbers.
Furthermore, we actually want hexadecimal numbers instead of decimal. As
it is a debugfs interface, we can change the API at any time.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-debug.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-wiimote-debug.c b/drivers/hid/hid-wiimote-debug.c
index 6e76a2c..c13fb5b 100644
--- a/drivers/hid/hid-wiimote-debug.c
+++ b/drivers/hid/hid-wiimote-debug.c
@@ -141,7 +141,7 @@ static ssize_t wiidebug_drm_write(struct file *f, const char __user *u,
 	if (copy_from_user(buf, u, len))
 		return -EFAULT;
 
-	buf[15] = 0;
+	buf[len] = 0;
 
 	for (i = 0; i < WIIPROTO_REQ_MAX; ++i) {
 		if (!wiidebug_drmmap[i])
@@ -151,7 +151,7 @@ static ssize_t wiidebug_drm_write(struct file *f, const char __user *u,
 	}
 
 	if (i == WIIPROTO_REQ_MAX)
-		i = simple_strtoul(buf, NULL, 10);
+		i = simple_strtoul(buf, NULL, 16);
 
 	spin_lock_irqsave(&dbg->wdata->state.lock, flags);
 	dbg->wdata->state.flags &= ~WIIPROTO_FLAG_DRM_LOCKED;
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [RFC 26/26] HID/ALSA: wiimote: add speaker support
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (24 preceding siblings ...)
  2013-05-05 21:13 ` [PATCH 25/26] HID: wiimote: fix DRM debug-attr to correctly parse input David Herrmann
@ 2013-05-05 21:13 ` David Herrmann
  2013-05-26 20:55 ` [PATCH 27/26] HID: wiimote: init EXT/MP during device detection David Herrmann
  26 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-05 21:13 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann, alsa-devel, Takashi Iwai

Classic Wii Remotes provide a speaker on the device. We can stream PCM
data, mute and change volume via special protocol requests and remote
audio registers.
The device supports several different formats, but fully understood are
only signed 8-bit PCM and something that looks like 4-bit Yamaha ADPCM.
Theoretically, we can set data-rates from 183Hz up to 12MHz, but a
realistic range for Bluetooth l2cap is 1000-4000 Hz.

Data is streamed as 20bytes blocks and must be sent in a constant rate.
There is no way to read the current position. It would be way too slow,
anyway. However, if we stream data too fast, older devices will report
overrun errors. It is nearly impossible to avoid them as we have no
fine-grained control over bluetooth radio. But that's probably also the
reason why newer devices no longer send these errors.

Note that 8bit PCM at such low rates has quite bad sound quality. You
also need to use user-space programs like "sox/play" as other
applications require rates >4000/8000Hz.

The 4bit Yamaha ADPCM should improve sound-quality a lot, however, the
kernel currently doesn't support this. So we either need to add ALSA
support or add an internal encoder. But this will be implemented in
follow-up patches so we first get the infrastructure ready.

Thanks to David Henningsson for very helpful advice on the driver layout.
The driver is also very loosely based on the sgio2audio driver by
Vivien Chappelier <vivien.chappelier@linux-mips.org> and the dummy
alsa sound driver.

Cc: alsa-devel@alsa-project.org
Cc: Takashi Iwai <tiwai@suse.de>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
Hi

This is my second revision of the Wii Remote sound-driver. It is based on the
hotplugging rework in this series. It's the only patch that is marked as RFC
but I thought I'd just include it in this series for completenes.

If someone wants to give it a try, I used the "sox" tools for testing as most
other tools require rates >4000/8000Hz.
  Sample created via:
    sox input.wav -b 8 -e signed -c 1 -r 2000 output.raw
  Play via: (prefix with: SOX_OPTS="--buffer 128" for better responsiveness)
    AUDIODEV=hw:1 play -e signed -b 8 -c 1 -r 2000 output.raw

Comments welcome! I tested this with 4 different devices and all worked well.
I also tested hotplugging while playing audio and I never got any deadlocks or
oopses. It's the first time I worked with the sound-layer, but I'm now pretty
sure I got everything right. Maybe I could even relax the heavy-locking but
better be safe.

Cheers
David

 drivers/hid/Kconfig               |   4 +
 drivers/hid/Makefile              |   3 +
 drivers/hid/hid-wiimote-core.c    |  66 +++-
 drivers/hid/hid-wiimote-modules.c |   5 +
 drivers/hid/hid-wiimote-speaker.c | 662 ++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-wiimote.h         |  17 +
 6 files changed, 755 insertions(+), 2 deletions(-)
 create mode 100644 drivers/hid/hid-wiimote-speaker.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index e8ef86c..3bc81c8 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -699,6 +699,7 @@ config HID_WIIMOTE
 	tristate "Nintendo Wii / Wii U peripherals"
 	depends on HID
 	depends on LEDS_CLASS
+	depends on !SND || HIGH_RES_TIMERS
 	select POWER_SUPPLY
 	select INPUT_FF_MEMLESS
 	---help---
@@ -715,6 +716,9 @@ config HID_WIIMOTE
 	the Wii U Gamepad) might be supported in the future. But currently
 	support is limited to Bluetooth based devices.
 
+	If Alsa sound support (CONFIG_SND) is enabled, this driver also provides
+	a sound card interface for Wii Remote speakers.
+
 	If unsure, say N.
 
 	To compile this driver as a module, choose M here: the
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 8b7106b..d149e10 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -32,6 +32,9 @@ hid-wiimote-y		:= hid-wiimote-core.o hid-wiimote-modules.o
 ifdef CONFIG_DEBUG_FS
 	hid-wiimote-y	+= hid-wiimote-debug.o
 endif
+ifdef CONFIG_SND
+	hid-wiimote-y	+= hid-wiimote-speaker.o
+endif
 
 obj-$(CONFIG_HID_A4TECH)	+= hid-a4tech.o
 obj-$(CONFIG_HID_ACRUX)		+= hid-axff.o
diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 279a07d..ea07fc3 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -309,8 +309,8 @@ void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags)
 #define wiiproto_req_weeprom(wdata, os, buf, sz) \
 			wiiproto_req_wmem((wdata), true, (os), (buf), (sz))
 
-static void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom,
-				__u32 offset, const __u8 *buf, __u8 size)
+void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom,
+		       __u32 offset, const __u8 *buf, __u8 size)
 {
 	__u8 cmd[22];
 
@@ -359,6 +359,58 @@ void wiiproto_req_rmem(struct wiimote_data *wdata, bool eeprom, __u32 offset,
 	wiimote_queue(wdata, cmd, sizeof(cmd));
 }
 
+void wiiproto_req_speaker(struct wiimote_data *wdata, bool on)
+{
+	__u8 cmd[2];
+
+	cmd[0] = WIIPROTO_REQ_SPEAKER;
+	cmd[1] = on ? 0x04 : 0x00;
+
+	wiiproto_keep_rumble(wdata, &cmd[1]);
+	wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+void wiiproto_req_mute(struct wiimote_data *wdata, bool on)
+{
+	__u8 cmd[2];
+
+	cmd[0] = WIIPROTO_REQ_MUTE;
+	cmd[1] = on ? 0x04 : 0x00;
+
+	wiiproto_keep_rumble(wdata, &cmd[1]);
+	wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+/* send audio data, possibly fragmented into two blocks */
+size_t wiiproto_req_audio(struct wiimote_data *wdata, const __u8 *b1,
+			  size_t len1, const __u8 *b2, size_t len2)
+{
+	__u8 cmd[22];
+	size_t pos, len;
+
+	pos = 0;
+	len = min_t(size_t, len1, 20ULL);
+	memcpy(&cmd[2 + pos], b1, len);
+	pos += len;
+
+	if (pos < 20 && len2) {
+		len = min_t(size_t, len2, 20ULL - pos);
+		memcpy(&cmd[2 + pos], b2, len);
+		pos += len;
+	}
+
+	cmd[0] = WIIPROTO_REQ_AUDIO;
+	cmd[1] = (pos & 0xff) << 3;
+
+	for ( ; pos < 20; ++pos)
+		cmd[pos] = 0;
+
+	wiiproto_keep_rumble(wdata, &cmd[1]);
+	wiimote_queue(wdata, cmd, sizeof(cmd));
+
+	return pos;
+}
+
 /* requries the cmd-mutex to be held */
 int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
 						const __u8 *wmem, __u8 size)
@@ -570,6 +622,7 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		WIIMOD_LED4,
 		WIIMOD_ACCEL,
 		WIIMOD_IR,
+		WIIMOD_SPEAKER,
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_GEN10] = (const __u8[]){
@@ -582,6 +635,7 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		WIIMOD_LED4,
 		WIIMOD_ACCEL,
 		WIIMOD_IR,
+		WIIMOD_SPEAKER,
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_GEN20] = (const __u8[]){
@@ -595,6 +649,7 @@ static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
 		WIIMOD_ACCEL,
 		WIIMOD_IR,
 		WIIMOD_BUILTIN_MP,
+		WIIMOD_SPEAKER,
 		WIIMOD_NULL,
 	},
 	[WIIMOTE_DEV_BALANCE_BOARD] = (const __u8[]) {
@@ -1469,6 +1524,13 @@ static void handler_return(struct wiimote_data *wdata, const __u8 *payload)
 	if (wiimote_cmd_pending(wdata, cmd, 0)) {
 		wdata->state.cmd_err = err;
 		wiimote_cmd_complete(wdata);
+	} else if (cmd == WIIPROTO_REQ_AUDIO && err == 4) {
+		/* If we stream audio data too fast, we get overrun errors
+		 * from the device. However, on audio-period bounds,
+		 * we have a very hard time sending audio at the exact rate
+		 * as we have no control over the bluetooth l2cap IRQs.
+		 * Hence, drop any overrun errors. */
+		hid_dbg(wdata->hdev, "audio overrun error\n");
 	} else if (err) {
 		hid_warn(wdata->hdev, "Remote error %hhu on req %hhu\n", err,
 									cmd);
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index ab25a64..25483aa 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -2074,6 +2074,11 @@ const struct wiimod_ops *wiimod_table[WIIMOD_NUM] = {
 	[WIIMOD_IR] = &wiimod_ir,
 	[WIIMOD_BUILTIN_MP] = &wiimod_builtin_mp,
 	[WIIMOD_NO_MP] = &wiimod_no_mp,
+#ifdef CONFIG_SND
+	[WIIMOD_SPEAKER] = &wiimod_speaker,
+#else
+	[WIIMOD_SPEAKER] = &wiimod_dummy,
+#endif
 };
 
 const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = {
diff --git a/drivers/hid/hid-wiimote-speaker.c b/drivers/hid/hid-wiimote-speaker.c
new file mode 100644
index 0000000..89b7f9f
--- /dev/null
+++ b/drivers/hid/hid-wiimote-speaker.c
@@ -0,0 +1,662 @@
+/*
+ * Driver for audio speakers of Nintendo Wii / Wii U peripherals
+ * Copyright (c) 2012-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.
+ */
+
+/*
+ * Audio Speakers
+ * Some Wii peripherals provide an audio speaker that supports 8bit PCM, 4bit
+ * Yamaha ADPCM and some other mostly unknown formats. Not all setup options
+ * are known, but we know how to setup an 8bit PCM or 4bit ADPCM stream and
+ * adjust volume. Data is sent as 20bytes chunks and needs to be streamed at
+ * a constant rate.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "hid-wiimote.h"
+
+struct wiimote_speaker {
+	spinlock_t lock;
+	struct snd_card *card;
+	struct wiimote_data *wdata;
+	unsigned int online : 1;
+	unsigned int mute : 1;
+	__u8 volume;
+
+	unsigned long pos;
+	struct snd_pcm_substream *subs;
+	struct mutex runlock;
+
+	struct hrtimer timer;
+	struct tasklet_struct tasklet;
+};
+
+enum wiimod_speaker_mode {
+	WIIMOD_SPEAKER_MODE_PCM8,
+	/* TODO: 4bit Yamaha ADPCM is currently not implemented */
+	WIIMOD_SPEAKER_MODE_ADPCM4,
+};
+
+static int wiimod_speaker_enable(struct wiimote_speaker *speaker)
+{
+	struct wiimote_data *wdata = speaker->wdata;
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+
+	if (!speaker->online) {
+		speaker->online = 1;
+		wiiproto_req_speaker(wdata, true);
+		wiiproto_req_mute(wdata, speaker->mute);
+	}
+
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	return 0;
+}
+
+static void wiimod_speaker_disable(struct wiimote_speaker *speaker)
+{
+	struct wiimote_data *wdata = speaker->wdata;
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+
+	if (speaker->online) {
+		speaker->online = 0;
+		wiiproto_req_speaker(wdata, false);
+	}
+
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+static void wiimod_speaker_set_mute(struct wiimote_speaker *speaker, bool mute)
+{
+	struct wiimote_data *wdata = speaker->wdata;
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+
+	if (speaker->mute != mute) {
+		speaker->mute = mute;
+		if (speaker->online)
+			wiiproto_req_mute(wdata, mute);
+	}
+
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+static void wiimod_speaker_set_volume(struct wiimote_speaker *speaker,
+				      __u8 volume)
+{
+	struct wiimote_data *wdata = speaker->wdata;
+	unsigned long flags;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+
+	if (speaker->volume != volume) {
+		speaker->volume = volume;
+		if (speaker->online)
+			wiiproto_req_wmem(wdata, false, 0xa20005, &volume,
+					  sizeof(volume));
+	}
+
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+}
+
+/* Change speaker configuration. \mode can be one of WIIMOD_SPEAKER_MODE_*,
+ * \rate is the PCM sample rate and \volume is the requested volume. */
+static int wiimod_speaker_setup(struct wiimote_speaker *speaker,
+				__u8 mode, __u16 rate, __s16 volume)
+{
+	struct wiimote_data *wdata = speaker->wdata;
+	unsigned long flags;
+	__u8 config[7], m, wmem;
+	__u16 r;
+	int ret;
+
+	if (!rate)
+		return -EINVAL;
+
+	switch (mode) {
+	case WIIMOD_SPEAKER_MODE_PCM8:
+		r = 12000000ULL / rate;
+		m = 0x40;
+		break;
+	case WIIMOD_SPEAKER_MODE_ADPCM4:
+		r = 6000000ULL / rate;
+		m = 0x00;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	config[0] = 0x00;
+	config[1] = m;
+	config[2] = r & 0x00ff;
+	config[3] = (r & 0xff00) >> 8;
+	config[4] = volume & 0xff;
+	config[5] = 0x00;
+	config[6] = 0x00;
+
+	wiimote_cmd_acquire_noint(wdata);
+
+	/* mute speaker during setup and read/write volume field */
+	spin_lock_irqsave(&wdata->state.lock, flags);
+
+	wiiproto_req_mute(wdata, true);
+	if (volume < 0)
+		config[4] = speaker->volume;
+	else
+		speaker->volume = volume;
+
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	/* power speaker */
+	wmem = 0x01;
+	ret = wiimote_cmd_write(wdata, 0xa20009, &wmem, sizeof(wmem));
+	if (ret)
+		goto out_unlock;
+
+	/* prepare setup */
+	wmem = 0x08;
+	ret = wiimote_cmd_write(wdata, 0xa20001, &wmem, sizeof(wmem));
+	if (ret)
+		goto out_unlock;
+
+	/* write configuration */
+	ret = wiimote_cmd_write(wdata, 0xa20001, config, sizeof(config));
+	if (ret)
+		goto out_unlock;
+
+	/* enable speaker */
+	wmem = 0x01;
+	ret = wiimote_cmd_write(wdata, 0xa20008, &wmem, sizeof(wmem));
+	if (ret)
+		goto out_unlock;
+
+out_unlock:
+	wiimote_cmd_release(wdata);
+	return ret;
+}
+
+/* returns true if a period has elapsed */
+static bool wiimod_speaker_push(struct wiimote_speaker *speaker)
+{
+	struct wiimote_data *wdata = speaker->wdata;
+	struct snd_pcm_runtime *runtime = speaker->subs->runtime;
+	unsigned long flags;
+	bool elapsed;
+	unsigned long buflen, plen, len;
+	__u8 *src;
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+
+	buflen = frames_to_bytes(runtime, runtime->buffer_size);
+	plen = frames_to_bytes(runtime, runtime->period_size);
+	src = runtime->dma_area;
+
+	len = buflen - speaker->pos;
+
+	/* no need to send data while muted or offline */
+	if (speaker->online && !speaker->mute) {
+		if (len < 20)
+			wiiproto_req_audio(wdata, &src[speaker->pos],
+					   len, src, 20 - len);
+		else
+			wiiproto_req_audio(wdata, &src[speaker->pos],
+					   20, NULL, 0);
+	}
+
+	speaker->pos += 20;
+	speaker->pos %= buflen;
+	elapsed = (speaker->pos % plen) < 20;
+
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	return elapsed;
+}
+
+/* timer handling */
+
+static void wiimod_speaker_task(unsigned long data)
+{
+	struct wiimote_speaker *speaker = (void*)data;
+	unsigned long flags;
+	bool elapsed = false;
+
+	spin_lock_irqsave(&speaker->lock, flags);
+	if (speaker->wdata)
+		elapsed = wiimod_speaker_push(speaker);
+	spin_unlock_irqrestore(&speaker->lock, flags);
+
+	if (elapsed)
+		snd_pcm_period_elapsed(speaker->subs);
+}
+
+static enum hrtimer_restart wiimod_speaker_tick(struct hrtimer *timer)
+{
+	struct wiimote_speaker *speaker = container_of(timer,
+						       struct wiimote_speaker,
+						       timer);
+	unsigned long missed;
+	__u64 ival;
+
+	tasklet_schedule(&speaker->tasklet);
+
+	ival = 1000000000ULL * 20U;
+	ival /= speaker->subs->runtime->rate;
+
+	missed = hrtimer_forward_now(timer, ns_to_ktime(ival));
+	if (missed > 1)
+		snd_printdd("wiimote: speaker: missed %lu timer interrupts\n",
+			    missed - 1);
+
+	return HRTIMER_RESTART;
+}
+
+/* PCM layer */
+
+static const struct snd_pcm_hardware wiimod_speaker_playback_hw = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_INTERLEAVED,
+	.formats = SNDRV_PCM_FMTBIT_S8,
+	.rates = SNDRV_PCM_RATE_CONTINUOUS,
+	.rate_min = 500,
+	.rate_max = 2000, /* TODO: >2000 only supported by 2nd gen */
+	.channels_min = 1,
+	.channels_max = 1,
+	.buffer_bytes_max = 32768,
+	.period_bytes_min = 128,
+	.period_bytes_max = 32768,
+	.periods_min = 1,
+	.periods_max = 1024,
+};
+
+static int wiimod_speaker_playback_open(struct snd_pcm_substream *substream)
+{
+	struct wiimote_speaker *speaker = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned long flags;
+	int ret;
+
+	runtime->hw = wiimod_speaker_playback_hw;
+
+	spin_lock_irqsave(&speaker->lock, flags);
+	ret = -ENODEV;
+	if (speaker->wdata) {
+		runtime->private_data = speaker->wdata;
+		ret = wiimod_speaker_enable(speaker);
+	}
+	spin_unlock_irqrestore(&speaker->lock, flags);
+
+	return ret;
+}
+
+static int wiimod_speaker_playback_close(struct snd_pcm_substream *substream)
+{
+	struct wiimote_speaker *speaker = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&speaker->lock, flags);
+	if (speaker->wdata)
+		wiimod_speaker_disable(speaker);
+	spin_unlock_irqrestore(&speaker->lock, flags);
+
+	hrtimer_cancel(&speaker->timer);
+	tasklet_kill(&speaker->tasklet);
+
+	return 0;
+}
+
+static int wiimod_speaker_playback_hw_params(struct snd_pcm_substream *subs,
+					     struct snd_pcm_hw_params *hw)
+{
+	return snd_pcm_lib_alloc_vmalloc_buffer(subs, params_buffer_bytes(hw));
+}
+
+static int wiimod_speaker_playback_hw_free(struct snd_pcm_substream *subs)
+{
+	return snd_pcm_lib_free_vmalloc_buffer(subs);
+}
+
+static int wiimod_speaker_playback_prepare(struct snd_pcm_substream *subs)
+{
+	struct wiimote_speaker *speaker = snd_pcm_substream_chip(subs);
+	struct wiimote_data *wdata;
+	struct snd_pcm_runtime *runtime = subs->runtime;
+	int ret, online;
+	unsigned long flags;
+
+	/* runlock synchronizes with device hotplugging */
+	mutex_lock(&speaker->runlock);
+
+	spin_lock_irqsave(&speaker->lock, flags);
+	wdata = speaker->wdata;
+	online = speaker->online;
+	spin_unlock_irqrestore(&speaker->lock, flags);
+
+	if (!wdata || !online) {
+		ret = -ENODEV;
+		goto unlock;
+	}
+
+	spin_lock_irqsave(&wdata->state.lock, flags);
+	speaker->pos = 0;
+	speaker->subs = subs;
+	spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+	/* Perform speaker setup. This may take a few milliseconds and the
+	 * handlers perform synchronous network operations so this
+	 * may sleep. */
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_S8:
+		ret = wiimod_speaker_setup(speaker, WIIMOD_SPEAKER_MODE_PCM8,
+					   runtime->rate, -1);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+unlock:
+	mutex_unlock(&speaker->runlock);
+	return ret;
+}
+
+static int wiimod_speaker_playback_trigger(struct snd_pcm_substream *subs,
+					   int cmd)
+{
+	struct wiimote_speaker *speaker = snd_pcm_substream_chip(subs);
+	struct snd_pcm_runtime *runtime = subs->runtime;
+	struct wiimote_data *wdata;
+	unsigned long flags;
+	__u64 ival;
+
+	spin_lock_irqsave(&speaker->lock, flags);
+
+	wdata = speaker->wdata;
+	if (!wdata || !speaker->online)
+		goto unlock;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		/* unmute device on start if not muted by user-space */
+		spin_lock(&wdata->state.lock);
+		if (!speaker->mute)
+			wiiproto_req_mute(wdata, false);
+		spin_unlock(&wdata->state.lock);
+
+		ival = 20U * 1000000000ULL / runtime->rate;
+		hrtimer_start(&speaker->timer, ktime_set(0, ival),
+			      HRTIMER_MODE_REL);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		/* mute device when stopping transmission */
+		spin_lock(&wdata->state.lock);
+		wiiproto_req_mute(wdata, true);
+		spin_unlock(&wdata->state.lock);
+
+		hrtimer_cancel(&speaker->timer);
+		break;
+	case SNDRV_PCM_TRIGGER_RESUME:
+		/* unmute device on start if not muted by user-space */
+		spin_lock(&wdata->state.lock);
+		if (!speaker->mute)
+			wiiproto_req_mute(wdata, false);
+		spin_unlock(&wdata->state.lock);
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		/* mute device when stopping transmission */
+		spin_lock(&wdata->state.lock);
+		wiiproto_req_mute(wdata, true);
+		spin_unlock(&wdata->state.lock);
+		break;
+	}
+
+unlock:
+	spin_unlock_irqrestore(&speaker->lock, flags);
+	return 0;
+}
+
+static snd_pcm_uframes_t wiimod_speaker_playback_pointer(struct snd_pcm_substream *subs)
+{
+	struct wiimote_speaker *speaker = snd_pcm_substream_chip(subs);
+
+	return bytes_to_frames(subs->runtime, speaker->pos);
+}
+
+static struct snd_pcm_ops wiimod_speaker_playback_ops = {
+	.open		= wiimod_speaker_playback_open,
+	.close		= wiimod_speaker_playback_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= wiimod_speaker_playback_hw_params,
+	.hw_free	= wiimod_speaker_playback_hw_free,
+	.prepare	= wiimod_speaker_playback_prepare,
+	.trigger	= wiimod_speaker_playback_trigger,
+	.pointer	= wiimod_speaker_playback_pointer,
+	.page		= snd_pcm_lib_get_vmalloc_page,
+	.mmap		= snd_pcm_lib_mmap_vmalloc,
+};
+
+/* volume control */
+
+static int wiimod_speaker_volume_info(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_info *info)
+{
+	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	info->count = 1;
+	info->value.integer.min = 0;
+	info->value.integer.max = 0xff;
+
+	return 0;
+}
+
+static int wiimod_speaker_volume_get(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *val)
+{
+	struct wiimote_speaker *speaker = snd_kcontrol_chip(kcontrol);
+
+	val->value.integer.value[0] = speaker->volume;
+
+	return 0;
+}
+
+static int wiimod_speaker_volume_put(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *val)
+{
+	struct wiimote_speaker *speaker = snd_kcontrol_chip(kcontrol);
+	unsigned long value, flags;
+
+	value = val->value.integer.value[0];
+	if (value > 0xff)
+		value = 0xff;
+
+	spin_lock_irqsave(&speaker->lock, flags);
+	if (speaker->wdata)
+		wiimod_speaker_set_volume(speaker, value);
+	spin_unlock_irqrestore(&speaker->lock, flags);
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new wiimod_speaker_volume = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "PCM Playback Volume",
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = wiimod_speaker_volume_info,
+	.get = wiimod_speaker_volume_get,
+	.put = wiimod_speaker_volume_put,
+};
+
+/* mute control */
+
+static int wiimod_speaker_mute_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *val)
+{
+	struct wiimote_speaker *speaker = snd_kcontrol_chip(kcontrol);
+
+	val->value.integer.value[0] = !speaker->mute;
+
+	return 0;
+}
+
+static int wiimod_speaker_mute_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *val)
+{
+	struct wiimote_speaker *speaker = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&speaker->lock, flags);
+	if (speaker->wdata)
+		wiimod_speaker_set_mute(speaker,
+					!val->value.integer.value[0]);
+	spin_unlock_irqrestore(&speaker->lock, flags);
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new wiimod_speaker_mute = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "PCM Playback Switch",
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = snd_ctl_boolean_mono_info,
+	.get = wiimod_speaker_mute_get,
+	.put = wiimod_speaker_mute_put,
+};
+
+/* initialization and setup */
+
+static int wiimod_speaker_probe(const struct wiimod_ops *ops,
+				struct wiimote_data *wdata)
+{
+	int ret;
+	struct wiimote_speaker *speaker;
+	struct snd_card *card;
+	struct snd_kcontrol *kcontrol;
+	struct snd_pcm *pcm;
+
+	/* create sound card device */
+	ret = snd_card_create(-1, NULL, THIS_MODULE,
+			      sizeof(struct wiimote_speaker), &card);
+	if (ret)
+		return ret;
+	speaker = card->private_data;
+
+	wdata->speaker = speaker;
+	speaker->wdata = wdata;
+	speaker->card = card;
+	speaker->mute = 1;
+	speaker->volume = 0xff;
+	strcpy(card->driver, "hid-wiimote");
+	strcpy(card->shortname, "wiimote");
+	strcpy(card->longname, "Nintendo Wii Remote speaker");
+
+	spin_lock_init(&speaker->lock);
+	mutex_init(&speaker->runlock);
+	hrtimer_init(&speaker->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	speaker->timer.function = wiimod_speaker_tick;
+	tasklet_init(&speaker->tasklet, wiimod_speaker_task,
+		     (unsigned long)speaker);
+
+	/* create volume control */
+	kcontrol = snd_ctl_new1(&wiimod_speaker_volume, speaker);
+	if (!kcontrol) {
+		ret = -ENOMEM;
+		goto err_free;
+	}
+
+	ret = snd_ctl_add(card, kcontrol);
+	if (ret) {
+		snd_ctl_free_one(kcontrol);
+		goto err_free;
+	}
+
+	/* create mute control */
+	kcontrol = snd_ctl_new1(&wiimod_speaker_mute, speaker);
+	if (!kcontrol) {
+		ret = -ENOMEM;
+		goto err_free;
+	}
+
+	ret = snd_ctl_add(card, kcontrol);
+	if (ret) {
+		snd_ctl_free_one(kcontrol);
+		goto err_free;
+	}
+
+	/* create PCM sub-device for playback */
+	ret = snd_pcm_new(card, "Speaker", 0, 1, 0, &pcm);
+	if (ret)
+		goto err_free;
+
+	pcm->private_data = speaker;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&wiimod_speaker_playback_ops);
+
+	/* register sound card */
+	snd_card_set_dev(card, &wdata->hdev->dev);
+	ret = snd_card_register(card);
+	if (ret)
+		goto err_free;
+
+	return 0;
+
+err_free:
+	snd_card_free(card);
+	wdata->speaker = NULL;
+	return ret;
+}
+
+static void wiimod_speaker_remove(const struct wiimod_ops *ops,
+				  struct wiimote_data *wdata)
+{
+	struct wiimote_speaker *speaker = wdata->speaker;
+	unsigned long flags;
+
+	if (!speaker)
+		return;
+
+	mutex_lock(&speaker->runlock);
+	spin_lock_irqsave(&speaker->lock, flags);
+
+	wdata->speaker = NULL;
+	speaker->online = 0;
+	speaker->wdata = NULL;
+
+	spin_lock(&wdata->state.lock);
+	wiiproto_req_mute(wdata, true);
+	wiiproto_req_speaker(wdata, false);
+	spin_unlock(&wdata->state.lock);
+
+	spin_unlock_irqrestore(&speaker->lock, flags);
+	mutex_unlock(&speaker->runlock);
+
+	hrtimer_cancel(&speaker->timer);
+	tasklet_kill(&speaker->tasklet);
+	snd_card_free_when_closed(speaker->card);
+}
+
+const struct wiimod_ops wiimod_speaker = {
+	.flags = 0,
+	.arg = 0,
+	.probe = wiimod_speaker_probe,
+	.remove = wiimod_speaker_remove,
+};
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index f1474f3..63a9244 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -147,6 +147,7 @@ struct wiimote_data {
 	struct input_dev *mp;
 	struct timer_list timer;
 	struct wiimote_debug *debug;
+	struct wiimote_speaker *speaker;
 
 	union {
 		struct input_dev *input;
@@ -171,6 +172,7 @@ enum wiimod_module {
 	WIIMOD_IR,
 	WIIMOD_BUILTIN_MP,
 	WIIMOD_NO_MP,
+	WIIMOD_SPEAKER,
 	WIIMOD_NUM,
 	WIIMOD_NULL = WIIMOD_NUM,
 };
@@ -207,9 +209,12 @@ enum wiiproto_reqs {
 	WIIPROTO_REQ_LED = 0x11,
 	WIIPROTO_REQ_DRM = 0x12,
 	WIIPROTO_REQ_IR1 = 0x13,
+	WIIPROTO_REQ_SPEAKER = 0x14,
 	WIIPROTO_REQ_SREQ = 0x15,
 	WIIPROTO_REQ_WMEM = 0x16,
 	WIIPROTO_REQ_RMEM = 0x17,
+	WIIPROTO_REQ_AUDIO = 0x18,
+	WIIPROTO_REQ_MUTE = 0x19,
 	WIIPROTO_REQ_IR2 = 0x1a,
 	WIIPROTO_REQ_STATUS = 0x20,
 	WIIPROTO_REQ_DATA = 0x21,
@@ -263,6 +268,12 @@ extern void wiiproto_req_status(struct wiimote_data *wdata);
 extern void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel);
 extern void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags);
 extern void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags);
+extern void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom,
+			      __u32 offset, const __u8 *buf, __u8 size);
+extern void wiiproto_req_speaker(struct wiimote_data *wdata, bool on);
+extern void wiiproto_req_mute(struct wiimote_data *wdata, bool on);
+extern size_t wiiproto_req_audio(struct wiimote_data *wdata, const __u8 *b1,
+				 size_t len1, const __u8 *b2, size_t len2);
 extern int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
 						const __u8 *wmem, __u8 size);
 extern ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset,
@@ -287,6 +298,12 @@ static inline void wiidebug_deinit(void *u) { }
 
 #endif
 
+#ifdef CONFIG_SND
+
+extern const struct wiimod_ops wiimod_speaker;
+
+#endif
+
 /* requires the state.lock spinlock to be held */
 static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd,
 								__u32 opt)
-- 
1.8.2.2


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [PATCH 16/26] HID: wiimote: add Classic Controller extension
  2013-05-05 21:13 ` [PATCH 16/26] HID: wiimote: add Classic Controller extension David Herrmann
@ 2013-05-06  0:39   ` Todd Showalter
  2013-05-06  5:40     ` David Herrmann
  0 siblings, 1 reply; 50+ messages in thread
From: Todd Showalter @ 2013-05-06  0:39 UTC (permalink / raw)
  To: David Herrmann; +Cc: open list:HID CORE LAYER, Jiri Kosina

On Sun, May 5, 2013 at 5:13 PM, David Herrmann <dh.herrmann@gmail.com> wrote:

> Add a new extension module for the classic controller so we get hotplug
> support for this device. It is mostly the same as the old static classic
> controller parser.

    Could the buttons/axis values on this please be mapped as
appropriate for the standard gamepad?  What I'd like, ideally, with
new #defines in input.h as needed:

- left stick to ABS_LX, ABS_LY
- right stick to ABS_RX, ABS_RY
- dpad to ABS_DX, ABS_DY
- left trigger to ABS_LTRIG
- right trigger to ABS_RTRIG
- x to BTN_NORTH
- a to BTN_EAST
- b to BTN_SOUTH
- y to BTN_WEST
- zl to BTN_LSHOULDER
- zr to BTN_RSHOULDER
- plus to BTN_START
- minus to BTN_SELECT
- home to BTN_SYSTEM

    I'm trying to get all of the gamepads to use a common control
mapping.  Mapping functions by placement on the controller is far more
useful to games than mapping them by the names assigned by the
manufacturer.  There is no consistency on button naming between
manufacturers, but there is a lot of consistency in functionality and
layout.  I'm hoping that as we bring new gamepads online, we can
expose that layout consistency.

    As an example of what I mean, the classic controller x button is
in the same position as:

- the x button on nintendo gamepads (oddly...)
- the y button on xbox-family gamepads
- the y button on sega gamepads
- the y button on the ouya gamepads
- the p button on the gamestick gamepad
- the triangle button on playstation-family gamepads
- the triangle button on some futime gamepads
- the not-quite-a-triangle don't sue us sony button on at least one
betop gamepad (google for the football one)
- the blue button on gravis controllers
- the 1 button on the noga.net gamepad
- the 1 button on some saitek gamepads
- the 2 button on some steelseries gamepads
- the 4 button on some other steelseries gamepads
- the 4 button on some logitech gamepads
- probably every glyph in the basic multilingual plane on some gamepad or other

    I want those all to map to BTN_NORTH.  They're all at the top of a
diamond-shaped cluster of 4 face buttons under the player's right
thumb.  From a game development point of view, it's that position that
matters, because I'm going to be assigning functions to buttons based
on how accessible the buttons are rather than what random name the
manufacturer may have assigned to them.  Especially when for a while
there was some worry about whether the pattern of button labels was
copyrightable, so the second tier hardware manufacturers were
explicitly avoiding using established button naming conventions.

    If you look at the above list, it clearly doesn't make any sense
to use the button labels to identify these.  It makes far more sense
to identify them by layout position in a standardized layout.

    It seems like there's going to need to be a mapping layer for
gamepads that already have established interfaces (and I'm actually
writing a library to do that right now...), but I'd really prefer if
the library was just for legacy support.

                                       Todd.

--
 Todd Showalter, President,
 Electron Jump Games, Inc.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
  2013-05-05 21:13 ` [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller David Herrmann
@ 2013-05-06  0:43   ` Todd Showalter
  2013-05-06  5:49     ` David Herrmann
  0 siblings, 1 reply; 50+ messages in thread
From: Todd Showalter @ 2013-05-06  0:43 UTC (permalink / raw)
  To: David Herrmann; +Cc: open list:HID CORE LAYER, Jiri Kosina

On Sun, May 5, 2013 at 5:13 PM, David Herrmann <dh.herrmann@gmail.com> wrote:

> The Wii U Pro Controller is a new Nintendo remote device that looks very
> similar to the XBox controller. It has nearly the same features and uses
> the same protocol as the Wii Remote.

    As with the classic controller, could this please be mapped to the
standard gamepad where possible?

                                      Todd.

--
 Todd Showalter, President,
 Electron Jump Games, Inc.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 16/26] HID: wiimote: add Classic Controller extension
  2013-05-06  0:39   ` Todd Showalter
@ 2013-05-06  5:40     ` David Herrmann
  2013-05-06 14:15       ` Todd Showalter
  0 siblings, 1 reply; 50+ messages in thread
From: David Herrmann @ 2013-05-06  5:40 UTC (permalink / raw)
  To: Todd Showalter; +Cc: open list:HID CORE LAYER, Jiri Kosina

Hi Todd

On Mon, May 6, 2013 at 2:39 AM, Todd Showalter <todd@electronjump.com> wrote:
> On Sun, May 5, 2013 at 5:13 PM, David Herrmann <dh.herrmann@gmail.com> wrote:
>
>> Add a new extension module for the classic controller so we get hotplug
>> support for this device. It is mostly the same as the old static classic
>> controller parser.
>
>     Could the buttons/axis values on this please be mapped as
> appropriate for the standard gamepad?  What I'd like, ideally, with
> new #defines in input.h as needed:
>
> - left stick to ABS_LX, ABS_LY
> - right stick to ABS_RX, ABS_RY
> - dpad to ABS_DX, ABS_DY
> - left trigger to ABS_LTRIG
> - right trigger to ABS_RTRIG
> - x to BTN_NORTH
> - a to BTN_EAST
> - b to BTN_SOUTH
> - y to BTN_WEST
> - zl to BTN_LSHOULDER
> - zr to BTN_RSHOULDER
> - plus to BTN_START
> - minus to BTN_SELECT
> - home to BTN_SYSTEM

I cannot change the mapping here, because it has to be
backwards-compatible with the old classic-controller mapping.
Libraries like "xwiimote" depend on it.
This patch only converts the code into a "wiimod" extension module.

Regards
David

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
  2013-05-06  0:43   ` Todd Showalter
@ 2013-05-06  5:49     ` David Herrmann
  2013-05-06 14:14       ` Todd Showalter
  0 siblings, 1 reply; 50+ messages in thread
From: David Herrmann @ 2013-05-06  5:49 UTC (permalink / raw)
  To: Todd Showalter; +Cc: open list:HID CORE LAYER, Jiri Kosina

Hi Todd

On Mon, May 6, 2013 at 2:43 AM, Todd Showalter <todd@electronjump.com> wrote:
> On Sun, May 5, 2013 at 5:13 PM, David Herrmann <dh.herrmann@gmail.com> wrote:
>
>> The Wii U Pro Controller is a new Nintendo remote device that looks very
>> similar to the XBox controller. It has nearly the same features and uses
>> the same protocol as the Wii Remote.
>
>     As with the classic controller, could this please be mapped to the
> standard gamepad where possible?

> - left stick to ABS_LX, ABS_LY
> - right stick to ABS_RX, ABS_RY

These make sense to me, indeed.

> - dpad to ABS_DX, ABS_DY

These are digital buttons on this gamepad. What's wrong with
KEY_{left, right, up, down}? Or I would prefer BTN_{left, right, up,
down} instead.

> - left trigger to ABS_LTRIG
> - right trigger to ABS_RTRIG

These are also digital buttons, so what's wrong with BTN_TL/TR?

> - x to BTN_NORTH
> - a to BTN_EAST
> - b to BTN_SOUTH
> - y to BTN_WEST

These make sense to me. At least keyboards also return the American
keycodes instead of trying to guess the label. However, if you want
"help texts" in your applications, like "press A to continue", you
need a user-space helper library, anyway. Otherwise, you cannot be
sure this key is labeled "A" on the current device.
That's also the reason why I don't think these mappings really help.

> - zl to BTN_LSHOULDER
> - zr to BTN_RSHOULDER

Again, why not reuse BTN_ZL/ZR?

> - plus to BTN_START
> - minus to BTN_SELECT
> - home to BTN_SYSTEM

The other Nintendo drivers return BTN_MODE for "home". Could you
explain what's wrong with that?

Thanks!
David

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
  2013-05-06  5:49     ` David Herrmann
@ 2013-05-06 14:14       ` Todd Showalter
  2013-05-08 15:33         ` David Herrmann
  0 siblings, 1 reply; 50+ messages in thread
From: Todd Showalter @ 2013-05-06 14:14 UTC (permalink / raw)
  To: David Herrmann; +Cc: open list:HID CORE LAYER, Jiri Kosina

On Mon, May 6, 2013 at 1:49 AM, David Herrmann <dh.herrmann@gmail.com> wrote:

>>> The Wii U Pro Controller is a new Nintendo remote device that looks very
>>> similar to the XBox controller. It has nearly the same features and uses
>>> the same protocol as the Wii Remote.
>>
>>     As with the classic controller, could this please be mapped to the
>> standard gamepad where possible?
>
>> - left stick to ABS_LX, ABS_LY
>> - right stick to ABS_RX, ABS_RY
>
> These make sense to me, indeed.
>
>> - dpad to ABS_DX, ABS_DY
>
> These are digital buttons on this gamepad. What's wrong with
> KEY_{left, right, up, down}? Or I would prefer BTN_{left, right, up,
> down} instead.

    Some of the gamepads in evdev currently map their dpads to
ABS_HAT0X, ABS_HAT0Y.  For the most part I'd be good with switching
them to buttons; it's mostly just a matter of choosing a standard and
sticking to it.  The only argument for making them ABS_ values (other
than "some of them already do that") is the Sony controllers; the PS2
and PS3 dpads are actually pressure sensitive, and have an effective
resolution double that of the analog sticks even before you take the
deadzone into account.  The PS* analog sticks are very noisy and need
large deadzones, while the dpad pressure sensitivity is quite stable
around the center for obvious reasons.

>> - left trigger to ABS_LTRIG
>> - right trigger to ABS_RTRIG
>
> These are also digital buttons, so what's wrong with BTN_TL/TR?

    They're digital on the classic pro, but they're analog on the
classic, IIRC.  I believe they also have separate "button" values when
they bottom out, much like the gamecube controller, but that's beyond
the scope of the standard gamepad I'm proposing.

>> - x to BTN_NORTH
>> - a to BTN_EAST
>> - b to BTN_SOUTH
>> - y to BTN_WEST
>
> These make sense to me. At least keyboards also return the American
> keycodes instead of trying to guess the label. However, if you want
> "help texts" in your applications, like "press A to continue", you
> need a user-space helper library, anyway. Otherwise, you cannot be
> sure this key is labeled "A" on the current device.
> That's also the reason why I don't think these mappings really help.

    I'd far rather have position-defined buttons and then some sort of
mapping I could look up to tell me what the label was on the player's
controller.  I might want to put help text up, but I definitely want
to read the button.  Besides, with the standard gamepad I'm proposing
you could throw up a diagram of the controller with (for example) an
arrow pointing to a blank NORTH button just saying "Menu" or "Battloid
Mode" or "Use".

    Making a game is often about deciding where to spend your limited
resources.  Money, time, artwork and programming effort you spend
customizing the help screens for different controllers is effort not
being spent on making the part of the game that people play better.

>> - zl to BTN_LSHOULDER
>> - zr to BTN_RSHOULDER
>
> Again, why not reuse BTN_ZL/ZR?

    Because there are equivalent controls on lots of other gamepads.
L1/R1 on sony gamepads, LB/RB on xbox 360, and so forth.  Again, it's
the position and function that matters, not the arbitrary label on the
button.  The label on the button is only arguably useful for help
text, but there is *so much* variance in labeling between functionally
identical hardware that it makes no sense to waste the development
effort on custom per-controller help text.

>> - plus to BTN_START
>> - minus to BTN_SELECT
>> - home to BTN_SYSTEM
>
> The other Nintendo drivers return BTN_MODE for "home". Could you
> explain what's wrong with that?

    It's not essential; we could make the others do BTN_MODE.  The
idea is that we've got what's essentially a system request button on
many gamepads; a button whose job is to bypass the game and bring up
the underlying OS.  On recent Nintendo controllers (Wii, WiiU) it's
the "Home" button.  On PS3 it's the playstation logo button.  On XBox
360 controllers it's the big glowy X button.

    All of those are attempting to solve the same problem on their
respective consoles; how does one simply get through a full-screen
game to talk to the OS when the gamepad is the primary input method
and all of its functions have game mappings?  The answer in all three
cases was to put a system button on the controller that games aren't
allowed to map.

    We're not bound by that, obviously, but in the case of living room
gaming PCs where we may well have Linux boxes that are being treated
like game consoles much of the time, having a standardized system
request button on gamepads that have appropriate hardware is a useful
abstraction.  I'm suggesting calling it BTN_SYSTEM rather than
BTN_MODE because to me BTN_MODE implies different things, but it's
admittedly semantics at that point.

                                                Todd.

--
 Todd Showalter, President,
 Electron Jump Games, Inc.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 16/26] HID: wiimote: add Classic Controller extension
  2013-05-06  5:40     ` David Herrmann
@ 2013-05-06 14:15       ` Todd Showalter
  0 siblings, 0 replies; 50+ messages in thread
From: Todd Showalter @ 2013-05-06 14:15 UTC (permalink / raw)
  To: David Herrmann; +Cc: open list:HID CORE LAYER, Jiri Kosina

On Mon, May 6, 2013 at 1:40 AM, David Herrmann <dh.herrmann@gmail.com> wrote:

> I cannot change the mapping here, because it has to be
> backwards-compatible with the old classic-controller mapping.
> Libraries like "xwiimote" depend on it.
> This patch only converts the code into a "wiimod" extension module.

    Ok.  I'll deal with this case in the library, then.

                                          Todd.

--
 Todd Showalter, President,
 Electron Jump Games, Inc.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
  2013-05-06 14:14       ` Todd Showalter
@ 2013-05-08 15:33         ` David Herrmann
  2013-05-08 16:40           ` Todd Showalter
  0 siblings, 1 reply; 50+ messages in thread
From: David Herrmann @ 2013-05-08 15:33 UTC (permalink / raw)
  To: Todd Showalter; +Cc: open list:HID CORE LAYER, Jiri Kosina, Dmitry Torokhov

Hi Todd

On Mon, May 6, 2013 at 4:14 PM, Todd Showalter <todd@electronjump.com> wrote:
> On Mon, May 6, 2013 at 1:49 AM, David Herrmann <dh.herrmann@gmail.com> wrote:
>
>>>> The Wii U Pro Controller is a new Nintendo remote device that looks very
>>>> similar to the XBox controller. It has nearly the same features and uses
>>>> the same protocol as the Wii Remote.
>>>
>>>     As with the classic controller, could this please be mapped to the
>>> standard gamepad where possible?
>>
>>> - left stick to ABS_LX, ABS_LY
>>> - right stick to ABS_RX, ABS_RY
>>
>> These make sense to me, indeed.
>>
>>> - dpad to ABS_DX, ABS_DY
>>
>> These are digital buttons on this gamepad. What's wrong with
>> KEY_{left, right, up, down}? Or I would prefer BTN_{left, right, up,
>> down} instead.
>
>     Some of the gamepads in evdev currently map their dpads to
> ABS_HAT0X, ABS_HAT0Y.  For the most part I'd be good with switching
> them to buttons; it's mostly just a matter of choosing a standard and
> sticking to it.  The only argument for making them ABS_ values (other
> than "some of them already do that") is the Sony controllers; the PS2
> and PS3 dpads are actually pressure sensitive, and have an effective
> resolution double that of the analog sticks even before you take the
> deadzone into account.  The PS* analog sticks are very noisy and need
> large deadzones, while the dpad pressure sensitivity is quite stable
> around the center for obvious reasons.

See below, same reasons apply here.

>>> - left trigger to ABS_LTRIG
>>> - right trigger to ABS_RTRIG
>>
>> These are also digital buttons, so what's wrong with BTN_TL/TR?
>
>     They're digital on the classic pro, but they're analog on the
> classic, IIRC.  I believe they also have separate "button" values when
> they bottom out, much like the gamecube controller, but that's beyond
> the scope of the standard gamepad I'm proposing.

What's the problem with using both, ABS _and_ BTN? I don't think we
should do any interpretation in the kernel. Devices that have analog
triggers report ABS_XTRIG, devices with digital triggers report
BTN_TX. Userspace simply needs to listen for both. And if a device
exports both (like the Wii classic controller), they simply export
both and userspace chooses whatever it prefers.

As long as both ABS_XTRIG and BTN_TX are defined to correspond to the
same triggers, I think it's better to use both. Or am I missing
something?

>>> - x to BTN_NORTH
>>> - a to BTN_EAST
>>> - b to BTN_SOUTH
>>> - y to BTN_WEST
>>
>> These make sense to me. At least keyboards also return the American
>> keycodes instead of trying to guess the label. However, if you want
>> "help texts" in your applications, like "press A to continue", you
>> need a user-space helper library, anyway. Otherwise, you cannot be
>> sure this key is labeled "A" on the current device.
>> That's also the reason why I don't think these mappings really help.
>
>     I'd far rather have position-defined buttons and then some sort of
> mapping I could look up to tell me what the label was on the player's
> controller.  I might want to put help text up, but I definitely want
> to read the button.  Besides, with the standard gamepad I'm proposing
> you could throw up a diagram of the controller with (for example) an
> arrow pointing to a blank NORTH button just saying "Menu" or "Battloid
> Mode" or "Use".
>
>     Making a game is often about deciding where to spend your limited
> resources.  Money, time, artwork and programming effort you spend
> customizing the help screens for different controllers is effort not
> being spent on making the part of the game that people play better.

As I said before, I don't really care. I will send v2 with a patch
that introduces these buttons. Lets see what Dmitry thinks.

But we need a user-space library, anyway, to get button-names (as I
explained earlier).

>>> - zl to BTN_LSHOULDER
>>> - zr to BTN_RSHOULDER
>>
>> Again, why not reuse BTN_ZL/ZR?
>
>     Because there are equivalent controls on lots of other gamepads.
> L1/R1 on sony gamepads, LB/RB on xbox 360, and so forth.  Again, it's
> the position and function that matters, not the arbitrary label on the
> button.  The label on the button is only arguably useful for help
> text, but there is *so much* variance in labeling between functionally
> identical hardware that it makes no sense to waste the development
> effort on custom per-controller help text.

No, I meant what's wrong with re-using BTN_ZX for this purpose? Why
introduce new constants? Just say, all new drivers must not use BTN_ZX
if it's not a shoulder button. This avoids introducing BTN_XSHOULDER.

And btw., there is no BTN_ZL/ZR, I meant BTN_TL2/TR2.. sorry for the
confusion, my bad.

>>> - plus to BTN_START
>>> - minus to BTN_SELECT
>>> - home to BTN_SYSTEM
>>
>> The other Nintendo drivers return BTN_MODE for "home". Could you
>> explain what's wrong with that?
>
>     It's not essential; we could make the others do BTN_MODE.  The
> idea is that we've got what's essentially a system request button on
> many gamepads; a button whose job is to bypass the game and bring up
> the underlying OS.  On recent Nintendo controllers (Wii, WiiU) it's
> the "Home" button.  On PS3 it's the playstation logo button.  On XBox
> 360 controllers it's the big glowy X button.
>
>     All of those are attempting to solve the same problem on their
> respective consoles; how does one simply get through a full-screen
> game to talk to the OS when the gamepad is the primary input method
> and all of its functions have game mappings?  The answer in all three
> cases was to put a system button on the controller that games aren't
> allowed to map.
>
>     We're not bound by that, obviously, but in the case of living room
> gaming PCs where we may well have Linux boxes that are being treated
> like game consoles much of the time, having a standardized system
> request button on gamepads that have appropriate hardware is a useful
> abstraction.  I'm suggesting calling it BTN_SYSTEM rather than
> BTN_MODE because to me BTN_MODE implies different things, but it's
> admittedly semantics at that point.

So how about the following?

+static const __u16 wiimod_pro_map[] = {
+       BTN_EAST,          /* WIIMOD_PRO_KEY_A */
+       BTN_SOUTH,          /* WIIMOD_PRO_KEY_B */
+       BTN_NORTH,          /* WIIMOD_PRO_KEY_X */
+       BTN_WEST,          /* WIIMOD_PRO_KEY_Y */
+       BTN_START,      /* WIIMOD_PRO_KEY_PLUS */
+       BTN_SELECT,     /* WIIMOD_PRO_KEY_MINUS */
+       BTN_MODE,       /* WIIMOD_PRO_KEY_HOME */
+       BTN_DLEFT,       /* WIIMOD_PRO_KEY_LEFT */
+       BTN_DRIGHT,      /* WIIMOD_PRO_KEY_RIGHT */
+       BTN_DUP,         /* WIIMOD_PRO_KEY_UP */
+       BTN_DDOWN,       /* WIIMOD_PRO_KEY_DOWN */
+       BTN_TL,         /* WIIMOD_PRO_KEY_TL */
+       BTN_TR,         /* WIIMOD_PRO_KEY_TR */
+       BTN_TL2,        /* WIIMOD_PRO_KEY_ZL */
+       BTN_TR2,        /* WIIMOD_PRO_KEY_ZR */
+       BTN_THUMBL,     /* WIIMOD_PRO_KEY_THUMBL */
+       BTN_THUMBR,     /* WIIMOD_PRO_KEY_THUMBR */
+};

That would introduce BTN_DLEFT/DRIGHT/DUP/DDOWN for D-Pad buttons and
BTN_EAST/SOUTH/NORTH/WEST for action buttons.

If everyone is ok with this, I will resend the patches for
Pro-Controller after the other parts of this series got
applied/acked/nacked/whatever. I will also add some documentation to
src/Documentation/input/ and try to see what we can do about the other
gamepads.

Thanks
David

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
  2013-05-08 15:33         ` David Herrmann
@ 2013-05-08 16:40           ` Todd Showalter
  2013-05-08 17:05             ` Dmitry Torokhov
  0 siblings, 1 reply; 50+ messages in thread
From: Todd Showalter @ 2013-05-08 16:40 UTC (permalink / raw)
  To: David Herrmann; +Cc: open list:HID CORE LAYER, Jiri Kosina, Dmitry Torokhov

On Wed, May 8, 2013 at 11:33 AM, David Herrmann <dh.herrmann@gmail.com> wrote:

>>     They're digital on the classic pro, but they're analog on the
>> classic, IIRC.  I believe they also have separate "button" values when
>> they bottom out, much like the gamecube controller, but that's beyond
>> the scope of the standard gamepad I'm proposing.
>
> What's the problem with using both, ABS _and_ BTN? I don't think we
> should do any interpretation in the kernel. Devices that have analog
> triggers report ABS_XTRIG, devices with digital triggers report
> BTN_TX. Userspace simply needs to listen for both. And if a device
> exports both (like the Wii classic controller), they simply export
> both and userspace chooses whatever it prefers.

    My opinion is that the kernel is the right place to provide
hardware abstraction, and that a standard gamepad is useful the same
way a standard mouse, a standard ethernet device or a standard
filesystem interface are useful abstractions.  That is, I don't want
to have to care who made it if I want to make normal use of it.  My
opinion isn't universally shared, however, so I'm working on a library
to sit between the kernel and things that want a standard gamepad.

> As long as both ABS_XTRIG and BTN_TX are defined to correspond to the
> same triggers, I think it's better to use both. Or am I missing
> something?

    The problem is the specific thing I'm trying to solve with the
idea of the standard gamepad.  Right now, if I open() something in
/dev/input that seems like it ought to be a gamepad, I get a giant
unsorted bag of events that are only comprehensible via a priori
knowledge.  I keep coming back to the ps3 controller because it's the
worst offender that I've found; the right stick maps to ABS_Z, ABS_RZ,
while the XBox controller right stick maps to ABS_RX, ABS_RY.  This is
not documented anywhere I've found, and I've no idea what quirks other
controllers have in their mappings or whether they are documented or
not.

    What I'm trying to do is make it so that there are five categories
of control, in order of usefulness to games:

1) known controls that all sane gamepads will have (left stick, dpad,
NSEW buttons...)
2) known controls that most sane gamepads will have (right stick...)
3) known controls that many gamepads have (system/mode button, select button...)
4) common things (accelerometers...)
5) device-specific controls

    Right now, almost everything is in bucket 5, and from a game
development point of view that sucks.  The further up the buckets we
can move a control, the better.

    So, the problem that I have with "devices that have analog
triggers report XTRIG, devices with digital triggers report TX" is
that absent a common mapping I now potentially have to care about the
difference when I'm developing a game.  It moves these to bucket 3,
which means as a developer I likely just don't use them unless my game
really needs them.

> As I said before, I don't really care. I will send v2 with a patch
> that introduces these buttons. Lets see what Dmitry thinks.
>
> But we need a user-space library, anyway, to get button-names (as I
> explained earlier).

    I'm working on that. :)

> No, I meant what's wrong with re-using BTN_ZX for this purpose? Why
> introduce new constants? Just say, all new drivers must not use BTN_ZX
> if it's not a shoulder button. This avoids introducing BTN_XSHOULDER.

    I suppose we could just use that.  It probably makes more sense to
use BTN_TL and BTN_TR, though; the xbox 360 controller maps LB and RB
to BTN_TL and BTN_TR, and they're equivalent placement and function.

> And btw., there is no BTN_ZL/ZR, I meant BTN_TL2/TR2.. sorry for the
> confusion, my bad.

    Fair enough.

> So how about the following?
>
> +static const __u16 wiimod_pro_map[] = {
> +       BTN_EAST,          /* WIIMOD_PRO_KEY_A */
> +       BTN_SOUTH,          /* WIIMOD_PRO_KEY_B */
> +       BTN_NORTH,          /* WIIMOD_PRO_KEY_X */
> +       BTN_WEST,          /* WIIMOD_PRO_KEY_Y */
> +       BTN_START,      /* WIIMOD_PRO_KEY_PLUS */
> +       BTN_SELECT,     /* WIIMOD_PRO_KEY_MINUS */
> +       BTN_MODE,       /* WIIMOD_PRO_KEY_HOME */
> +       BTN_DLEFT,       /* WIIMOD_PRO_KEY_LEFT */
> +       BTN_DRIGHT,      /* WIIMOD_PRO_KEY_RIGHT */
> +       BTN_DUP,         /* WIIMOD_PRO_KEY_UP */
> +       BTN_DDOWN,       /* WIIMOD_PRO_KEY_DOWN */
> +       BTN_TL,         /* WIIMOD_PRO_KEY_TL */
> +       BTN_TR,         /* WIIMOD_PRO_KEY_TR */
> +       BTN_TL2,        /* WIIMOD_PRO_KEY_ZL */
> +       BTN_TR2,        /* WIIMOD_PRO_KEY_ZR */
> +       BTN_THUMBL,     /* WIIMOD_PRO_KEY_THUMBL */
> +       BTN_THUMBR,     /* WIIMOD_PRO_KEY_THUMBR */
> +};
>
> That would introduce BTN_DLEFT/DRIGHT/DUP/DDOWN for D-Pad buttons and
> BTN_EAST/SOUTH/NORTH/WEST for action buttons.

    That looks fine to me.

                                      Todd.

--
 Todd Showalter, President,
 Electron Jump Games, Inc.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
  2013-05-08 16:40           ` Todd Showalter
@ 2013-05-08 17:05             ` Dmitry Torokhov
  2013-05-08 17:20               ` Todd Showalter
  0 siblings, 1 reply; 50+ messages in thread
From: Dmitry Torokhov @ 2013-05-08 17:05 UTC (permalink / raw)
  To: Todd Showalter; +Cc: David Herrmann, open list:HID CORE LAYER, Jiri Kosina

On Wed, May 08, 2013 at 12:40:51PM -0400, Todd Showalter wrote:
> On Wed, May 8, 2013 at 11:33 AM, David Herrmann <dh.herrmann@gmail.com> wrote:
> 
> >>     They're digital on the classic pro, but they're analog on the
> >> classic, IIRC.  I believe they also have separate "button" values when
> >> they bottom out, much like the gamecube controller, but that's beyond
> >> the scope of the standard gamepad I'm proposing.
> >
> > What's the problem with using both, ABS _and_ BTN? I don't think we
> > should do any interpretation in the kernel. Devices that have analog
> > triggers report ABS_XTRIG, devices with digital triggers report
> > BTN_TX. Userspace simply needs to listen for both. And if a device
> > exports both (like the Wii classic controller), they simply export
> > both and userspace chooses whatever it prefers.
> 
>     My opinion is that the kernel is the right place to provide
> hardware abstraction, and that a standard gamepad is useful the same
> way a standard mouse, a standard ethernet device or a standard
> filesystem interface are useful abstractions.  That is, I don't want
> to have to care who made it if I want to make normal use of it.  My
> opinion isn't universally shared, however, so I'm working on a library
> to sit between the kernel and things that want a standard gamepad.
> 
> > As long as both ABS_XTRIG and BTN_TX are defined to correspond to the
> > same triggers, I think it's better to use both. Or am I missing
> > something?
> 
>     The problem is the specific thing I'm trying to solve with the
> idea of the standard gamepad.  Right now, if I open() something in
> /dev/input that seems like it ought to be a gamepad, I get a giant
> unsorted bag of events that are only comprehensible via a priori
> knowledge.  I keep coming back to the ps3 controller because it's the
> worst offender that I've found; the right stick maps to ABS_Z, ABS_RZ,
> while the XBox controller right stick maps to ABS_RX, ABS_RY.  This is
> not documented anywhere I've found, and I've no idea what quirks other
> controllers have in their mappings or whether they are documented or
> not.

So PS3 gamepad is such a mess because we simply do not have a driver
that maps the buttons properly. IIRC it gets bound to a generic HID
driver that enumerates absolute axis in a very naive fashion. I think
there were some effort in making PS3 work in userspace (in BT mode), but
I do not know if it was ever completed.

So for PS3 what we need is a HID driver providing proper mapping,
nothing more, nothing less.

> 
>     What I'm trying to do is make it so that there are five categories
> of control, in order of usefulness to games:
> 
> 1) known controls that all sane gamepads will have (left stick, dpad,
> NSEW buttons...)
> 2) known controls that most sane gamepads will have (right stick...)
> 3) known controls that many gamepads have (system/mode button, select button...)
> 4) common things (accelerometers...)
> 5) device-specific controls
> 
>     Right now, almost everything is in bucket 5, and from a game
> development point of view that sucks.  The further up the buckets we
> can move a control, the better.
> 
>     So, the problem that I have with "devices that have analog
> triggers report XTRIG, devices with digital triggers report TX" is
> that absent a common mapping I now potentially have to care about the
> difference when I'm developing a game.  It moves these to bucket 3,
> which means as a developer I likely just don't use them unless my game
> really needs them.

Or you have your library that provides ABS->BTN translation.

You can even have your API structured such that consumer provides list
of events it is interested in and the library tries to synthesize
missing events if it can.

> 
> > As I said before, I don't really care. I will send v2 with a patch
> > that introduces these buttons. Lets see what Dmitry thinks.

So we already have:

#define BTN_A                   0x130
#define BTN_B                   0x131
#define BTN_C                   0x132
#define BTN_X                   0x133
#define BTN_Y                   0x134
#define BTN_Z                   0x135
#define BTN_TL                  0x136
#define BTN_TR                  0x137
#define BTN_TL2                 0x138
#define BTN_TR2                 0x139
#define BTN_SELECT              0x13a
#define BTN_START               0x13b
#define BTN_MODE                0x13c
#define BTN_THUMBL              0x13d
#define BTN_THUMBR              0x13e

So if we really want to have BTN_NORTH, etc, we want to alias existing
ones.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
  2013-05-08 17:05             ` Dmitry Torokhov
@ 2013-05-08 17:20               ` Todd Showalter
  2013-05-08 17:25                 ` David Herrmann
  2013-05-08 17:26                 ` Dmitry Torokhov
  0 siblings, 2 replies; 50+ messages in thread
From: Todd Showalter @ 2013-05-08 17:20 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: David Herrmann, open list:HID CORE LAYER, Jiri Kosina

On Wed, May 8, 2013 at 1:05 PM, Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:

> Or you have your library that provides ABS->BTN translation.
>
> You can even have your API structured such that consumer provides list
> of events it is interested in and the library tries to synthesize
> missing events if it can.

    That's in the plans.

> So we already have:
>
> #define BTN_A                   0x130
> #define BTN_B                   0x131
> #define BTN_C                   0x132
> #define BTN_X                   0x133
> #define BTN_Y                   0x134
> #define BTN_Z                   0x135
> #define BTN_TL                  0x136
> #define BTN_TR                  0x137
> #define BTN_TL2                 0x138
> #define BTN_TR2                 0x139
> #define BTN_SELECT              0x13a
> #define BTN_START               0x13b
> #define BTN_MODE                0x13c
> #define BTN_THUMBL              0x13d
> #define BTN_THUMBR              0x13e
>
> So if we really want to have BTN_NORTH, etc, we want to alias existing
> ones.

    The problem is that the alias differs with hardware.  BTN_A would be:

- BTN_SOUTH on xbox gamepads
- BTN_EAST on nintendo gamepads and ouya

    Nintendo is:

A -> WEST
B -> SOUTH
X -> NORTH
Y -> EAST

   While Microsoft is:

A -> SOUTH
B -> EAST
X -> WEST
Y -> NORTH

    They're mirror images of each other. So just aliasing is going to
break something.

                                       Todd.

--
 Todd Showalter, President,
 Electron Jump Games, Inc.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
  2013-05-08 17:20               ` Todd Showalter
@ 2013-05-08 17:25                 ` David Herrmann
  2013-05-08 17:26                 ` Dmitry Torokhov
  1 sibling, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-08 17:25 UTC (permalink / raw)
  To: Todd Showalter; +Cc: Dmitry Torokhov, open list:HID CORE LAYER, Jiri Kosina

Hi Todd

On Wed, May 8, 2013 at 7:20 PM, Todd Showalter <todd@electronjump.com> wrote:
> On Wed, May 8, 2013 at 1:05 PM, Dmitry Torokhov
> <dmitry.torokhov@gmail.com> wrote:
>
>> Or you have your library that provides ABS->BTN translation.
>>
>> You can even have your API structured such that consumer provides list
>> of events it is interested in and the library tries to synthesize
>> missing events if it can.
>
>     That's in the plans.
>
>> So we already have:
>>
>> #define BTN_A                   0x130
>> #define BTN_B                   0x131
>> #define BTN_C                   0x132
>> #define BTN_X                   0x133
>> #define BTN_Y                   0x134
>> #define BTN_Z                   0x135
>> #define BTN_TL                  0x136
>> #define BTN_TR                  0x137
>> #define BTN_TL2                 0x138
>> #define BTN_TR2                 0x139
>> #define BTN_SELECT              0x13a
>> #define BTN_START               0x13b
>> #define BTN_MODE                0x13c
>> #define BTN_THUMBL              0x13d
>> #define BTN_THUMBR              0x13e
>>
>> So if we really want to have BTN_NORTH, etc, we want to alias existing
>> ones.
>
>     The problem is that the alias differs with hardware.  BTN_A would be:
>
> - BTN_SOUTH on xbox gamepads
> - BTN_EAST on nintendo gamepads and ouya
>
>     Nintendo is:
>
> A -> WEST
> B -> SOUTH
> X -> NORTH
> Y -> EAST
>
>    While Microsoft is:
>
> A -> SOUTH
> B -> EAST
> X -> WEST
> Y -> NORTH
>
>     They're mirror images of each other. So just aliasing is going to
> break something.

No. Just ignore BTN_A/B/X/Y, use BTN_EAST/WEST/NORTH/SOUTH instead.
Only for legacy gamepads with wrong mappings you might reuse these. So
we basically overwrite the old names.
So you no longer care for BTN_A/B/X/Y and they will no longer
represent the labels of the buttons, but instead the location.

Regards
David

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
  2013-05-08 17:20               ` Todd Showalter
  2013-05-08 17:25                 ` David Herrmann
@ 2013-05-08 17:26                 ` Dmitry Torokhov
  2013-05-13 17:03                   ` David Herrmann
  1 sibling, 1 reply; 50+ messages in thread
From: Dmitry Torokhov @ 2013-05-08 17:26 UTC (permalink / raw)
  To: Todd Showalter; +Cc: David Herrmann, open list:HID CORE LAYER, Jiri Kosina

On Wed, May 08, 2013 at 01:20:58PM -0400, Todd Showalter wrote:
> On Wed, May 8, 2013 at 1:05 PM, Dmitry Torokhov
> <dmitry.torokhov@gmail.com> wrote:
> 
> > Or you have your library that provides ABS->BTN translation.
> >
> > You can even have your API structured such that consumer provides list
> > of events it is interested in and the library tries to synthesize
> > missing events if it can.
> 
>     That's in the plans.
> 
> > So we already have:
> >
> > #define BTN_A                   0x130
> > #define BTN_B                   0x131
> > #define BTN_C                   0x132
> > #define BTN_X                   0x133
> > #define BTN_Y                   0x134
> > #define BTN_Z                   0x135
> > #define BTN_TL                  0x136
> > #define BTN_TR                  0x137
> > #define BTN_TL2                 0x138
> > #define BTN_TR2                 0x139
> > #define BTN_SELECT              0x13a
> > #define BTN_START               0x13b
> > #define BTN_MODE                0x13c
> > #define BTN_THUMBL              0x13d
> > #define BTN_THUMBR              0x13e
> >
> > So if we really want to have BTN_NORTH, etc, we want to alias existing
> > ones.
> 
>     The problem is that the alias differs with hardware.  BTN_A would be:
> 
> - BTN_SOUTH on xbox gamepads
> - BTN_EAST on nintendo gamepads and ouya
> 
>     Nintendo is:
> 
> A -> WEST
> B -> SOUTH
> X -> NORTH
> Y -> EAST
> 
>    While Microsoft is:
> 
> A -> SOUTH
> B -> EAST
> X -> WEST
> Y -> NORTH
> 
>     They're mirror images of each other. So just aliasing is going to
> break something.

We should take existing in-kernel mapping (that would be XBOX and other
less advanced gamepads in drivers/input/joystick), alias
NORTH/SOUTH/EAST/WEST accordingly, have new users use NSEW button
definitions.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
  2013-05-08 17:26                 ` Dmitry Torokhov
@ 2013-05-13 17:03                   ` David Herrmann
  2013-05-13 17:40                     ` Todd Showalter
  0 siblings, 1 reply; 50+ messages in thread
From: David Herrmann @ 2013-05-13 17:03 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Todd Showalter, open list:HID CORE LAYER, Jiri Kosina

Hi Todd & Dmitry

On Wed, May 8, 2013 at 7:26 PM, Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> We should take existing in-kernel mapping (that would be XBOX and other
> less advanced gamepads in drivers/input/joystick), alias
> NORTH/SOUTH/EAST/WEST accordingly, have new users use NSEW button
> definitions.

I tried to put up some documentation. Comments welcome. If no problems
arise with it, I will send a proper series later.

Regards
David

(As a hint: read this with a monospace font)

diff --git a/Documentation/input/gamepad.txt b/Documentation/input/gamepad.txt
index e69de29..6afb1dc 100644
--- a/Documentation/input/gamepad.txt
+++ b/Documentation/input/gamepad.txt
@@ -0,0 +1,154 @@
+                            Linux Gamepad API
+----------------------------------------------------------------------------
+
+1. Intro
+~~~~~~~~
+Linux provides many different input drivers for gamepad hardware. To avoid
+having user-space deal with different button-mappings for each gamepad, this
+document defines how gamepads are supposed to report their data.
+
+2. Geometry
+~~~~~~~~~~~
+As "gamepad" we define devices which roughly look like this:
+
+            ____________________________              __
+           / [__ZL__]          [__ZR__] \               |
+          / [__ TL __]        [__ TR __] \              | Front Triggers
+       __/________________________________\__         __|
+      /                                  _   \          |
+     /      /\                          (N)   \         |
+    /       ||      __   __   __     _       _ \        | Main Pad
+   |    <===DP===> |SE| |MO| |ST|   (W) -|- (E) |       |
+    \       ||    ___          ___       _     /        |
+    /\      \/   /   \        /   \     (S)   /\      __|
+   /  \________ | LS  | ____ |  RS | ________/  \       |
+  |         /  \ \___/ /    \ \___/ /  \         |      | Control Sticks
+  |        /    \_____/      \_____/    \        |    __|
+  |       /                              \       |
+   \_____/                                \_____/
+
+       |________|______|    |______|___________|
+         D-Pad    Left       Right   Action Pad
+                 Stick       Stick
+
+                   |_____________|
+                      Menu Pad
+
+Most gamepads have the following features:
+  - Action-Pad
+    4 buttons in diamonds-layout (on the right side). The buttons are
+    differently labeled on most devices so we define them as NORTH,
+    SOUTH, WEST and EAST.
+  - D-Pad (Direction-pad)
+    4 buttons (on the left side) that point up, down, left and right.
+  - Menu-Pad
+    Different constellations, but most-times 3 buttons: SELECT - MODE - START
+  - Analog-Sticks
+    Analog-sticks provide freely moveable sticks to control directions. Not
+    all devices have both or any, but they are present at most times.
+    Analog-sticks may also provide a digital button if you press them.
+  - Triggers
+    Triggers are located on the upper-side of the pad in vertical direction.
+    Not all devices provide them, but the upper buttons are normally named
+    Left- and Right-Triggers, the lower buttons Z-Left and Z-Right.
+  - Rumble
+    Many devices provide force-feedback features. But are mostly just
+    simple rumble motors.
+
+3. Detection
+~~~~~~~~~~~~
+All gamepads that follow the protocol described here map BTN_GAMEPAD. This is
+an alias for BTN_NORTH/BTN_A. It can be used to identify a gamepad as such.
+However, not all gamepads provide all features, so you need to test for all
+features that you need, first. How each feature is mapped is described below.
+
+Legacy drivers often don't comply to these rules. As we cannot change them
+for backwards-compatibility reasons, you need to provide fixup mappings in
+user-space yourself. Some of them might also provide module-options that
+change the mappings so you can adivce users to set these.
+
+All new gamepads are supposed to comply with this mapping. Please report any
+bugs, if they don't.
+
+There are a lot of less-featured/less-powerful devices out there, which re-use
+the buttons from this protocol. However, they try to do this in a compatible
+fashion. For example, the "Nintendo Wii Nunchuk" provides two trigger buttons
+and one analog stick. It reports them as if it were a gamepad with only one
+analog stick and two trigger buttons on the right side.
+But that means, that if you only support "real" gamepads, you must test
+devices for _all_ reported events that need. Otherwise, you will also get
+devices that report a small subset of the events.
+
+No other devices, that do not look/feel like a gamepad, shall report these
+events.
+
+4. Events
+~~~~~~~~~
+Gamepads report the following events:
+
+Action-Pad:
+  Every gamepad device has at least 2 action buttons. This means, that every
+  device reports BTN_NORTH (which BTN_GAMEPAD is an alias for). Regardless
+  of the labels on the buttons, the codes are sent according to the
+  physical position of the buttons.
+  Please note that 2- and 3-button pads are fairly rare and old. You might
+  want to filter gamepads that do not report all four.
+    2-Button Pad:
+      If only 2 action-buttons are present, they are reported as BTN_NORTH and
+      BTN_SOUTH. For vertical layouts, the upper button is BTN_NORTH. For
+      horizontal layouts, the button more on the left is BTN_NORTH.
+    3-Button Pad:
+      If only 3 action-buttons are present, they are reported as (from left
+      to right): BTN_WEST, BTN_NORTH, BTN_EAST
+      If the buttons are aligned perfectly vertically, they are reported as
+      (from bottom up): BTN_WEST, BTN_NORTH, BTN_EAST
+    4-Button Pad:
+      If all 4 action-buttons are present, they can be aligned in two
+      different formations. If diamond-shaped, they are reported as BTN_NORTH,
+      BTN_WEST, BTN_SOUTH, BTN_EAST according to their physical location.
+      If rectangular-shaped, the upper-left button is BTN_NORTH, lower-left
+      is BTN_WEST, lower-right is BTN_SOUTH and upper-right is BTN_EAST.
+
+D-Pad:
+  Every gamepad provides a D-Pad with four directions: Up, Down, Left, Right
+  Some of these are available as digital buttons, some as analog buttons. Some
+  may even report both. The kernel does not convert between these so
+  applications should support both and choose what is more appropriate if
+  both are reported.
+  Digital buttons are reported as BTN_TRIGGER_HAPPY_X, with:
+    Left is BTN_TRIGGER_HAPPY1, right is BTN_TRIGGER_HAPPY2, up is
+    BTN_TRIGGER_HAPPY3 and down is BTN_TRIGGER_HAPPY4.
+  Analog buttons are reported as:
+    ABS_HAT0X and ABS_HAT0Y
+
+Analog-Sticks:
+  The first (left) analog-stick is reported as ABS_X, ABS_Y. The second (right)
+  analog stick is reported as ABS_RX, ABS_RY. If only one stick is present,
+  ABS_RX and ABS_RY will be unmapped.
+  If analog-sticks provide digital buttons, they are mapped accordingly as
+  BTN_THUMBL (first/left) and BTN_THUMBR (second/right).
+
+Triggers:
+  Trigger buttons can be available as digital or analog buttons or both. User-
+  space must correctly deal with any situation and choose the most appropriate
+  mode.
+  Upper trigger buttons are reported as BTN_TR or ABS_HAT1X (right) and BTN_TL
+  or ABS_HAT1Y (left). Lower trigger buttons are reported as BTN_TR2 or
+  ABS_HAT2X (right/ZR) and BTN_TL2 or ABS_HAT2Y (left/ZL).
+  If only one trigger-button combination is present (upper+lower), they are
+  reported as "right" triggers (BTN_TR/ABS_HAT1X).
+
+Menu-Pad:
+  Menu buttons are always digital and are mapped according to their location
+  instead of their labels. That is:
+    1-button Pad: Mapped as BTN_START
+    2-button Pad: Left button mapped as BTN_SELECT, right button mapped as
+                  BTN_START
+    3-button Pad: Mapped (from left to right) as BTN_SELECT, BTN_MODE,
+                  BTN_START
+
+Rumble:
+  Rumble is advertices as FF_RUMBLE.
+
+----------------------------------------------------------------------------
+  Written 2013 by David Herrmann <dh.herrmann@gmail.com>
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index 935119c..f6c18b2 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -507,10 +507,14 @@ struct input_keymap_entry {

 #define BTN_GAMEPAD 0x130
 #define BTN_A 0x130
+#define BTN_NORTH 0x130
 #define BTN_B 0x131
+#define BTN_SOUTH 0x131
 #define BTN_C 0x132
 #define BTN_X 0x133
+#define BTN_EAST 0x133
 #define BTN_Y 0x134
+#define BTN_WEST 0x134
 #define BTN_Z 0x135
 #define BTN_TL 0x136
 #define BTN_TR 0x137

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
  2013-05-13 17:03                   ` David Herrmann
@ 2013-05-13 17:40                     ` Todd Showalter
  2013-05-22 12:10                       ` David Herrmann
  0 siblings, 1 reply; 50+ messages in thread
From: Todd Showalter @ 2013-05-13 17:40 UTC (permalink / raw)
  To: David Herrmann; +Cc: Dmitry Torokhov, open list:HID CORE LAYER, Jiri Kosina

On Mon, May 13, 2013 at 1:03 PM, David Herrmann <dh.herrmann@gmail.com> wrote:

> I tried to put up some documentation. Comments welcome. If no problems
> arise with it, I will send a proper series later.

    I'd be inclined to add something about the mode button; it's
somewhat special.  Not special in the sense of anything magical
happening in the hardware, just special in the sense that it's the
control that isn't game related.  The hardware that has something that
maps to MODE generally intends the button to mean "pause the game and
let me interact with the OS.".  That's certainly the case for the PS
button on the sixaxis, the backlit X button on the 360 controller and
the home button on the Wii controllers.

    Force feedback is a land of crawling madness and chaos; I'm not
sure there's much right now that can be done to unify it.  You've got
devices with linear motors, rotary motors, speakers, resistance
systems, and combinations of those.  The motor systems are mounted at
different angles in different hardware.  You've potentially got
strange power limitations; there's hardware out there where the
combined draw of all the feedback motors is more than the cable
supplies to the gamepad, so you can't run them all at the same time.
You've got sound feedback vs. rumble feedback vs. active feedback (ie:
the joystick or wheel is actively pushing back against you or trying
to pull you around).  I'd love a unified force feedback system, but
there's so little commonality between devices that I'm not sure
there's any useful set to abstract over.

    I'd be inclined to make two button systems map to BTN_SOUTH and
BTN_EAST; those are the two button that fall most easily under your
thumb with a 4 button gamepad.  BTN_NORTH and BTN_WEST require you to
reach over the other two buttons, which is fine for most people but
less ideal for people with smaller hands.  Even people with large
hands usually find SOUTH and EAST the easiest to reach, so for most
games (at least in my experience) those two buttons wind up being
mapped to the actions we expect people to do the most.  A game might
still have to remap (or throw its hands up and tell you that a two
button controller means you need to keep the keyboard nearby), but I
think making BTN_SOUTH and BTN_EAST the defaults on a 2 button
controller (and adding NORTH or WEST for 3) will be most likely to
work out of the box with most games.

    For the cases where you have buttons in a square pattern rather
than a diamond, yes, I guess we just have to pick a 45 degree rotation
of the compass and live with that.  Having SOUTH and EAST along the
right side makes as much sense as the alternative, so I'm good with
this.

    Could we do explicit BTN_DPAD_UP/DOWN/LEFT/RIGHT?  It feels like
the HAPPY buttons are placeholder defines.

    On the menu pad, I think in practice there's quite often a button
labelled "start".  Granted, it's usually on the right, but see (for
example) the original xbox S controller, where "start" is indeed the
button on the right hand side (and "back" is the one on the left), but
the actual "menu pad" is to the left of the dpad.

    I'd prefer if things were mapped to a pair of digital buttons and
a pair of analog linear pot triggers for the shoulders; that covers a
lot of existing hardware (the xbox series gamepads, wii classic
gamepad, and playstation series gamepad can all be made to fit this
model without undue violence).

    Overall: My complaints are largely minor; there are some tweaks
I'd appreciate, but there's nothing fundamentally wrong here.

                                            Todd.

--
 Todd Showalter, President,
 Electron Jump Games, Inc.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
  2013-05-13 17:40                     ` Todd Showalter
@ 2013-05-22 12:10                       ` David Herrmann
  2013-05-22 16:18                         ` Todd Showalter
  0 siblings, 1 reply; 50+ messages in thread
From: David Herrmann @ 2013-05-22 12:10 UTC (permalink / raw)
  To: Todd Showalter; +Cc: Dmitry Torokhov, open list:HID CORE LAYER, Jiri Kosina

Hi

On Mon, May 13, 2013 at 7:40 PM, Todd Showalter <todd@electronjump.com> wrote:
> On Mon, May 13, 2013 at 1:03 PM, David Herrmann <dh.herrmann@gmail.com> wrote:
>
>> I tried to put up some documentation. Comments welcome. If no problems
>> arise with it, I will send a proper series later.
>
>     I'd be inclined to add something about the mode button; it's
> somewhat special.  Not special in the sense of anything magical
> happening in the hardware, just special in the sense that it's the
> control that isn't game related.  The hardware that has something that
> maps to MODE generally intends the button to mean "pause the game and
> let me interact with the OS.".  That's certainly the case for the PS
> button on the sixaxis, the backlit X button on the 360 controller and
> the home button on the Wii controllers.

Sure, I will add a note that this is more like a "system button", than
a button an application should use for normal operations. See below.

>     Force feedback is a land of crawling madness and chaos; I'm not
> sure there's much right now that can be done to unify it.  You've got
> devices with linear motors, rotary motors, speakers, resistance
> systems, and combinations of those.  The motor systems are mounted at
> different angles in different hardware.  You've potentially got
> strange power limitations; there's hardware out there where the
> combined draw of all the feedback motors is more than the cable
> supplies to the gamepad, so you can't run them all at the same time.
> You've got sound feedback vs. rumble feedback vs. active feedback (ie:
> the joystick or wheel is actively pushing back against you or trying
> to pull you around).  I'd love a unified force feedback system, but
> there's so little commonality between devices that I'm not sure
> there's any useful set to abstract over.

I think the kernel provides a pretty nice FF interface. It even
provides emulations for the different kinds of FF if not all are
supported by hardware. Anyway, that's beyond the scope of this
definition and mostly belongs to joysticks. Gamepads normally only
have rumble-motors, don't they?

>     I'd be inclined to make two button systems map to BTN_SOUTH and
> BTN_EAST; those are the two button that fall most easily under your
> thumb with a 4 button gamepad.  BTN_NORTH and BTN_WEST require you to
> reach over the other two buttons, which is fine for most people but
> less ideal for people with smaller hands.  Even people with large
> hands usually find SOUTH and EAST the easiest to reach, so for most
> games (at least in my experience) those two buttons wind up being
> mapped to the actions we expect people to do the most.  A game might
> still have to remap (or throw its hands up and tell you that a two
> button controller means you need to keep the keyboard nearby), but I
> think making BTN_SOUTH and BTN_EAST the defaults on a 2 button
> controller (and adding NORTH or WEST for 3) will be most likely to
> work out of the box with most games.

I wanted to have BTN_NORTH/A/GAMEPAD mapped for all gamepads, but
you're right, the commercial systems use SOUTH/EAST for all trivial
operations. I will change this and see whether we can get another key
that is always mapped (probably BTN_SOUTH? and make this an alias for
BTN_GAMEPAD).

>     For the cases where you have buttons in a square pattern rather
> than a diamond, yes, I guess we just have to pick a 45 degree rotation
> of the compass and live with that.  Having SOUTH and EAST along the
> right side makes as much sense as the alternative, so I'm good with
> this.
>
>     Could we do explicit BTN_DPAD_UP/DOWN/LEFT/RIGHT?  It feels like
> the HAPPY buttons are placeholder defines.

Yep, I added them now.

>     On the menu pad, I think in practice there's quite often a button
> labelled "start".  Granted, it's usually on the right, but see (for
> example) the original xbox S controller, where "start" is indeed the
> button on the right hand side (and "back" is the one on the left), but
> the actual "menu pad" is to the left of the dpad.

I changed the menu-pad definition a little bit. 1-button PADs map to
BTN_START, 2-button pads to START and SELECT. BTN_MODE is no longer
mapped as 3rd button but instead is a special button to acknowledge
the fact that it is mostly some fancy branded button that isn't mapped
by applications.
So the menu-pad no longer _has_ to be in the middle, but I still map
the right button to START and the left to SELECT to keep the "map by
position, not by label" definition.

>     I'd prefer if things were mapped to a pair of digital buttons and
> a pair of analog linear pot triggers for the shoulders; that covers a
> lot of existing hardware (the xbox series gamepads, wii classic
> gamepad, and playstation series gamepad can all be made to fit this
> model without undue violence).

Ugh? I don't understand what you mean here.

>     Overall: My complaints are largely minor; there are some tweaks
> I'd appreciate, but there's nothing fundamentally wrong here.

Thanks a lot! Unfortunately, I don't have all hardware at home so I
can only test the Nintendo devices. However, I am trying to assemble a
list of supported gamepads and how they are mapped. This will give us
a better overview and we can see whether this definition actually
makes sense.

I will send a proper series soon. Thanks for your comments!
David

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
  2013-05-22 12:10                       ` David Herrmann
@ 2013-05-22 16:18                         ` Todd Showalter
  0 siblings, 0 replies; 50+ messages in thread
From: Todd Showalter @ 2013-05-22 16:18 UTC (permalink / raw)
  To: David Herrmann; +Cc: Dmitry Torokhov, open list:HID CORE LAYER, Jiri Kosina

On Wed, May 22, 2013 at 8:10 AM, David Herrmann <dh.herrmann@gmail.com> wrote:

> I think the kernel provides a pretty nice FF interface. It even
> provides emulations for the different kinds of FF if not all are
> supported by hardware. Anyway, that's beyond the scope of this
> definition and mostly belongs to joysticks. Gamepads normally only
> have rumble-motors, don't they?

    From an end-use point of view, (at least on casual reading, which
is as much as I can claim to have done at this point), the problem
with the force feedback API is that you find out what something is by
enumerating its capabilities, but the capabilities aren't specific
enough.

    Let's say I have a controller with two rotary motors.  I can
examine the FF caps and see that there are two motors, and possibly
even infer a little about them, but I don't think I can tell what
direction they are mounted in and how strong they are.

    There were some games on the PS2/PS3 that did some clever tricks
with force feedback; subtle stuff like making the controller fake a
gentle heartbeat that got faster and more uneven the more hurt your
character got, or gave feedback in time to the soundtrack.  To do that
really effectively, you need to understand the power, positioning,
response speed and orientation of the force feedback device.  Without
that complex understanding all you can really do is buzz or thump in a
fairly generic way.

> I wanted to have BTN_NORTH/A/GAMEPAD mapped for all gamepads, but
> you're right, the commercial systems use SOUTH/EAST for all trivial
> operations. I will change this and see whether we can get another key
> that is always mapped (probably BTN_SOUTH? and make this an alias for
> BTN_GAMEPAD).

    Sounds good.

> I changed the menu-pad definition a little bit. 1-button PADs map to
> BTN_START, 2-button pads to START and SELECT. BTN_MODE is no longer
> mapped as 3rd button but instead is a special button to acknowledge
> the fact that it is mostly some fancy branded button that isn't mapped
> by applications.
> So the menu-pad no longer _has_ to be in the middle, but I still map
> the right button to START and the left to SELECT to keep the "map by
> position, not by label" definition.

    Ok.

>>     I'd prefer if things were mapped to a pair of digital buttons and
>> a pair of analog linear pot triggers for the shoulders; that covers a
>> lot of existing hardware (the xbox series gamepads, wii classic
>> gamepad, and playstation series gamepad can all be made to fit this
>> model without undue violence).
>
> Ugh? I don't understand what you mean here.

    If you look at (for example) the XBox 360 controller, it has two
digital shoulder buttons (LB and RB, IIRC) and two analog shoulder
triggers (LT and RT).  The Wii Classic controller has the same; L and
R are analog, ZL and ZR are digital.  The PS series controllers have
four pressure sensitive buttons (L1, L2, R1, R2) which can be digital
or analog depending on whether you read the button bit or the pressure
sensor byte.  Other controllers all map onto this model reasonably
well.

    It's not ideal, but it's a fairly close.

> Thanks a lot! Unfortunately, I don't have all hardware at home so I
> can only test the Nintendo devices. However, I am trying to assemble a
> list of supported gamepads and how they are mapped. This will give us
> a better overview and we can see whether this definition actually
> makes sense.

    I've an original XBox S controller (with a Lik Sang USB adapter),
an XBox 360 wired controller (Microsoft-branded), a PS3 controller
(Sony-branded, but has to be plugged in since I don't have bluetooth),
and can probably dig up my gamecube/ps2/dreamcast USB adapter if given
time.

                                           Todd.

--
 Todd Showalter, President,
 Electron Jump Games, Inc.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 13/26] HID: wiimote: add extension hotplug support
  2013-05-05 21:12 ` [PATCH 13/26] HID: wiimote: add extension hotplug support David Herrmann
@ 2013-05-23 10:53   ` David Herrmann
  2013-05-23 12:28     ` Jiri Kosina
  0 siblings, 1 reply; 50+ messages in thread
From: David Herrmann @ 2013-05-23 10:53 UTC (permalink / raw)
  To: open list:HID CORE LAYER; +Cc: Jiri Kosina, David Herrmann

Hi Jiri

On Sun, May 5, 2013 at 11:12 PM, David Herrmann <dh.herrmann@gmail.com> wrote:
> 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: David Herrmann <dh.herrmann@gmail.com>
> ---
>  drivers/hid/hid-wiimote-core.c    | 669 ++++++++++++++++++++++++++++++++++++--
>  drivers/hid/hid-wiimote-modules.c |  16 +
>  drivers/hid/hid-wiimote.h         |  49 +++
>  3 files changed, 709 insertions(+), 25 deletions(-)
>
> diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
> index 1ea70c8..836611e 100644
> --- a/drivers/hid/hid-wiimote-core.c
> +++ b/drivers/hid/hid-wiimote-core.c
> @@ -179,17 +179,38 @@ 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;
> +
> +       ext = (wdata->state.flags & WIIPROTO_FLAG_EXT_USED) ||
> +             (wdata->state.flags & WIIPROTO_FLAG_MP_USED);
>
>         if (ir == WIIPROTO_FLAG_IR_BASIC) {
> -               if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
> -                       return WIIPROTO_REQ_DRM_KAIE;
> -               else
> +               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) {
> @@ -202,7 +223,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;
>                 }
> @@ -399,9 +420,8 @@ static int wiimote_cmd_init_ext(struct wiimote_data *wdata)
>  }
>
>  /* requires the cmd-mutex to be held */
> -static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata)
> +static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem)
>  {
> -       __u8 rmem[6];
>         int ret;
>
>         /* read extension ID */
> @@ -409,6 +429,9 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata)
>         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;
> @@ -416,6 +439,92 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata)
>         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;
> +
> +       /* 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) {
> +       default:
> +               wmem = 0x04;
> +               break;
> +       }
> +
> +       return wiimote_cmd_write(wdata, 0xa600fe, &wmem, sizeof(wmem));
> +}
> +
> +/* requires the cmd-mutex to be held */
> +static bool wiimote_cmd_read_mp(struct wiimote_data *wdata, __u8 *rmem)
> +{
> +       int ret;
> +
> +       /* read motion plus ID */
> +       ret = wiimote_cmd_read(wdata, 0xa600fa, rmem, 6);
> +       if (ret != 6)
> +               return false;
> +
> +       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 (rmem[5] == 0x05)
> +               return true;
> +
> +       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]);
> +
> +       return false;
> +}
> +
> +/* requires the cmd-mutex to be held */
> +static __u8 wiimote_cmd_read_mp_mapped(struct wiimote_data *wdata)
> +{
> +       int ret;
> +       __u8 rmem[6];
> +
> +       /* 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] = {
> @@ -561,6 +670,81 @@ static void wiimote_modules_unload(struct wiimote_data *wdata)
>         }
>  }
>
> +/* device extension handling */
> +
> +static void wiimote_ext_load(struct wiimote_data *wdata, unsigned int ext)
> +{
> +       unsigned long flags;
> +       const struct wiimod_ops *ops;
> +       int ret;
> +
> +       ops = wiimod_ext_table[ext];
> +
> +       if (ops->probe) {
> +               ret = ops->probe(ops, wdata);
> +               if (ret)
> +                       ext = WIIMOTE_EXT_UNKNOWN;
> +       }
> +
> +       spin_lock_irqsave(&wdata->state.lock, flags);
> +       wdata->state.exttype = ext;
> +       spin_unlock_irqrestore(&wdata->state.lock, flags);
> +}
> +
> +static void wiimote_ext_unload(struct wiimote_data *wdata)
> +{
> +       unsigned long flags;
> +       const struct wiimod_ops *ops;
> +
> +       ops = wiimod_ext_table[wdata->state.exttype];
> +
> +       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 void wiimote_mp_unload(struct wiimote_data *wdata)
> +{
> +       unsigned long flags;
> +       const struct wiimod_ops *ops;
> +
> +       if (wdata->state.mp < 2)
> +               return;
> +
> +       ops = &wiimod_mp;
> +
> +       spin_lock_irqsave(&wdata->state.lock, flags);
> +       wdata->state.mp = 0;
> +       wdata->state.flags &= ~WIIPROTO_FLAG_MP_USED;
> +       spin_unlock_irqrestore(&wdata->state.lock, flags);
> +
> +       if (ops->remove)
> +               ops->remove(ops, wdata);
> +}
> +
>  /* device (re-)initialization and detection */
>
>  static const char *wiimote_devtype_names[WIIMOTE_DEV_NUM] = {
> @@ -617,7 +801,7 @@ done:
>
>  static void wiimote_init_detect(struct wiimote_data *wdata)
>  {
> -       __u8 exttype = WIIMOTE_EXT_NONE;
> +       __u8 exttype = WIIMOTE_EXT_NONE, extdata[6];
>         bool ext;
>         int ret;
>
> @@ -641,11 +825,301 @@ static void wiimote_init_detect(struct wiimote_data *wdata)
>                 goto out_release;
>
>         wiimote_cmd_init_ext(wdata);
> -       exttype = wiimote_cmd_read_ext(wdata);
> +       exttype = wiimote_cmd_read_ext(wdata, extdata);
>
>  out_release:
>         wiimote_cmd_release(wdata);
>         wiimote_init_set_type(wdata, exttype);
> +       /* schedule MP timer */
> +       mod_timer(&wdata->timer, jiffies + HZ * 4);
> +}
> +
> +/*
> + * 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)
> +{
> +       bool mp;
> +       __u8 mpdata[6];
> +
> +       wiimote_cmd_acquire_noint(wdata);
> +       wiimote_cmd_init_mp(wdata);
> +       mp = wiimote_cmd_read_mp(wdata, mpdata);
> +       wiimote_cmd_release(wdata);
> +
> +       /* 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);
> +}
> +
> +/*
> + * 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)
> +{
> +       __u32 flags;
> +       __u8 type, data[6];
> +       bool ret, poll_mp;
> +
> +       spin_lock_irq(&wdata->state.lock);
> +       flags = wdata->state.flags;
> +       spin_unlock_irq(&wdata->state.lock);
> +
> +       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;
> +       }
> +
> +       /* 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 */
> +       ret = false;
> +
> +out_release:
> +       wiimote_cmd_release(wdata);
> +
> +       /* only poll for MP if requested and if state didn't change */
> +       if (ret && poll_mp)
> +               wiimote_init_poll_mp(wdata);
> +
> +       return ret;
> +}
> +
> +static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = {
> +       [WIIMOTE_EXT_NONE] = "None",
> +       [WIIMOTE_EXT_UNKNOWN] = "Unknown",
> +};
> +
> +/*
> + * Handle hotplug events
> + * If we receive an hotplug event and the device-check failed, we deinitialize
> + * the extension ports, re-read all extension IDs and set the device into
> + * the desired state. This involves mapping MP into the main extension
> + * registers, setting up extension passthrough modes and initializing the
> + * requested extensions.
> + */
> +static void wiimote_init_hotplug(struct wiimote_data *wdata)
> +{
> +       __u8 exttype, extdata[6], mpdata[6];
> +       __u32 flags;
> +       bool mp;
> +
> +       hid_dbg(wdata->hdev, "detect extensions..\n");
> +
> +       wiimote_cmd_acquire_noint(wdata);
> +
> +       spin_lock_irq(&wdata->state.lock);
> +
> +       /* get state snapshot that we will then work on */
> +       flags = wdata->state.flags;
> +
> +       /* disable event forwarding temporarily */
> +       wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE;
> +       wdata->state.flags &= ~WIIPROTO_FLAG_MP_ACTIVE;
> +
> +       spin_unlock_irq(&wdata->state.lock);
> +
> +       /* init extension and MP (deactivates current extension or MP) */
> +       wiimote_cmd_init_ext(wdata);
> +       wiimote_cmd_init_mp(wdata);
> +       mp = wiimote_cmd_read_mp(wdata, mpdata);
> +       exttype = wiimote_cmd_read_ext(wdata, extdata);
> +
> +       wiimote_cmd_release(wdata);
> +
> +       /* load/unload extension module if it changed */
> +       if (exttype != wdata->state.exttype) {
> +               /* unload previous extension */
> +               wiimote_ext_unload(wdata);
> +
> +               if (exttype == WIIMOTE_EXT_UNKNOWN) {
> +                       hid_info(wdata->hdev, "cannot detect extension; %02x:%02x %02x:%02x %02x:%02x\n",
> +                                extdata[0], extdata[1], extdata[2],
> +                                extdata[3], extdata[4], extdata[5]);
> +               } else if (exttype == WIIMOTE_EXT_NONE) {
> +                       spin_lock_irq(&wdata->state.lock);
> +                       wdata->state.exttype = WIIMOTE_EXT_NONE;
> +                       spin_unlock_irq(&wdata->state.lock);
> +               } else {
> +                       hid_info(wdata->hdev, "detected extension: %s\n",
> +                                wiimote_exttype_names[exttype]);
> +                       /* try loading new extension */
> +                       wiimote_ext_load(wdata, exttype);
> +               }
> +       }
> +
> +       /* 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);
> +       }
> +
> +       /* if MP is not used, do not map or activate it */
> +       if (!(flags & WIIPROTO_FLAG_MP_USED))
> +               mp = false;
> +
> +       /* map MP into main extension registers if used */
> +       if (mp) {
> +               wiimote_cmd_acquire_noint(wdata);
> +               wiimote_cmd_map_mp(wdata, exttype);
> +               wiimote_cmd_release(wdata);
> +
> +               /* delete MP hotplug timer */
> +               del_timer_sync(&wdata->timer);
> +       } else {
> +               /* reschedule MP hotplug timer */
> +               mod_timer(&wdata->timer, jiffies + HZ * 4);
> +       }
> +
> +       spin_lock_irq(&wdata->state.lock);
> +
> +       /* enable data forwarding again and set expected hotplug state */
> +       if (mp) {
> +               wdata->state.flags |= WIIPROTO_FLAG_MP_ACTIVE;
> +               if (wdata->state.exttype == WIIMOTE_EXT_NONE) {
> +                       wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
> +                       wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED;
> +               } else {
> +                       wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
> +                       wdata->state.flags |= WIIPROTO_FLAG_MP_PLUGGED;
> +                       wdata->state.flags |= WIIPROTO_FLAG_EXT_ACTIVE;
> +               }
> +       } else if (wdata->state.exttype != WIIMOTE_EXT_NONE) {
> +               wdata->state.flags |= WIIPROTO_FLAG_EXT_ACTIVE;
> +       }
> +
> +       /* request status report for hotplug state updates */
> +       wiiproto_req_status(wdata);
> +
> +       spin_unlock_irq(&wdata->state.lock);
> +
> +       hid_dbg(wdata->hdev, "detected extensions: MP: %d EXT: %d\n",
> +               wdata->state.mp, wdata->state.exttype);
>  }
>
>  static void wiimote_init_worker(struct work_struct *work)
> @@ -655,6 +1129,30 @@ static void wiimote_init_worker(struct work_struct *work)
>
>         if (wdata->state.devtype == WIIMOTE_DEV_PENDING)
>                 wiimote_init_detect(wdata);

We need an additional "wiimote_init_hotplug(wdata);" in this if-clause
for devices with built-in MP to detect it right during init.
Otherwise, MP is not initialized until you plug some other extension.
That's because the MP-timer is disabled for devices with built-in MP.
Other than that I didn't get any bug-reports. And I haven't found any
bugs, either.

I want to avoid resending the whole series, so I will send a fixup
patch on top of your branch once you applied it (if that is ok?).

Cheers
David

> +       if (!wiimote_init_check(wdata))
> +               wiimote_init_hotplug(wdata);
> +}
> +
> +void __wiimote_schedule(struct wiimote_data *wdata)
> +{
> +       if (!(wdata->state.flags & WIIPROTO_FLAG_EXITING))
> +               schedule_work(&wdata->init_worker);
> +}
> +
> +static void wiimote_schedule(struct wiimote_data *wdata)
> +{
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&wdata->state.lock, flags);
> +       __wiimote_schedule(wdata);
> +       spin_unlock_irqrestore(&wdata->state.lock, flags);
> +}
> +
> +static void wiimote_init_timeout(unsigned long arg)
> +{
> +       struct wiimote_data *wdata = (void*)arg;
> +
> +       wiimote_schedule(wdata);
>  }
>
>  /* protocol handlers */
> @@ -664,6 +1162,12 @@ static void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
>         const __u8 *iter, *mods;
>         const struct wiimod_ops *ops;
>
> +       ops = wiimod_ext_table[wdata->state.exttype];
> +       if (ops->in_keys) {
> +               ops->in_keys(wdata, payload);
> +               return;
> +       }
> +
>         mods = wiimote_devtype_mods[wdata->state.devtype];
>         for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
>                 ops = wiimod_table[*iter];
> @@ -679,6 +1183,12 @@ static void handler_accel(struct wiimote_data *wdata, const __u8 *payload)
>         const __u8 *iter, *mods;
>         const struct wiimod_ops *ops;
>
> +       ops = wiimod_ext_table[wdata->state.exttype];
> +       if (ops->in_accel) {
> +               ops->in_accel(wdata, payload);
> +               return;
> +       }
> +
>         mods = wiimote_devtype_mods[wdata->state.devtype];
>         for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
>                 ops = wiimod_table[*iter];
> @@ -689,6 +1199,93 @@ static void handler_accel(struct wiimote_data *wdata, const __u8 *payload)
>         }
>  }
>
> +static bool valid_ext_handler(const struct wiimod_ops *ops, size_t len)
> +{
> +       if (!ops->in_ext)
> +               return false;
> +       if ((ops->flags & WIIMOD_FLAG_EXT8) && len < 8)
> +               return false;
> +       if ((ops->flags & WIIMOD_FLAG_EXT16) && len < 16)
> +               return false;
> +
> +       return true;
> +}
> +
> +static void handler_ext(struct wiimote_data *wdata, const __u8 *payload,
> +                       size_t len)
> +{
> +       const __u8 *iter, *mods;
> +       const struct wiimod_ops *ops;
> +       bool is_mp;
> +
> +       if (len < 6)
> +               return;
> +
> +       /* if MP is active, track MP slot hotplugging */
> +       if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
> +               /* this bit is set for invalid events (eg. during hotplug) */
> +               if (payload[5] & 0x01)
> +                       return;
> +
> +               if (payload[4] & 0x01) {
> +                       if (!(wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED)) {
> +                               hid_dbg(wdata->hdev, "MP hotplug: 1\n");
> +                               wdata->state.flags |= WIIPROTO_FLAG_MP_PLUGGED;
> +                               __wiimote_schedule(wdata);
> +                       }
> +               } else {
> +                       if (wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED) {
> +                               hid_dbg(wdata->hdev, "MP hotplug: 0\n");
> +                               wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED;
> +                               wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE;
> +                               __wiimote_schedule(wdata);
> +                       }
> +               }
> +
> +               /* detect MP data that is sent interleaved with EXT data */
> +               is_mp = payload[5] & 0x02;
> +       } else {
> +               is_mp = false;
> +       }
> +
> +       /* ignore EXT events if no extension is active */
> +       if (!(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE) && !is_mp)
> +               return;
> +
> +       /* try forwarding to extension handler, first */
> +       ops = wiimod_ext_table[wdata->state.exttype];
> +       if (is_mp && ops->in_mp) {
> +               ops->in_mp(wdata, payload);
> +               return;
> +       } else if (!is_mp && valid_ext_handler(ops, len)) {
> +               ops->in_ext(wdata, payload);
> +               return;
> +       }
> +
> +       /* try forwarding to MP handler */
> +       ops = &wiimod_mp;
> +       if (is_mp && ops->in_mp) {
> +               ops->in_mp(wdata, payload);
> +               return;
> +       } else if (!is_mp && valid_ext_handler(ops, len)) {
> +               ops->in_ext(wdata, payload);
> +               return;
> +       }
> +
> +       /* try forwarding to loaded modules */
> +       mods = wiimote_devtype_mods[wdata->state.devtype];
> +       for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
> +               ops = wiimod_table[*iter];
> +               if (is_mp && ops->in_mp) {
> +                       ops->in_mp(wdata, payload);
> +                       return;
> +               } else if (!is_mp && valid_ext_handler(ops, len)) {
> +                       ops->in_ext(wdata, payload);
> +                       return;
> +               }
> +       }
> +}
> +
>  #define ir_to_input0(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 0)
>  #define ir_to_input1(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 1)
>  #define ir_to_input2(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 2)
> @@ -700,6 +1297,12 @@ static void handler_ir(struct wiimote_data *wdata, const __u8 *payload,
>         const __u8 *iter, *mods;
>         const struct wiimod_ops *ops;
>
> +       ops = wiimod_ext_table[wdata->state.exttype];
> +       if (ops->in_ir) {
> +               ops->in_ir(wdata, payload, packed, id);
> +               return;
> +       }
> +
>         mods = wiimote_devtype_mods[wdata->state.devtype];
>         for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
>                 ops = wiimod_table[*iter];
> @@ -727,11 +1330,20 @@ static void handler_status(struct wiimote_data *wdata, const __u8 *payload)
>
>         /* update extension status */
>         if (payload[2] & 0x02) {
> -               wdata->state.flags |= WIIPROTO_FLAG_EXT_PLUGGED;
> -               wiiext_event(wdata, true);
> +               if (!(wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED)) {
> +                       hid_dbg(wdata->hdev, "EXT hotplug: 1\n");
> +                       wdata->state.flags |= WIIPROTO_FLAG_EXT_PLUGGED;
> +                       __wiimote_schedule(wdata);
> +               }
>         } else {
> -               wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
> -               wiiext_event(wdata, false);
> +               if (wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED) {
> +                       hid_dbg(wdata->hdev, "EXT hotplug: 0\n");
> +                       wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
> +                       wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED;
> +                       wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE;
> +                       wdata->state.flags &= ~WIIPROTO_FLAG_MP_ACTIVE;
> +                       __wiimote_schedule(wdata);
> +               }
>         }
>
>         wdata->state.cmd_battery = payload[5];
> @@ -791,7 +1403,7 @@ static void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload)
>  static void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload)
>  {
>         handler_keys(wdata, payload);
> -       wiiext_handle(wdata, &payload[2]);
> +       handler_ext(wdata, &payload[2], 8);
>  }
>
>  static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload)
> @@ -807,7 +1419,7 @@ static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload)
>  static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload)
>  {
>         handler_keys(wdata, payload);
> -       wiiext_handle(wdata, &payload[2]);
> +       handler_ext(wdata, &payload[2], 19);
>  }
>
>  static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload)
> @@ -817,14 +1429,14 @@ static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload)
>         ir_to_input1(wdata, &payload[4], true);
>         ir_to_input2(wdata, &payload[7], false);
>         ir_to_input3(wdata, &payload[9], true);
> -       wiiext_handle(wdata, &payload[12]);
> +       handler_ext(wdata, &payload[12], 9);
>  }
>
>  static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload)
>  {
>         handler_keys(wdata, payload);
>         handler_accel(wdata, payload);
> -       wiiext_handle(wdata, &payload[5]);
> +       handler_ext(wdata, &payload[5], 16);
>  }
>
>  static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload)
> @@ -835,12 +1447,12 @@ static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload)
>         ir_to_input1(wdata, &payload[7], true);
>         ir_to_input2(wdata, &payload[10], false);
>         ir_to_input3(wdata, &payload[12], true);
> -       wiiext_handle(wdata, &payload[15]);
> +       handler_ext(wdata, &payload[15], 6);
>  }
>
>  static void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload)
>  {
> -       wiiext_handle(wdata, payload);
> +       handler_ext(wdata, payload, 21);
>  }
>
>  static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload)
> @@ -960,16 +1572,27 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
>         wdata->state.cmd_battery = 0xff;
>
>         INIT_WORK(&wdata->init_worker, wiimote_init_worker);
> +       setup_timer(&wdata->timer, wiimote_init_timeout, (long)wdata);
>
>         return wdata;
>  }
>
>  static void wiimote_destroy(struct wiimote_data *wdata)
>  {
> +       unsigned long flags;
> +
>         wiidebug_deinit(wdata);
> -       wiiext_deinit(wdata);
> +
> +       /* prevent init_worker from being scheduled again */
> +       spin_lock_irqsave(&wdata->state.lock, flags);
> +       wdata->state.flags |= WIIPROTO_FLAG_EXITING;
> +       spin_unlock_irqrestore(&wdata->state.lock, flags);
>
>         cancel_work_sync(&wdata->init_worker);
> +       del_timer_sync(&wdata->timer);
> +
> +       wiimote_mp_unload(wdata);
> +       wiimote_ext_unload(wdata);
>         wiimote_modules_unload(wdata);
>         cancel_work_sync(&wdata->queue.worker);
>         hid_hw_close(wdata->hdev);
> @@ -1010,10 +1633,6 @@ static int wiimote_hid_probe(struct hid_device *hdev,
>                 goto err_stop;
>         }
>
> -       ret = wiiext_init(wdata);
> -       if (ret)
> -               goto err_free;
> -
>         ret = wiidebug_init(wdata);
>         if (ret)
>                 goto err_free;
> @@ -1021,7 +1640,7 @@ static int wiimote_hid_probe(struct hid_device *hdev,
>         hid_info(hdev, "New device registered\n");
>
>         /* schedule device detection */
> -       schedule_work(&wdata->init_worker);
> +       wiimote_schedule(wdata);
>
>         return 0;
>
> diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
> index 5cc593a..d3eef77 100644
> --- a/drivers/hid/hid-wiimote-modules.c
> +++ b/drivers/hid/hid-wiimote-modules.c
> @@ -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,
> +};
> diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
> index 3a2d3a1..0afc9f9 100644
> --- a/drivers/hid/hid-wiimote.h
> +++ b/drivers/hid/hid-wiimote.h
> @@ -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);
> --
> 1.8.2.2
>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 13/26] HID: wiimote: add extension hotplug support
  2013-05-23 10:53   ` David Herrmann
@ 2013-05-23 12:28     ` Jiri Kosina
  0 siblings, 0 replies; 50+ messages in thread
From: Jiri Kosina @ 2013-05-23 12:28 UTC (permalink / raw)
  To: David Herrmann; +Cc: open list:HID CORE LAYER

On Thu, 23 May 2013, David Herrmann wrote:

[ ... snip ... ]
> >  static void wiimote_init_worker(struct work_struct *work)
> > @@ -655,6 +1129,30 @@ static void wiimote_init_worker(struct work_struct *work)
> >
> >         if (wdata->state.devtype == WIIMOTE_DEV_PENDING)
> >                 wiimote_init_detect(wdata);
> 
> We need an additional "wiimote_init_hotplug(wdata);" in this if-clause
> for devices with built-in MP to detect it right during init.
> Otherwise, MP is not initialized until you plug some other extension.
> That's because the MP-timer is disabled for devices with built-in MP.
> Other than that I didn't get any bug-reports. And I haven't found any
> bugs, either.
> 
> I want to avoid resending the whole series, so I will send a fixup
> patch on top of your branch once you applied it (if that is ok?).

Hi David,

that is certainly OK. I just haven't yet finished reviewing the whole lot, 
as it's quite some code :)

Please send the followup patch to this thread, marking it [27/26] or 
some such :) to make it clear that it's a followup to the series.

Thanks,

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH 27/26] HID: wiimote: init EXT/MP during device detection
  2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
                   ` (25 preceding siblings ...)
  2013-05-05 21:13 ` [RFC 26/26] HID/ALSA: wiimote: add speaker support David Herrmann
@ 2013-05-26 20:55 ` David Herrmann
  2013-05-26 20:55   ` [PATCH 28/26] HID: wiimote: discard invalid EXT data reports David Herrmann
  2013-05-26 20:55   ` [PATCH 29/26] HID: wiimote: fix classic controller parsing David Herrmann
  26 siblings, 2 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-26 20:55 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

We normally get EXT hotplug events or poll for MP hotplugging so we
don't need to force extension port initialization during device setup.
But for gen20 devices, we disable MP polling because MP is always
present. However, this prevents MP initialization during device setup
and users need to plug another extension to trigger EXT/MP detection.

Therefore, we now trigger EXT/MP detection during device setup
automatically. This also avoids slightly delayed extension detection
and provides sysfs child-devices prior to the "changed"-uevent during
device setup.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
Hi Jiri

Three trivial fixes for the pending series. If there are any issues, please
let me know. And thanks for reviewing them!

Cheers
David

 drivers/hid/hid-wiimote-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 89118e9..f54595c 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -1247,7 +1247,7 @@ static void wiimote_init_worker(struct work_struct *work)
 		changed = true;
 	}
 
-	if (!wiimote_init_check(wdata))
+	if (changed || !wiimote_init_check(wdata))
 		wiimote_init_hotplug(wdata);
 
 	if (changed)
-- 
1.8.2.3


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 28/26] HID: wiimote: discard invalid EXT data reports
  2013-05-26 20:55 ` [PATCH 27/26] HID: wiimote: init EXT/MP during device detection David Herrmann
@ 2013-05-26 20:55   ` David Herrmann
  2013-05-26 20:55   ` [PATCH 29/26] HID: wiimote: fix classic controller parsing David Herrmann
  1 sibling, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-26 20:55 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

If an extension device isn't initialized properly, or during hardware
initialization, a device might send extension data which is all 0xff.
This is ambigious because this is also a valid normal data report. But
it is impossible, under normal conditions, to trigger valid reports with
all 0xff. Hence, we can safely ignore them.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-core.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index f54595c..40fdc62 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -1335,11 +1335,19 @@ static bool valid_ext_handler(const struct wiimod_ops *ops, size_t len)
 static void handler_ext(struct wiimote_data *wdata, const __u8 *payload,
 			size_t len)
 {
+	static const __u8 invalid[21] = { 0xff, 0xff, 0xff, 0xff,
+					  0xff, 0xff, 0xff, 0xff,
+					  0xff, 0xff, 0xff, 0xff,
+					  0xff, 0xff, 0xff, 0xff,
+					  0xff, 0xff, 0xff, 0xff,
+					  0xff };
 	const __u8 *iter, *mods;
 	const struct wiimod_ops *ops;
 	bool is_mp;
 
-	if (len < 6)
+	if (len > 21)
+		len = 21;
+	if (len < 6 || !memcmp(payload, invalid, len))
 		return;
 
 	/* if MP is active, track MP slot hotplugging */
-- 
1.8.2.3


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [PATCH 29/26] HID: wiimote: fix classic controller parsing
  2013-05-26 20:55 ` [PATCH 27/26] HID: wiimote: init EXT/MP during device detection David Herrmann
  2013-05-26 20:55   ` [PATCH 28/26] HID: wiimote: discard invalid EXT data reports David Herrmann
@ 2013-05-26 20:55   ` David Herrmann
  1 sibling, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-05-26 20:55 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, David Herrmann

I finally got a "Classic Controller" and "Classic Controller Pro" in my
hands and noticed that all analog data was incorrectly parsed. Fix this
up so we report the data that we pretend we do.

I really doubt that this breaks any backwards compatibility, but if we
get any reports, we only need to revert this single patch.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-wiimote-modules.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index 25483aa..c7a2c6b 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -1062,12 +1062,12 @@ static void wiimod_classic_in_ext(struct wiimote_data *wdata, const __u8 *ext)
 	 * With motionp enabled it changes slightly to this:
 	 *   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
 	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
-	 *    1   | RX <4:3>  |          LX <5:1>           | BDU |
-	 *    2   | RX <2:1>  |          LY <5:1>           | BDL |
+	 *    1   | RX <5:4>  |          LX <5:1>           | BDU |
+	 *    2   | RX <3:2>  |          LY <5:1>           | BDL |
 	 *   -----+-----+-----+-----+-----------------------+-----+
-	 *    3   |RX<0>| LT <4:3>  |         RY <4:0>            |
+	 *    3   |RX<1>| LT <5:4>  |         RY <5:1>            |
 	 *   -----+-----+-----------+-----------------------------+
-	 *    4   |     LT <2:0>    |         RT <4:0>            |
+	 *    4   |     LT <3:1>    |         RT <5:1>            |
 	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
 	 *    5   | BDR | BDD | BLT | B-  | BH  | B+  | BRT | EXT |
 	 *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
@@ -1079,13 +1079,13 @@ static void wiimod_classic_in_ext(struct wiimote_data *wdata, const __u8 *ext)
 
 	if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
 		lx = ext[0] & 0x3e;
-		ly = ext[0] & 0x3e;
+		ly = ext[1] & 0x3e;
 	} else {
 		lx = ext[0] & 0x3f;
-		ly = ext[0] & 0x3f;
+		ly = ext[1] & 0x3f;
 	}
 
-	rx = (ext[0] >> 3) & 0x14;
+	rx = (ext[0] >> 3) & 0x18;
 	rx |= (ext[1] >> 5) & 0x06;
 	rx |= (ext[2] >> 7) & 0x01;
 	ry = ext[2] & 0x1f;
@@ -1103,8 +1103,8 @@ static void wiimod_classic_in_ext(struct wiimote_data *wdata, const __u8 *ext)
 	input_report_abs(wdata->extension.input, ABS_HAT1Y, ly - 0x20);
 	input_report_abs(wdata->extension.input, ABS_HAT2X, rx - 0x20);
 	input_report_abs(wdata->extension.input, ABS_HAT2Y, ry - 0x20);
-	input_report_abs(wdata->extension.input, ABS_HAT3X, rt - 0x20);
-	input_report_abs(wdata->extension.input, ABS_HAT3Y, lt - 0x20);
+	input_report_abs(wdata->extension.input, ABS_HAT3X, rt);
+	input_report_abs(wdata->extension.input, ABS_HAT3Y, lt);
 
 	input_report_key(wdata->extension.input,
 			 wiimod_classic_map[WIIMOD_CLASSIC_KEY_RIGHT],
-- 
1.8.2.3


^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [PATCH 01/26] HID: wiimote: extend driver description
  2013-05-05 21:12 ` [PATCH 01/26] HID: wiimote: extend driver description David Herrmann
@ 2013-06-03  9:19   ` Jiri Kosina
  2013-06-03 20:28     ` David Herrmann
  0 siblings, 1 reply; 50+ messages in thread
From: Jiri Kosina @ 2013-06-03  9:19 UTC (permalink / raw)
  To: David Herrmann; +Cc: linux-input

On Sun, 5 May 2013, David Herrmann wrote:

> The hid-wiimote driver supports more than the Wii Remote. Nintendo
> produced many devices based on the Wii Remote, which have extension
> devices built-in. It is not clear to many users, that these devices have
> anything in common with the Wii Remote, so fix the driver description.
> 
> This also updates the copyright information for the coming hotplugging
> rework.

I have now applied the whole patchset, with these two exceptions:

1) [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller

   - It's not completely clear to me whether you have reached conclusion 
     with Todd on this one or not

2) [RFC 26/26] HID/ALSA: wiimote: add speaker support

   - It'd be nice to get Ack from sound people on this one.

Thanks a lot, David. Good work.

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH 01/26] HID: wiimote: extend driver description
  2013-06-03  9:19   ` Jiri Kosina
@ 2013-06-03 20:28     ` David Herrmann
  0 siblings, 0 replies; 50+ messages in thread
From: David Herrmann @ 2013-06-03 20:28 UTC (permalink / raw)
  To: Jiri Kosina; +Cc: open list:HID CORE LAYER

Hi Jiri

On Mon, Jun 3, 2013 at 11:19 AM, Jiri Kosina <jkosina@suse.cz> wrote:
> On Sun, 5 May 2013, David Herrmann wrote:
>
>> The hid-wiimote driver supports more than the Wii Remote. Nintendo
>> produced many devices based on the Wii Remote, which have extension
>> devices built-in. It is not clear to many users, that these devices have
>> anything in common with the Wii Remote, so fix the driver description.
>>
>> This also updates the copyright information for the coming hotplugging
>> rework.
>
> I have now applied the whole patchset, with these two exceptions:
>
> 1) [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller
>
>    - It's not completely clear to me whether you have reached conclusion
>      with Todd on this one or not

No, I am still working on this. I will resend the patch plus some
Documentation when I'm done.

> 2) [RFC 26/26] HID/ALSA: wiimote: add speaker support
>
>    - It'd be nice to get Ack from sound people on this one.

Yeah, sure. That one is still under construction. I got ADPCM working
now and will try to get some review from alsa-devel.

> Thanks a lot, David. Good work.

Thanks for reviewing! I checked your tree and all patches (including
the fixups) are applied as required. Thanks!

Cheers
David

^ permalink raw reply	[flat|nested] 50+ messages in thread

end of thread, other threads:[~2013-06-03 20:28 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-05-05 21:12 [PATCH 00/26] Wii Remote Extension Hotplugging Support David Herrmann
2013-05-05 21:12 ` [PATCH 01/26] HID: wiimote: extend driver description David Herrmann
2013-06-03  9:19   ` Jiri Kosina
2013-06-03 20:28     ` David Herrmann
2013-05-05 21:12 ` [PATCH 02/26] HID: wiimote: move queue handling into separate struct David Herrmann
2013-05-05 21:12 ` [PATCH 03/26] HID: wiimote: keep HID device open David Herrmann
2013-05-05 21:12 ` [PATCH 04/26] HID: wiimote: add device detection David Herrmann
2013-05-05 21:12 ` [PATCH 05/26] HID: wiimote: use cached battery values on I/O failure David Herrmann
2013-05-05 21:12 ` [PATCH 06/26] HID: wiimote: wake up if output queue failed David Herrmann
2013-05-05 21:12 ` [PATCH 07/26] HID: wiimote: add sub-device module infrastructure David Herrmann
2013-05-05 21:12 ` [PATCH 08/26] HID: wiimote: convert KEYS and RUMBLE to modules David Herrmann
2013-05-05 21:12 ` [PATCH 09/26] HID: wiimote: convert BATTERY to module David Herrmann
2013-05-05 21:12 ` [PATCH 10/26] HID: wiimote: convert LEDS to modules David Herrmann
2013-05-05 21:12 ` [PATCH 11/26] HID: wiimote: convert ACCEL to module David Herrmann
2013-05-05 21:12 ` [PATCH 12/26] HID: wiimote: convert IR " David Herrmann
2013-05-05 21:12 ` [PATCH 13/26] HID: wiimote: add extension hotplug support David Herrmann
2013-05-23 10:53   ` David Herrmann
2013-05-23 12:28     ` Jiri Kosina
2013-05-05 21:12 ` [PATCH 14/26] HID: wiimote: add Balance Board support David Herrmann
2013-05-05 21:12 ` [PATCH 15/26] HID: wiimote: add Nunchuk support David Herrmann
2013-05-05 21:13 ` [PATCH 16/26] HID: wiimote: add Classic Controller extension David Herrmann
2013-05-06  0:39   ` Todd Showalter
2013-05-06  5:40     ` David Herrmann
2013-05-06 14:15       ` Todd Showalter
2013-05-05 21:13 ` [PATCH 17/26] HID: wiimote: add Motion Plus extension module David Herrmann
2013-05-05 21:13 ` [PATCH 18/26] HID: wiimote: fix ctx pointer in debugfs DRM-write David Herrmann
2013-05-05 21:13 ` [PATCH 19/26] HID: wiimote: lock DRM mode during debugfs overwrite David Herrmann
2013-05-05 21:13 ` [PATCH 20/26] HID: wiimote: add sysfs extension/device-type attrs David Herrmann
2013-05-05 21:13 ` [PATCH 21/26] HID: wiimote: add "bboard_calib" attribute David Herrmann
2013-05-05 21:13 ` [PATCH 22/26] HID: wiimote: remove old static extension support David Herrmann
2013-05-05 21:13 ` [PATCH 23/26] HID: wiimote: add MP quirks David Herrmann
2013-05-05 21:13 ` [RFC 24/26] HID: wiimote: support Nintendo Wii U Pro Controller David Herrmann
2013-05-06  0:43   ` Todd Showalter
2013-05-06  5:49     ` David Herrmann
2013-05-06 14:14       ` Todd Showalter
2013-05-08 15:33         ` David Herrmann
2013-05-08 16:40           ` Todd Showalter
2013-05-08 17:05             ` Dmitry Torokhov
2013-05-08 17:20               ` Todd Showalter
2013-05-08 17:25                 ` David Herrmann
2013-05-08 17:26                 ` Dmitry Torokhov
2013-05-13 17:03                   ` David Herrmann
2013-05-13 17:40                     ` Todd Showalter
2013-05-22 12:10                       ` David Herrmann
2013-05-22 16:18                         ` Todd Showalter
2013-05-05 21:13 ` [PATCH 25/26] HID: wiimote: fix DRM debug-attr to correctly parse input David Herrmann
2013-05-05 21:13 ` [RFC 26/26] HID/ALSA: wiimote: add speaker support David Herrmann
2013-05-26 20:55 ` [PATCH 27/26] HID: wiimote: init EXT/MP during device detection David Herrmann
2013-05-26 20:55   ` [PATCH 28/26] HID: wiimote: discard invalid EXT data reports David Herrmann
2013-05-26 20:55   ` [PATCH 29/26] HID: wiimote: fix classic controller parsing David Herrmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.