Commit 73f8645d authored by David Herrmann's avatar David Herrmann Committed by Jiri Kosina

HID: wiimote: add support for Guitar-Hero drums

Guitar-Hero comes with a drums extension. Use the newly introduced input
drums-bits to report this back to user-space. This is a usual extension
like any other device. Nothing special to take care of.

We report this to user-space as "Nintendo Wii Remote Drums". There are
other drums (like "RockBand" drums) which we currently do not support and
maybe will at some point. However, it is quite likely that we can report
these via the same interface. This allows user-space to work with them
without knowing the exact branding.
I couldn't find anyone who owns a "RockBand" device, though.
Initial-work-by: default avatarNicolas Adenis-Lamarre <nicolas.adenis.lamarre@gmail.com>
Signed-off-by: David Herrmann's avatarDavid Herrmann <dh.herrmann@gmail.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 61e00655
......@@ -456,6 +456,9 @@ static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem)
return WIIMOTE_EXT_BALANCE_BOARD;
if (rmem[4] == 0x01 && rmem[5] == 0x20)
return WIIMOTE_EXT_PRO_CONTROLLER;
if (rmem[0] == 0x01 && rmem[1] == 0x00 &&
rmem[4] == 0x01 && rmem[5] == 0x03)
return WIIMOTE_EXT_GUITAR_HERO_DRUMS;
return WIIMOTE_EXT_UNKNOWN;
}
......@@ -489,6 +492,7 @@ 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:
case WIIMOTE_EXT_GUITAR_HERO_DRUMS:
wmem = 0x07;
break;
case WIIMOTE_EXT_NUNCHUK:
......@@ -1079,6 +1083,7 @@ static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = {
[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",
[WIIMOTE_EXT_GUITAR_HERO_DRUMS] = "Nintendo Wii Guitar Hero Drums",
};
/*
......@@ -1665,6 +1670,8 @@ static ssize_t wiimote_ext_show(struct device *dev,
return sprintf(buf, "balanceboard\n");
case WIIMOTE_EXT_PRO_CONTROLLER:
return sprintf(buf, "procontroller\n");
case WIIMOTE_EXT_GUITAR_HERO_DRUMS:
return sprintf(buf, "drums\n");
case WIIMOTE_EXT_UNKNOWN:
/* fallthrough */
default:
......
......@@ -1833,6 +1833,223 @@ static const struct wiimod_ops wiimod_pro = {
.in_ext = wiimod_pro_in_ext,
};
/*
* Drums
* Guitar-Hero, Rock-Band and other games came bundled with drums which can
* be plugged as extension to a Wiimote. Drum-reports are still not entirely
* figured out, but the most important information is known.
* We create a separate device for drums and report all information via this
* input device.
*/
static inline void wiimod_drums_report_pressure(struct wiimote_data *wdata,
__u8 none, __u8 which,
__u8 pressure, __u8 onoff,
__u8 *store, __u16 code,
__u8 which_code)
{
static const __u8 default_pressure = 3;
if (!none && which == which_code) {
*store = pressure;
input_report_abs(wdata->extension.input, code, *store);
} else if (onoff != !!*store) {
*store = onoff ? default_pressure : 0;
input_report_abs(wdata->extension.input, code, *store);
}
}
static void wiimod_drums_in_ext(struct wiimote_data *wdata, const __u8 *ext)
{
__u8 pressure, which, none, hhp, sx, sy;
__u8 o, r, y, g, b, bass, bm, bp;
/* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
* 1 | 0 | 0 | SX <5:0> |
* 2 | 0 | 0 | SY <5:0> |
* -----+-----+-----+-----------------------------+-----+
* 3 | HPP | NON | WHICH <5:1> | ? |
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
* 4 | SOFT <7:5> | 0 | 1 | 1 | 0 | ? |
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
* 5 | ? | 1 | 1 | B- | 1 | B+ | 1 | ? |
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
* 6 | O | R | Y | G | B | BSS | 1 | 1 |
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
* All buttons are 0 if pressed
*
* With Motion+ enabled, the following bits will get invalid:
* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
* 1 | 0 | 0 | SX <5:1> |XXXXX|
* 2 | 0 | 0 | SY <5:1> |XXXXX|
* -----+-----+-----+-----------------------------+-----+
* 3 | HPP | NON | WHICH <5:1> | ? |
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
* 4 | SOFT <7:5> | 0 | 1 | 1 | 0 | ? |
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
* 5 | ? | 1 | 1 | B- | 1 | B+ | 1 |XXXXX|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
* 6 | O | R | Y | G | B | BSS |XXXXX|XXXXX|
* -----+-----+-----+-----+-----+-----+-----+-----+-----+
*/
pressure = 7 - (ext[3] >> 5);
which = (ext[2] >> 1) & 0x1f;
none = !!(ext[2] & 0x40);
hhp = !(ext[2] & 0x80);
sx = ext[0] & 0x3f;
sy = ext[1] & 0x3f;
o = !(ext[5] & 0x80);
r = !(ext[5] & 0x40);
y = !(ext[5] & 0x20);
g = !(ext[5] & 0x10);
b = !(ext[5] & 0x08);
bass = !(ext[5] & 0x04);
bm = !(ext[4] & 0x10);
bp = !(ext[4] & 0x04);
wiimod_drums_report_pressure(wdata, none, which, pressure,
o, &wdata->state.pressure_drums[0],
ABS_CYMBAL_RIGHT, 0x0e);
wiimod_drums_report_pressure(wdata, none, which, pressure,
r, &wdata->state.pressure_drums[1],
ABS_TOM_LEFT, 0x19);
wiimod_drums_report_pressure(wdata, none, which, pressure,
y, &wdata->state.pressure_drums[2],
ABS_CYMBAL_LEFT, 0x11);
wiimod_drums_report_pressure(wdata, none, which, pressure,
g, &wdata->state.pressure_drums[3],
ABS_TOM_FAR_RIGHT, 0x12);
wiimod_drums_report_pressure(wdata, none, which, pressure,
b, &wdata->state.pressure_drums[4],
ABS_TOM_RIGHT, 0x0f);
/* Bass shares pressure with hi-hat (set via hhp) */
wiimod_drums_report_pressure(wdata, none, hhp ? 0xff : which, pressure,
bass, &wdata->state.pressure_drums[5],
ABS_BASS, 0x1b);
/* Hi-hat has no on/off values, just pressure. Force to off/0. */
wiimod_drums_report_pressure(wdata, none, hhp ? which : 0xff, pressure,
0, &wdata->state.pressure_drums[6],
ABS_HI_HAT, 0x0e);
input_report_abs(wdata->extension.input, ABS_X, sx - 0x20);
input_report_abs(wdata->extension.input, ABS_Y, sy - 0x20);
input_report_key(wdata->extension.input, BTN_START, bp);
input_report_key(wdata->extension.input, BTN_SELECT, bm);
input_sync(wdata->extension.input);
}
static int wiimod_drums_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_drums_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_drums_probe(const struct wiimod_ops *ops,
struct wiimote_data *wdata)
{
int ret;
wdata->extension.input = input_allocate_device();
if (!wdata->extension.input)
return -ENOMEM;
input_set_drvdata(wdata->extension.input, wdata);
wdata->extension.input->open = wiimod_drums_open;
wdata->extension.input->close = wiimod_drums_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 " Drums";
set_bit(EV_KEY, wdata->extension.input->evbit);
set_bit(BTN_START, wdata->extension.input->keybit);
set_bit(BTN_SELECT, wdata->extension.input->keybit);
set_bit(EV_ABS, wdata->extension.input->evbit);
set_bit(ABS_X, wdata->extension.input->absbit);
set_bit(ABS_Y, wdata->extension.input->absbit);
set_bit(ABS_TOM_LEFT, wdata->extension.input->absbit);
set_bit(ABS_TOM_RIGHT, wdata->extension.input->absbit);
set_bit(ABS_TOM_FAR_RIGHT, wdata->extension.input->absbit);
set_bit(ABS_CYMBAL_LEFT, wdata->extension.input->absbit);
set_bit(ABS_CYMBAL_RIGHT, wdata->extension.input->absbit);
set_bit(ABS_BASS, wdata->extension.input->absbit);
set_bit(ABS_HI_HAT, wdata->extension.input->absbit);
input_set_abs_params(wdata->extension.input,
ABS_X, -32, 31, 1, 1);
input_set_abs_params(wdata->extension.input,
ABS_Y, -32, 31, 1, 1);
input_set_abs_params(wdata->extension.input,
ABS_TOM_LEFT, 0, 7, 0, 0);
input_set_abs_params(wdata->extension.input,
ABS_TOM_RIGHT, 0, 7, 0, 0);
input_set_abs_params(wdata->extension.input,
ABS_TOM_FAR_RIGHT, 0, 7, 0, 0);
input_set_abs_params(wdata->extension.input,
ABS_CYMBAL_LEFT, 0, 7, 0, 0);
input_set_abs_params(wdata->extension.input,
ABS_CYMBAL_RIGHT, 0, 7, 0, 0);
input_set_abs_params(wdata->extension.input,
ABS_BASS, 0, 7, 0, 0);
input_set_abs_params(wdata->extension.input,
ABS_HI_HAT, 0, 7, 0, 0);
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_drums_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_drums = {
.flags = 0,
.arg = 0,
.probe = wiimod_drums_probe,
.remove = wiimod_drums_remove,
.in_ext = wiimod_drums_in_ext,
};
/*
* Builtin Motion Plus
* This module simply sets the WIIPROTO_FLAG_BUILTIN_MP protocol flag which
......@@ -2083,4 +2300,5 @@ const struct wiimod_ops *wiimod_ext_table[WIIMOTE_EXT_NUM] = {
[WIIMOTE_EXT_CLASSIC_CONTROLLER] = &wiimod_classic,
[WIIMOTE_EXT_BALANCE_BOARD] = &wiimod_bboard,
[WIIMOTE_EXT_PRO_CONTROLLER] = &wiimod_pro,
[WIIMOTE_EXT_GUITAR_HERO_DRUMS] = &wiimod_drums,
};
......@@ -88,6 +88,7 @@ enum wiimote_exttype {
WIIMOTE_EXT_CLASSIC_CONTROLLER,
WIIMOTE_EXT_BALANCE_BOARD,
WIIMOTE_EXT_PRO_CONTROLLER,
WIIMOTE_EXT_GUITAR_HERO_DRUMS,
WIIMOTE_EXT_NUM,
};
......@@ -135,6 +136,7 @@ struct wiimote_state {
/* calibration data */
__u16 calib_bboard[4][3];
__u8 pressure_drums[7];
};
struct wiimote_data {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment