diff mbox

HID: Add guitar controller extension support to hid-wiimote

Message ID 20180308000927.GA2977@zen.votava (mailing list archive)
State New, archived
Headers show

Commit Message

Matt Votava March 8, 2018, 12:09 a.m. UTC
Some wii games come with a guitar controller extension that plugs into
the wii remote controller. This patch adds support for the les paul
style guitar that comes with Guitar Hero 3 to hid-wiimote.

Signed-off-by: Matt Votava <matt@votava.net>
---
 drivers/hid/hid-wiimote-core.c    |   7 ++
 drivers/hid/hid-wiimote-modules.c | 200 ++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-wiimote.h         |   1 +
 3 files changed, 208 insertions(+)

Comments

David Herrmann May 23, 2018, 9:22 a.m. UTC | #1
Hi

On Thu, Mar 8, 2018 at 1:09 AM, Matt Votava <matt@votava.net> wrote:
> Some wii games come with a guitar controller extension that plugs into
> the wii remote controller. This patch adds support for the les paul
> style guitar that comes with Guitar Hero 3 to hid-wiimote.
>
> Signed-off-by: Matt Votava <matt@votava.net>
> ---
>  drivers/hid/hid-wiimote-core.c    |   7 ++
>  drivers/hid/hid-wiimote-modules.c | 200 ++++++++++++++++++++++++++++++++++++++
>  drivers/hid/hid-wiimote.h         |   1 +
>  3 files changed, 208 insertions(+)
>
> diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
> index 579884ebd94d..8b18d104934f 100644
> --- a/drivers/hid/hid-wiimote-core.c
> +++ b/drivers/hid/hid-wiimote-core.c
> @@ -449,6 +449,9 @@ 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[0] == 0x00 && rmem[1] == 0x00 && rmem[2] == 0xa4 &&
> +           rmem[3] == 0x20 && rmem[4] == 0x01 && rmem[5] == 0x03)
> +               return WIIMOTE_EXT_GUITAR;

I would skip rmem[2] and rmem[3]. I think they simply encode the
extension address. This should be enough:

+       if (rmem[0] == 0x00 && rmem[1] == 0x00 &&
+           rmem[4] == 0x01 && rmem[5] == 0x03)
        ...

>         if (rmem[4] == 0x01 && rmem[5] == 0x01)
>                 return WIIMOTE_EXT_CLASSIC_CONTROLLER;
>         if (rmem[4] == 0x04 && rmem[5] == 0x02)
> @@ -491,6 +494,7 @@ static bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype)
>                 wmem = 0x07;
>                 break;
>         case WIIMOTE_EXT_NUNCHUK:
> +       case WIIMOTE_EXT_GUITAR:
>                 wmem = 0x05;

I think using 0x07 here is easier. That is, use the CLASSIC_CONTROLLER
pass-through, rather than the nunchuk one. But see below..

>                 break;
>         default:
> @@ -1075,6 +1079,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] = "Guitar Hero 3 Les Paul Controller",

Is that the official name? I always used "Nintendo Wii Guitar Hero
Guitar" to follow the style of the other ones. However, I never owned
such a device, so no clue what they report as name. I am also unsure
whether Nintendo actually produced them, or whether they're just
licensed.

>  };
>
>  /*
> @@ -1660,6 +1665,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:
> +               return sprintf(buf, "guitar\n");
>         case WIIMOTE_EXT_UNKNOWN:
>                 /* fallthrough */
>         default:
> diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
> index c830ed39348f..129f67ef7bd9 100644
> --- a/drivers/hid/hid-wiimote-modules.c
> +++ b/drivers/hid/hid-wiimote-modules.c
> @@ -2176,6 +2176,205 @@ const struct wiimod_ops wiimod_mp = {
>         .in_mp = wiimod_mp_in_mp,
>  };
>
> +/*
> + * Guitar Hero 3 Gibson Les Paul Controller
> + * The Les Paul Guitar Controller is used by Guitar Hero 3 and is
> + * compatible with later Guitar Hero Wii games and Rockband games
> + * as well. The controller has 5 fret keys, strum up and down, an
> + * analog whammy bar, analog stick, and plus and minus keys.
> + */
> +
> +enum wiimod_guitar_keys {
> +       WIIMOD_GUITAR_KEY_G,
> +       WIIMOD_GUITAR_KEY_R,
> +       WIIMOD_GUITAR_KEY_Y,
> +       WIIMOD_GUITAR_KEY_B,
> +       WIIMOD_GUITAR_KEY_O,
> +       WIIMOD_GUITAR_KEY_UP,
> +       WIIMOD_GUITAR_KEY_DOWN,
> +       WIIMOD_GUITAR_KEY_PLUS,
> +       WIIMOD_GUITAR_KEY_MINUS,
> +       WIIMOD_GUITAR_KEY_NUM,
> +};
> +
> +static const __u16 wiimod_guitar_map[] = {
> +       BTN_1,          /* WIIMOD_GUITAR_KEY_G */
> +       BTN_2,          /* WIIMOD_GUITAR_KEY_R */
> +       BTN_3,          /* WIIMOD_GUITAR_KEY_Y */
> +       BTN_4,          /* WIIMOD_GUITAR_KEY_B */
> +       BTN_5,          /* WIIMOD_GUITAR_KEY_O */
> +       BTN_DPAD_UP,    /* WIIMOD_GUITAR_KEY_UP */
> +       BTN_DPAD_DOWN,  /* WIIMOD_GUITAR_KEY_DOWN */
> +       BTN_START,              /* WIIMOD_GUITAR_KEY_PLUS */
> +       BTN_SELECT,             /* WIIMOD_GUITAR_KEY_MINUS */

This mapping looks ok to me.

> +};
> +
> +static void wiimod_guitar_in_ext(struct wiimote_data *wdata, const __u8 *ext)
> +{
> +       __s16 bx, by, bt, bw;
> +
> +       /*   Byte |  7 |  6 |  5 |  4 |  3 |  2 |  1 |  0 |
> +        *   -----+----+----+----+----+----+----+----+----+
> +        *    0   |         |           Button X          |
> +        *    1   |         |           Button Y          |
> +        *   -----+---------+----+------------------------+
> +        *    2   |              |       Button TB        |
> +        *    3   |              |       Button WB        |
> +        *   -----+----+----+----+----+----+----+---------+
> +        *    4   |    | BD |    | B- |    | B+ |         |
> +        *   -----+----+----+----+----+----+----+----+----+
> +        *    5   | BO | BR | BB | BG | BY |         | BU |
> +        *   -----+----+----+----+----+----+---------+----+
> +        * Button X/Y is the analog stick. Button TB, WB is the touch bar
> +        * on the neck of guitars supporting this feature, and the whammy bar.
> +        *
> +        * BD and BU are strum button down and strum button up.
> +        * B- and B+ are the minus and plus button at the base of the neck.
> +        *
> +        * BG, BR, BY, BB, BO are buttons green, red, yellow, blue, and orange.
> +        * All buttons are low active

You lack MP+ handling here. If you use the passthrough, some bits are
moved. I am not entirely sure whether there are actually devices with
MP and guitars, but I'd prefer being on the safe side. Have a look at
commit 8e22ecb603c8 in the official kernel tree. This contains details
on how to handle the mapping (which used the 0x07 pass-through as
described above). Note that this commit got reverted some years ago.

> +        */
> +
> +       bx = ext[0];
> +       by = ext[1];
> +       bx -= 224;
> +       by -= 224;

Err, this looks bogus. The upper bits of bx and by are not necessarily
1. They might be 0 for some devices (I think the "World Tour" ones).
Hence, I think you need to clear the 6th and 7th bit and then use '32'
as center-point, rather than 224.

If you can, have a look at the old guitar-support (as I mentioned:
8e22ecb603c8). I would like to have the MP+ handling in there.

Otherwise, this looks really good! Thanks a lot for picking it up!
Please remember to keep Jiri in CC, otherwise he will not pick up HID
patches.

Thanks
David

> +
> +       bt = ext[2];
> +       bw = ext[3];
> +       bw &= 0x1f;
> +
> +       bw -= 15;
> +
> +       input_report_abs(wdata->extension.input, ABS_X, bx);
> +       input_report_abs(wdata->extension.input, ABS_Y, by);
> +
> +       input_report_abs(wdata->extension.input, ABS_HAT0X, bt);
> +       input_report_abs(wdata->extension.input, ABS_HAT1X, bw);
> +
> +       input_report_key(wdata->extension.input,
> +                       wiimod_guitar_map[WIIMOD_GUITAR_KEY_G],
> +                       !(ext[5] & 0x10));
> +       input_report_key(wdata->extension.input,
> +                       wiimod_guitar_map[WIIMOD_GUITAR_KEY_R],
> +                       !(ext[5] & 0x40));
> +       input_report_key(wdata->extension.input,
> +                       wiimod_guitar_map[WIIMOD_GUITAR_KEY_Y],
> +                       !(ext[5] & 0x08));
> +       input_report_key(wdata->extension.input,
> +                       wiimod_guitar_map[WIIMOD_GUITAR_KEY_B],
> +                       !(ext[5] & 0x20));
> +       input_report_key(wdata->extension.input,
> +                       wiimod_guitar_map[WIIMOD_GUITAR_KEY_O],
> +                       !(ext[5] & 0x80));
> +       input_report_key(wdata->extension.input,
> +                       wiimod_guitar_map[WIIMOD_GUITAR_KEY_UP],
> +                       !(ext[5] & 0x01));
> +       input_report_key(wdata->extension.input,
> +                       wiimod_guitar_map[WIIMOD_GUITAR_KEY_DOWN],
> +                       !(ext[4] & 0x40));
> +       input_report_key(wdata->extension.input,
> +                       wiimod_guitar_map[WIIMOD_GUITAR_KEY_PLUS],
> +                       !(ext[4] & 0x04));
> +       input_report_key(wdata->extension.input,
> +                       wiimod_guitar_map[WIIMOD_GUITAR_KEY_MINUS],
> +                       !(ext[4] & 0x10));
> +
> +       input_sync(wdata->extension.input);
> +}
> +
> +static int wiimod_guitar_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_guitar_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_guitar_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_guitar_open;
> +       wdata->extension.input->close = wiimod_guitar_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 " Guitar";
> +
> +       set_bit(EV_KEY, wdata->extension.input->evbit);
> +       for (i = 0; i < WIIMOD_GUITAR_KEY_NUM; ++i)
> +               set_bit(wiimod_guitar_map[i],
> +                       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_HAT0X, wdata->extension.input->absbit);
> +       set_bit(ABS_HAT1X, wdata->extension.input->absbit);
> +       input_set_abs_params(wdata->extension.input,
> +                            ABS_X, -30, 30, 2, 4);
> +       input_set_abs_params(wdata->extension.input,
> +                            ABS_Y, -30, 30, 2, 4);
> +       input_set_abs_params(wdata->extension.input,
> +                            ABS_HAT0X, -120, 120, 2, 4);
> +       input_set_abs_params(wdata->extension.input,
> +                            ABS_HAT1X, 0, 12, 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_guitar_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_guitar = {
> +       .flags = 0,
> +       .arg = 0,
> +       .probe = wiimod_guitar_probe,
> +       .remove = wiimod_guitar_remove,
> +       .in_ext = wiimod_guitar_in_ext,
> +};
> +
>  /* module table */
>
>  static const struct wiimod_ops wiimod_dummy;
> @@ -2201,4 +2400,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] = &wiimod_guitar,
>  };
> diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
> index 510ca77fe14e..c55c034281f0 100644
> --- a/drivers/hid/hid-wiimote.h
> +++ b/drivers/hid/hid-wiimote.h
> @@ -89,6 +89,7 @@ enum wiimote_exttype {
>         WIIMOTE_EXT_CLASSIC_CONTROLLER,
>         WIIMOTE_EXT_BALANCE_BOARD,
>         WIIMOTE_EXT_PRO_CONTROLLER,
> +       WIIMOTE_EXT_GUITAR,
>         WIIMOTE_EXT_NUM,
>  };
>
> --
> 2.16.2
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 579884ebd94d..8b18d104934f 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -449,6 +449,9 @@  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[0] == 0x00 && rmem[1] == 0x00 && rmem[2] == 0xa4 &&
+	    rmem[3] == 0x20 && rmem[4] == 0x01 && rmem[5] == 0x03)
+		return WIIMOTE_EXT_GUITAR;
 	if (rmem[4] == 0x01 && rmem[5] == 0x01)
 		return WIIMOTE_EXT_CLASSIC_CONTROLLER;
 	if (rmem[4] == 0x04 && rmem[5] == 0x02)
@@ -491,6 +494,7 @@  static bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype)
 		wmem = 0x07;
 		break;
 	case WIIMOTE_EXT_NUNCHUK:
+	case WIIMOTE_EXT_GUITAR:
 		wmem = 0x05;
 		break;
 	default:
@@ -1075,6 +1079,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] = "Guitar Hero 3 Les Paul Controller",
 };
 
 /*
@@ -1660,6 +1665,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:
+		return sprintf(buf, "guitar\n");
 	case WIIMOTE_EXT_UNKNOWN:
 		/* fallthrough */
 	default:
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index c830ed39348f..129f67ef7bd9 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -2176,6 +2176,205 @@  const struct wiimod_ops wiimod_mp = {
 	.in_mp = wiimod_mp_in_mp,
 };
 
+/*
+ * Guitar Hero 3 Gibson Les Paul Controller
+ * The Les Paul Guitar Controller is used by Guitar Hero 3 and is
+ * compatible with later Guitar Hero Wii games and Rockband games
+ * as well. The controller has 5 fret keys, strum up and down, an
+ * analog whammy bar, analog stick, and plus and minus keys.
+ */
+
+enum wiimod_guitar_keys {
+	WIIMOD_GUITAR_KEY_G,
+	WIIMOD_GUITAR_KEY_R,
+	WIIMOD_GUITAR_KEY_Y,
+	WIIMOD_GUITAR_KEY_B,
+	WIIMOD_GUITAR_KEY_O,
+	WIIMOD_GUITAR_KEY_UP,
+	WIIMOD_GUITAR_KEY_DOWN,
+	WIIMOD_GUITAR_KEY_PLUS,
+	WIIMOD_GUITAR_KEY_MINUS,
+	WIIMOD_GUITAR_KEY_NUM,
+};
+
+static const __u16 wiimod_guitar_map[] = {
+	BTN_1,		/* WIIMOD_GUITAR_KEY_G */
+	BTN_2,		/* WIIMOD_GUITAR_KEY_R */
+	BTN_3,		/* WIIMOD_GUITAR_KEY_Y */
+	BTN_4,		/* WIIMOD_GUITAR_KEY_B */
+	BTN_5,		/* WIIMOD_GUITAR_KEY_O */
+	BTN_DPAD_UP,	/* WIIMOD_GUITAR_KEY_UP */
+	BTN_DPAD_DOWN,	/* WIIMOD_GUITAR_KEY_DOWN */
+	BTN_START,		/* WIIMOD_GUITAR_KEY_PLUS */
+	BTN_SELECT,		/* WIIMOD_GUITAR_KEY_MINUS */
+};
+
+static void wiimod_guitar_in_ext(struct wiimote_data *wdata, const __u8 *ext)
+{
+	__s16 bx, by, bt, bw;
+
+	/*   Byte |  7 |  6 |  5 |  4 |  3 |  2 |  1 |  0 |
+	 *   -----+----+----+----+----+----+----+----+----+
+	 *    0   |         |           Button X          |
+	 *    1   |         |           Button Y          |
+	 *   -----+---------+----+------------------------+
+	 *    2   |              |       Button TB        |
+	 *    3   |              |       Button WB        |
+	 *   -----+----+----+----+----+----+----+---------+
+	 *    4   |    | BD |    | B- |    | B+ |         |
+	 *   -----+----+----+----+----+----+----+----+----+
+	 *    5   | BO | BR | BB | BG | BY |         | BU |
+	 *   -----+----+----+----+----+----+---------+----+
+	 * Button X/Y is the analog stick. Button TB, WB is the touch bar
+	 * on the neck of guitars supporting this feature, and the whammy bar.
+	 *
+	 * BD and BU are strum button down and strum button up.
+	 * B- and B+ are the minus and plus button at the base of the neck.
+	 *
+	 * BG, BR, BY, BB, BO are buttons green, red, yellow, blue, and orange.
+	 * All buttons are low active
+	 */
+
+	bx = ext[0];
+	by = ext[1];
+	bx -= 224;
+	by -= 224;
+
+	bt = ext[2];
+	bw = ext[3];
+	bw &= 0x1f;
+
+	bw -= 15;
+
+	input_report_abs(wdata->extension.input, ABS_X, bx);
+	input_report_abs(wdata->extension.input, ABS_Y, by);
+
+	input_report_abs(wdata->extension.input, ABS_HAT0X, bt);
+	input_report_abs(wdata->extension.input, ABS_HAT1X, bw);
+
+	input_report_key(wdata->extension.input,
+			wiimod_guitar_map[WIIMOD_GUITAR_KEY_G],
+			!(ext[5] & 0x10));
+	input_report_key(wdata->extension.input,
+			wiimod_guitar_map[WIIMOD_GUITAR_KEY_R],
+			!(ext[5] & 0x40));
+	input_report_key(wdata->extension.input,
+			wiimod_guitar_map[WIIMOD_GUITAR_KEY_Y],
+			!(ext[5] & 0x08));
+	input_report_key(wdata->extension.input,
+			wiimod_guitar_map[WIIMOD_GUITAR_KEY_B],
+			!(ext[5] & 0x20));
+	input_report_key(wdata->extension.input,
+			wiimod_guitar_map[WIIMOD_GUITAR_KEY_O],
+			!(ext[5] & 0x80));
+	input_report_key(wdata->extension.input,
+			wiimod_guitar_map[WIIMOD_GUITAR_KEY_UP],
+			!(ext[5] & 0x01));
+	input_report_key(wdata->extension.input,
+			wiimod_guitar_map[WIIMOD_GUITAR_KEY_DOWN],
+			!(ext[4] & 0x40));
+	input_report_key(wdata->extension.input,
+			wiimod_guitar_map[WIIMOD_GUITAR_KEY_PLUS],
+			!(ext[4] & 0x04));
+	input_report_key(wdata->extension.input,
+			wiimod_guitar_map[WIIMOD_GUITAR_KEY_MINUS],
+			!(ext[4] & 0x10));
+
+	input_sync(wdata->extension.input);
+}
+
+static int wiimod_guitar_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_guitar_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_guitar_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_guitar_open;
+	wdata->extension.input->close = wiimod_guitar_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 " Guitar";
+
+	set_bit(EV_KEY, wdata->extension.input->evbit);
+	for (i = 0; i < WIIMOD_GUITAR_KEY_NUM; ++i)
+		set_bit(wiimod_guitar_map[i],
+			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_HAT0X, wdata->extension.input->absbit);
+	set_bit(ABS_HAT1X, wdata->extension.input->absbit);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_X, -30, 30, 2, 4);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_Y, -30, 30, 2, 4);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT0X, -120, 120, 2, 4);
+	input_set_abs_params(wdata->extension.input,
+			     ABS_HAT1X, 0, 12, 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_guitar_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_guitar = {
+	.flags = 0,
+	.arg = 0,
+	.probe = wiimod_guitar_probe,
+	.remove = wiimod_guitar_remove,
+	.in_ext = wiimod_guitar_in_ext,
+};
+
 /* module table */
 
 static const struct wiimod_ops wiimod_dummy;
@@ -2201,4 +2400,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] = &wiimod_guitar,
 };
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 510ca77fe14e..c55c034281f0 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -89,6 +89,7 @@  enum wiimote_exttype {
 	WIIMOTE_EXT_CLASSIC_CONTROLLER,
 	WIIMOTE_EXT_BALANCE_BOARD,
 	WIIMOTE_EXT_PRO_CONTROLLER,
+	WIIMOTE_EXT_GUITAR,
 	WIIMOTE_EXT_NUM,
 };