From patchwork Sat Apr 13 10:47:55 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Herrmann X-Patchwork-Id: 2440411 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: X-Original-To: patchwork-linux-input@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 5DA753FD1A for ; Sat, 13 Apr 2013 10:48:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753160Ab3DMKsw (ORCPT ); Sat, 13 Apr 2013 06:48:52 -0400 Received: from mail-ee0-f45.google.com ([74.125.83.45]:64238 "EHLO mail-ee0-f45.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754066Ab3DMKsw (ORCPT ); Sat, 13 Apr 2013 06:48:52 -0400 Received: by mail-ee0-f45.google.com with SMTP id c50so1553802eek.4 for ; Sat, 13 Apr 2013 03:48:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references; bh=+REqPlSfO0RB0ybU6xyj9vAPsahoXxtmt8W9QM/U3vo=; b=QjwJ/GigQCFwfTAYyXo5UNb1CLVleUVn36TZ9lUAt8kyLNcf3mRQvV77AeI6c7MFmW S8iFkGk8qvTxqtZ/XSHUVorQVmCQRaGcbA4jUkFU7uWACzGgcOhcgsae3Pq0oeXIU+ym L6qRD+TlrGwXuy1GTZJZ/+Th2nzYJMvllNjdJJe4U9DGzz3oOOwCCyfc0bT6heccftPn ZM+Hh5meITFuij9LAOBfkUmb0UZvhXwNt6bFeUj9iogz2ApQUEMbNteTGYFAyvwoTEUL dgVhLuRHoUfLGCzz8Wd7ys7r5O+qymRulEs+xnzsOP9et2pZn4ruUM4J8QFytFwy1z5J xdHQ== X-Received: by 10.15.83.73 with SMTP id b49mr38065254eez.25.1365850130912; Sat, 13 Apr 2013 03:48:50 -0700 (PDT) Received: from localhost.localdomain (stgt-5f71827d.pool.mediaWays.net. [95.113.130.125]) by mx.google.com with ESMTPS id t4sm15859796eel.0.2013.04.13.03.48.49 (version=TLSv1.2 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Sat, 13 Apr 2013 03:48:50 -0700 (PDT) From: David Herrmann To: linux-input@vger.kernel.org Cc: Jiri Kosina , David Herrmann Subject: [PATCH 14/21] HID: wiimote: add Balance Board support Date: Sat, 13 Apr 2013 12:47:55 +0200 Message-Id: <1365850082-3585-15-git-send-email-dh.herrmann@gmail.com> X-Mailer: git-send-email 1.8.2.1 In-Reply-To: <1365850082-3585-1-git-send-email-dh.herrmann@gmail.com> References: <1365850082-3585-1-git-send-email-dh.herrmann@gmail.com> Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org 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 --- drivers/hid/hid-wiimote-core.c | 23 +++++ drivers/hid/hid-wiimote-modules.c | 211 ++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-wiimote.h | 9 ++ 3 files changed, 243 insertions(+) diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 1f7a550..573f439 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -198,6 +198,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) return WIIPROTO_REQ_DRM_KAIE; @@ -425,6 +433,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; } @@ -556,6 +567,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, @@ -739,6 +755,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 @@ -756,6 +773,11 @@ 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 (vendor == USB_VENDOR_ID_NINTENDO) { if (product == USB_DEVICE_ID_NINTENDO_WIIMOTE) { devtype = WIIMOTE_DEV_GEN10; @@ -984,6 +1006,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 2b0d00c..79dbeac 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;