diff mbox

[08/16] HID: wiimote: Parse IR input and report to userspace

Message ID 1311869316-17128-9-git-send-email-dh.herrmann@googlemail.com (mailing list archive)
State New, archived
Delegated to: Jiri Kosina
Headers show

Commit Message

David Herrmann July 28, 2011, 4:08 p.m. UTC
A wiimote sends IR pointing information for up to 4 trackable IR lights. If less
lights are visible, the missing lights report max value. This patch adds parser
functions for IR input and reports this via ABS_HAT*XY values to the input
subsystem.

The IR cam can be in four states: off, basic, extended, full
The DRM chooser automatically chosses an DRM that matches the current IR cam
state so no information is lost.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
 drivers/hid/hid-wiimote.c |  101 +++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 97 insertions(+), 4 deletions(-)
diff mbox

Patch

diff --git a/drivers/hid/hid-wiimote.c b/drivers/hid/hid-wiimote.c
index 6947c2d..ed7feb2 100644
--- a/drivers/hid/hid-wiimote.c
+++ b/drivers/hid/hid-wiimote.c
@@ -53,8 +53,13 @@  struct wiimote_data {
 #define WIIPROTO_FLAG_LED4		0x08
 #define WIIPROTO_FLAG_RUMBLE		0x10
 #define WIIPROTO_FLAG_ACCEL		0x20
+#define WIIPROTO_FLAG_IR_BASIC		0x40
+#define WIIPROTO_FLAG_IR_EXT		0x80
+#define WIIPROTO_FLAG_IR_FULL		0xc0 /* IR_BASIC | IR_EXT */
 #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 | \
+							WIIPROTO_FLAG_IR_FULL)
 
 enum wiiproto_reqs {
 	WIIPROTO_REQ_NULL = 0x0,
@@ -67,6 +72,7 @@  enum wiiproto_reqs {
 	WIIPROTO_REQ_DRM_KA = 0x31,
 	WIIPROTO_REQ_DRM_KAI = 0x33,
 	WIIPROTO_REQ_DRM_KAE = 0x35,
+	WIIPROTO_REQ_DRM_KIE = 0x36,
 	WIIPROTO_REQ_DRM_KAIE = 0x37,
 	WIIPROTO_REQ_DRM_SKAI1 = 0x3e,
 	WIIPROTO_REQ_DRM_SKAI2 = 0x3f,
@@ -247,10 +253,22 @@  static void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
  */
 static __u8 select_drm(struct wiimote_data *wdata)
 {
-	if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
-		return WIIPROTO_REQ_DRM_KA;
-	else
-		return WIIPROTO_REQ_DRM_K;
+	__u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR;
+	if (ir == WIIPROTO_FLAG_IR_BASIC) {
+		if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
+			return WIIPROTO_REQ_DRM_KAIE;
+		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) {
+		return WIIPROTO_REQ_DRM_SKAI1;
+	} else {
+		if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
+			return WIIPROTO_REQ_DRM_KA;
+		else
+			return WIIPROTO_REQ_DRM_K;
+	}
 }
 
 static void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm)
@@ -469,6 +487,40 @@  static void handler_accel(struct wiimote_data *wdata, const __u8 *payload)
 	input_event(wdata->input, EV_ABS, ABS_Z, z - 0x200);
 }
 
+#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;
+
+	if (packed) {
+		x = ir[1] << 2;
+		y = ir[2] << 2;
+
+		x |= ir[0] & 0x3;
+		y |= (ir[0] >> 2) & 0x3;
+	} else {
+		x = ir[0] << 2;
+		y = ir[1] << 2;
+
+		x |= (ir[2] >> 4) & 0x3;
+		y |= (ir[2] >> 6) & 0x3;
+	}
+
+	input_event(wdata->input, EV_ABS, xid, x);
+	input_event(wdata->input, EV_ABS, yid, y);
+}
 
 static void handler_status(struct wiimote_data *wdata, const __u8 *payload)
 {
@@ -500,6 +552,19 @@  static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload)
 {
 	handler_keys(wdata, payload);
 	handler_accel(wdata, payload);
+	ir_to_input0(wdata, &payload[5], false);
+	ir_to_input1(wdata, &payload[8], false);
+	ir_to_input2(wdata, &payload[11], false);
+	ir_to_input3(wdata, &payload[14], false);
+}
+
+static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload)
+{
+	handler_keys(wdata, payload);
+	ir_to_input0(wdata, &payload[2], false);
+	ir_to_input1(wdata, &payload[4], true);
+	ir_to_input2(wdata, &payload[7], false);
+	ir_to_input3(wdata, &payload[9], true);
 }
 
 static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload)
@@ -512,6 +577,10 @@  static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload)
 {
 	handler_keys(wdata, payload);
 	handler_accel(wdata, payload);
+	ir_to_input0(wdata, &payload[5], false);
+	ir_to_input1(wdata, &payload[7], true);
+	ir_to_input2(wdata, &payload[10], false);
+	ir_to_input3(wdata, &payload[12], true);
 }
 
 static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload)
@@ -521,6 +590,9 @@  static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload)
 	wdata->state.accel_split[0] = payload[2];
 	wdata->state.accel_split[1] = (payload[0] >> 1) & (0x10 | 0x20);
 	wdata->state.accel_split[1] |= (payload[1] << 1) & (0x40 | 0x80);
+
+	ir_to_input0(wdata, &payload[3], false);
+	ir_to_input1(wdata, &payload[12], false);
 }
 
 static void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload)
@@ -538,6 +610,9 @@  static void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload)
 	buf[3] = payload[2];
 	buf[4] = wdata->state.accel_split[1];
 	handler_accel(wdata, buf);
+
+	ir_to_input2(wdata, &payload[3], false);
+	ir_to_input3(wdata, &payload[12], false);
 }
 
 struct wiiproto_handler {
@@ -553,6 +628,7 @@  static struct wiiproto_handler handlers[] = {
 	{ .id = WIIPROTO_REQ_DRM_KA, .size = 5, .func = handler_drm_KA },
 	{ .id = WIIPROTO_REQ_DRM_KAI, .size = 17, .func = handler_drm_KAI },
 	{ .id = WIIPROTO_REQ_DRM_KAE, .size = 21, .func = handler_drm_KAE },
+	{ .id = WIIPROTO_REQ_DRM_KIE, .size = 21, .func = handler_drm_KIE },
 	{ .id = WIIPROTO_REQ_DRM_KAIE, .size = 21, .func = handler_drm_KAIE },
 	{ .id = WIIPROTO_REQ_DRM_SKAI1, .size = 21, .func = handler_drm_SKAI1 },
 	{ .id = WIIPROTO_REQ_DRM_SKAI2, .size = 21, .func = handler_drm_SKAI2 },
@@ -628,6 +704,23 @@  static struct wiimote_data *wiimote_create(struct hid_device *hdev)
 	input_set_abs_params(wdata->input, ABS_Y, -500, 500, 2, 4);
 	input_set_abs_params(wdata->input, ABS_Z, -500, 500, 2, 4);
 
+	set_bit(ABS_HAT0X, wdata->input->absbit);
+	set_bit(ABS_HAT0Y, wdata->input->absbit);
+	set_bit(ABS_HAT1X, wdata->input->absbit);
+	set_bit(ABS_HAT1Y, wdata->input->absbit);
+	set_bit(ABS_HAT2X, wdata->input->absbit);
+	set_bit(ABS_HAT2Y, wdata->input->absbit);
+	set_bit(ABS_HAT3X, wdata->input->absbit);
+	set_bit(ABS_HAT3Y, wdata->input->absbit);
+	input_set_abs_params(wdata->input, ABS_HAT0X, 0, 1023, 2, 4);
+	input_set_abs_params(wdata->input, ABS_HAT0Y, 0, 767, 2, 4);
+	input_set_abs_params(wdata->input, ABS_HAT1X, 0, 1023, 2, 4);
+	input_set_abs_params(wdata->input, ABS_HAT1Y, 0, 767, 2, 4);
+	input_set_abs_params(wdata->input, ABS_HAT2X, 0, 1023, 2, 4);
+	input_set_abs_params(wdata->input, ABS_HAT2Y, 0, 767, 2, 4);
+	input_set_abs_params(wdata->input, ABS_HAT3X, 0, 1023, 2, 4);
+	input_set_abs_params(wdata->input, ABS_HAT3Y, 0, 767, 2, 4);
+
 	spin_lock_init(&wdata->qlock);
 	INIT_WORK(&wdata->worker, wiimote_worker);