diff mbox

input: Add Nintendo extension controller driver

Message ID 20110527081251.3992.27574.stgit@ponder (mailing list archive)
State New, archived
Headers show

Commit Message

Grant Likely May 27, 2011, 8:14 a.m. UTC
This driver adds support for Nintendo Wiimote extension controllers
directly attached to an i2c bus, such as when using an adaptor like
the Wiichuck.

v3: - Fixed and tested classic controller connection
    - Fixed comment block
v2: - Fixed DMA buffers on stack problem
    - added classic controller support

Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
I've actually got my hands on a classic controller now, so this driver
has been tested with both nunchuck and classic controller.  I leave adding
support for the motionplus to someone who actually has the hardware.  :)

This driver should be ready for merging now.

g.

 drivers/input/joystick/Kconfig    |   13 +
 drivers/input/joystick/Makefile   |    1 
 drivers/input/joystick/wiichuck.c |  435 +++++++++++++++++++++++++++++++++++++
 3 files changed, 449 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/joystick/wiichuck.c


--
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

Comments

Grant Likely Aug. 1, 2011, 4:04 p.m. UTC | #1
Hi Dmitry,

Any thoughts on this patch?  Can you send it on to Linus?

Thanks,
g.

On Fri, May 27, 2011 at 9:14 AM, Grant Likely <grant.likely@secretlab.ca> wrote:
> This driver adds support for Nintendo Wiimote extension controllers
> directly attached to an i2c bus, such as when using an adaptor like
> the Wiichuck.
>
> v3: - Fixed and tested classic controller connection
>    - Fixed comment block
> v2: - Fixed DMA buffers on stack problem
>    - added classic controller support
>
> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
> ---
> I've actually got my hands on a classic controller now, so this driver
> has been tested with both nunchuck and classic controller.  I leave adding
> support for the motionplus to someone who actually has the hardware.  :)
>
> This driver should be ready for merging now.
>
> g.
>
>  drivers/input/joystick/Kconfig    |   13 +
>  drivers/input/joystick/Makefile   |    1
>  drivers/input/joystick/wiichuck.c |  435 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 449 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/input/joystick/wiichuck.c
>
> diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
> index 56eb471..5b7fba7 100644
> --- a/drivers/input/joystick/Kconfig
> +++ b/drivers/input/joystick/Kconfig
> @@ -193,6 +193,19 @@ config JOYSTICK_TWIDJOY
>          To compile this driver as a module, choose M here: the
>          module will be called twidjoy.
>
> +config JOYSTICK_WIICHUCK
> +       tristate "Nintendo Wiimote Extension connector on i2c bus"
> +       depends on I2C
> +       select INPUT_POLLDEV
> +       help
> +         Say Y here if you have a Nintendo Wiimote extension connector
> +         attached directly to an i2c bus, like the Sparcfun Wiichuck adapter
> +         board.  This driver supports both the Nunchuck and the Classic
> +         Controller extensions.
> +
> +         To compile this driver as a module, choose M here: the
> +         modules will be called wiichuck.
> +
>  config JOYSTICK_ZHENHUA
>        tristate "5-byte Zhenhua RC transmitter"
>        select SERIO
> diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
> index 92dc0de..78466d6 100644
> --- a/drivers/input/joystick/Makefile
> +++ b/drivers/input/joystick/Makefile
> @@ -29,6 +29,7 @@ obj-$(CONFIG_JOYSTICK_TMDC)           += tmdc.o
>  obj-$(CONFIG_JOYSTICK_TURBOGRAFX)      += turbografx.o
>  obj-$(CONFIG_JOYSTICK_TWIDJOY)         += twidjoy.o
>  obj-$(CONFIG_JOYSTICK_WARRIOR)         += warrior.o
> +obj-$(CONFIG_JOYSTICK_WIICHUCK)                += wiichuck.o
>  obj-$(CONFIG_JOYSTICK_XPAD)            += xpad.o
>  obj-$(CONFIG_JOYSTICK_ZHENHUA)         += zhenhua.o
>  obj-$(CONFIG_JOYSTICK_WALKERA0701)     += walkera0701.o
> diff --git a/drivers/input/joystick/wiichuck.c b/drivers/input/joystick/wiichuck.c
> new file mode 100644
> index 0000000..ab26c34b
> --- /dev/null
> +++ b/drivers/input/joystick/wiichuck.c
> @@ -0,0 +1,435 @@
> +/*
> + * i2c Wiichuck driver (Nintendo Wiimote accessory connector)
> + *
> + * This driver supports Nintendo Wiimote accessories like the Nunchuck and
> + * the Classic Controller connected to an i2c bus.
> + *
> + * Copyright (c) 2011 Secret Lab Technologies Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of version 2 of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This driver uses the polled input device abstraction to implement an
> + * input driver for Nintendo expansion devices wired up to an i2c bus.
> + *
> + * A state machine implements the protocol handling.  It starts in the
> + * DISCONNECTED state initially and polls every second waiting for a
> + * device to get attached, and reading the device id when one does.
> + * If the device is recognized, then the polling period is bumped
> + * to 100ms, and the state machine enters into a loop reading the data
> + * capture reports out of the controller.  If at any time the device
> + * is disconnected, then it goes back to DISCONNECTED state and bumps
> + * the polling frequency back to 1 second.
> + *
> + * A callback is implemented for each supported devices, currently the
> + * Nunchuck and the Classic Controller.  Wii Motion Plus has yet to be
> + * added.
> + */
> +
> +#include <linux/cache.h>
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/input.h>
> +#include <linux/input-polldev.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/slab.h>
> +
> +MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
> +MODULE_DESCRIPTION("Wiichuck (i2c Nintendo Wiimote accessory) driver");
> +MODULE_LICENSE("GPL");
> +
> +#define WIICHUCK_POLL_PERIOD   (1000)  /* 1 second */
> +#ifdef DEBUG
> +#define WIICHUCK_CAPTURE_PERIOD (500)  /* 1/2 second for debug */
> +#else
> +#define WIICHUCK_CAPTURE_PERIOD (100)  /* 100 milliseconds */
> +#endif /* DEBUG */
> +
> +enum wiichuck_state {
> +       WIICHUCK_STATE_DISCONNECTED = 0,
> +       WIICHUCK_STATE_DATA,
> +};
> +
> +struct wiichuck_device {
> +       struct input_polled_dev *poll_dev;
> +       struct i2c_client *i2c_client;
> +       int (*process)(struct wiichuck_device *wiichuck);
> +       enum wiichuck_state state;
> +
> +       /* transfer buffer, aligned to cache line so it can be used with DMA */
> +       uint8_t buf[6] ____cacheline_aligned;
> +};
> +
> +static int wiichuck_transfer(struct wiichuck_device *wiichuck,
> +                            int len, bool read)
> +{
> +       struct i2c_client *i2c = wiichuck->i2c_client;
> +       struct i2c_msg msg = {
> +               .addr = i2c->addr, .len = len, .buf = wiichuck->buf };
> +
> +       if (read)
> +               msg.flags = I2C_M_RD;
> +       return i2c_transfer(i2c->adapter, &msg, 1);
> +}
> +
> +static int wiichuck_write(struct wiichuck_device *wiichuck,
> +                          uint8_t addr, uint8_t val)
> +{
> +       wiichuck->buf[0] = addr;
> +       wiichuck->buf[1] = val;
> +       return wiichuck_transfer(wiichuck, 2, false);
> +}
> +
> +static inline int wiichuck_setaddr(struct wiichuck_device *wiichuck,
> +                                   uint8_t addr)
> +{
> +       wiichuck->buf[0] = addr;
> +       return wiichuck_transfer(wiichuck, 1, false);
> +}
> +
> +static inline int wiichuck_read(struct wiichuck_device *wiichuck, int len)
> +{
> +       return wiichuck_transfer(wiichuck, len, true);
> +}
> +
> +static inline bool WIIBTN(int val, int bit)
> +{
> +       return (val & BIT(bit)) == 0;
> +}
> +
> +static int wiichuck_process_unknown(struct wiichuck_device *wiichuck)
> +{
> +       return 0;
> +}
> +
> +static int wiichuck_process_nunchuck(struct wiichuck_device *wiichuck)
> +{
> +       struct input_dev *input_dev = wiichuck->poll_dev->input;
> +       uint8_t *b = wiichuck->buf;
> +       int ax, ay, az, rc;
> +
> +       rc = wiichuck_read(wiichuck, 6);
> +       if (rc < 0)
> +               return rc;
> +
> +       ax = (b[2] << 2) | ((b[5] >> 2) & 0x3);
> +       ay = (b[3] << 2) | ((b[5] >> 4) & 0x3);
> +       az = (b[4] << 2) | ((b[5] >> 6) & 0x3);
> +
> +       input_report_abs(input_dev, ABS_X, b[0]);
> +       input_report_abs(input_dev, ABS_Y, b[1]);
> +       input_report_abs(input_dev, ABS_RX, ax);
> +       input_report_abs(input_dev, ABS_RY, ax);
> +       input_report_abs(input_dev, ABS_RZ, ay);
> +       input_report_key(input_dev, BTN_C, WIIBTN(b[5], 1));
> +       input_report_key(input_dev, BTN_Z, WIIBTN(b[5], 0));
> +       input_sync(input_dev);
> +
> +       dev_dbg(&wiichuck->i2c_client->dev,
> +               "nunchuck: j=%.3i,%.3i a=%.3x,%.3x,%.3x c,z=%i,%i\n",
> +               b[0],b[1], ax,ay,az, WIIBTN(b[5], 1), WIIBTN(b[5], 0));
> +       return 0;
> +};
> +
> +static int wiichuck_process_classic(struct wiichuck_device *wiichuck)
> +{
> +       struct input_dev *input_dev = wiichuck->poll_dev->input;
> +       uint8_t *b = wiichuck->buf;
> +       int lx, ly, lt, rx, ry, rt, rc;
> +
> +       rc = wiichuck_read(wiichuck, 6);
> +       if (rc < 0)
> +               return rc;
> +
> +       /* Analog measurements; some values are split between registers */
> +       rx = ((b[0] >> 3) & 0x18) | ((b[1] >> 5) & 6) | ((b[2] >> 7) & 1);
> +       ry = b[2] & 0x1f;
> +       rt = b[3] & 0x1f;
> +       lx = b[0] & 0x3f;
> +       ly = b[1] & 0x3f;
> +       lt = ((b[2] >> 2) & 0x18) | ((b[3] >> 5) & 7);
> +
> +       input_report_abs(input_dev, ABS_HAT0X, lx); /* left joystick */
> +       input_report_abs(input_dev, ABS_HAT0Y, ly);
> +       input_report_abs(input_dev, ABS_BRAKE, lt); /* left trigger */
> +       input_report_abs(input_dev, ABS_HAT1X, rx); /* right joystick */
> +       input_report_abs(input_dev, ABS_HAT1Y, ry);
> +       input_report_abs(input_dev, ABS_GAS, rt); /* right trigger */
> +
> +       /* D-pad */
> +       input_report_abs(input_dev, ABS_HAT2X, WIIBTN(b[4],7) - WIIBTN(b[5],1));
> +       input_report_abs(input_dev, ABS_HAT2Y, WIIBTN(b[4],6) - WIIBTN(b[5],0));
> +
> +       /* Buttons */
> +       input_report_key(input_dev, BTN_TL, WIIBTN(b[4], 5)); /* left trigger */
> +       input_report_key(input_dev, BTN_SELECT, WIIBTN(b[4], 4)); /* minus */
> +       input_report_key(input_dev, BTN_MODE, WIIBTN(b[4], 3)); /* home */
> +       input_report_key(input_dev, BTN_START, WIIBTN(b[4], 2)); /* plus */
> +       input_report_key(input_dev, BTN_TR, WIIBTN(b[4], 1)); /* right trigger */
> +
> +       input_report_key(input_dev, BTN_TL2, WIIBTN(b[5], 7)); /* left z */
> +       input_report_key(input_dev, BTN_B, WIIBTN(b[5], 6));
> +       input_report_key(input_dev, BTN_Y, WIIBTN(b[5], 5));
> +       input_report_key(input_dev, BTN_A, WIIBTN(b[5], 4));
> +       input_report_key(input_dev, BTN_X, WIIBTN(b[5], 3));
> +       input_report_key(input_dev, BTN_TR2, WIIBTN(b[5], 2)); /* right z */
> +       input_sync(input_dev);
> +
> +       dev_dbg(&wiichuck->i2c_client->dev,
> +               "classic: r=%.3i,%.3i,%.3i l=%.3x,%.3x,%.3x b=%.2x,%.2x\n",
> +               lx,ly,lt, rx,ry,rt, b[4],b[5]);
> +       return 0;
> +};
> +
> +/**
> + * wiichuck_poll() - Protocol state machine implementation
> + *
> + * A state machine is used here to keep the protocol processing
> + * reentrant.  Any of the i2c transactions will indeed sleep because
> + * i2c transactions are slow, but there is a mandatory delay between
> + * triggering the data capture (set address) and reading the data
> + * back.  Using a state machine means the poll function can return and
> + * free up the worker thread while waiting for the data.
> + */
> +static void wiichuck_poll(struct input_polled_dev *poll_dev)
> +{
> +       struct wiichuck_device *wiichuck = poll_dev->private;
> +       int id;
> +
> +       switch (wiichuck->state) {
> +       case WIICHUCK_STATE_DISCONNECTED:
> +               /* Disable encryption */
> +               if (wiichuck_write(wiichuck, 0xf0, 0x55) < 0)
> +                       return;
> +               if (wiichuck_write(wiichuck, 0xfb, 0x00) < 0)
> +                       return;
> +
> +               /* Read device id */
> +               if (wiichuck_setaddr(wiichuck, 0xfe) < 0)
> +                       return;
> +               if (wiichuck_read(wiichuck, 2) < 0)
> +                       return;
> +               id = (wiichuck->buf[0] << 8) | wiichuck->buf[1];
> +
> +               switch (id) {
> +                       case 0x0000: /* Nunchuck */
> +                               wiichuck->process = wiichuck_process_nunchuck;
> +                               dev_info(&wiichuck->i2c_client->dev,
> +                                        "Connected Nunchuck\n");
> +                               break;
> +
> +                       case 0x0101: /* Classic Controller */
> +                               wiichuck->process = wiichuck_process_classic;
> +                               dev_info(&wiichuck->i2c_client->dev,
> +                                        "Connected Classic Controller\n");
> +                               break;
> +
> +                       default: /* No connection or unsupported device */
> +                               wiichuck->process = wiichuck_process_unknown;
> +                               dev_dbg(&wiichuck->i2c_client->dev,
> +                                        "Connected unknown id: %x\n", id);
> +                               return;
> +               }
> +
> +               /* Setup the first data transfer */
> +               if (wiichuck_setaddr(wiichuck, 0) < 0)
> +                       return;
> +
> +               wiichuck->state = WIICHUCK_STATE_DATA;
> +               poll_dev->poll_interval = WIICHUCK_CAPTURE_PERIOD;
> +               break;
> +
> +       case WIICHUCK_STATE_DATA:
> +               /* Read the input report */
> +               if (wiichuck->process(wiichuck) < 0)
> +                       goto disconnect;
> +
> +               /* Setup the next transfer */
> +               if (wiichuck_setaddr(wiichuck, 0) < 0)
> +                       goto disconnect;
> +               break;
> +
> +       default:
> +               goto disconnect;
> +       }
> +       return;
> +
> + disconnect:
> +       dev_info(&wiichuck->i2c_client->dev, "disconnected\n");
> +       wiichuck->state = WIICHUCK_STATE_DISCONNECTED;
> +       poll_dev->poll_interval = WIICHUCK_POLL_PERIOD;
> +}
> +
> +/**
> + * wiichuck_open() - Set up initial state machine state
> + *
> + * Called exactly once the first time the device is opened by
> + * userspace.  Will not be called again unless all users close it
> + * before reopening.  This simply clears the state to disconnected and
> + * sets the poll rate back to the slow speed
> + */
> +static void wiichuck_open(struct input_polled_dev *poll_dev)
> +{
> +       struct wiichuck_device *wiichuck = poll_dev->private;
> +
> +       wiichuck->process = wiichuck_process_unknown;
> +       wiichuck->state = WIICHUCK_STATE_DISCONNECTED;
> +       wiichuck->poll_dev->poll_interval = WIICHUCK_POLL_PERIOD;
> +}
> +
> +static int __devinit wiichuck_probe(struct i2c_client *client,
> +                               const struct i2c_device_id *id)
> +{
> +       struct wiichuck_device *wiichuck;
> +       struct input_polled_dev *poll_dev;
> +       struct input_dev *input_dev;
> +       int rc;
> +
> +       wiichuck = kzalloc(sizeof(*wiichuck), GFP_KERNEL);
> +       if (!wiichuck)
> +               return -ENOMEM;
> +
> +       poll_dev = input_allocate_polled_device();
> +       if (!poll_dev) {
> +               rc = -ENOMEM;
> +               goto err_alloc;
> +       }
> +
> +       wiichuck->i2c_client = client;
> +       wiichuck->poll_dev = poll_dev;
> +
> +       poll_dev->private = wiichuck;
> +       poll_dev->poll = wiichuck_poll;
> +       poll_dev->open = wiichuck_open;
> +
> +       input_dev = poll_dev->input;
> +       input_dev->name = "Wiichuck expansion connector";
> +       input_dev->id.bustype = BUS_I2C;
> +       input_dev->dev.parent = &client->dev;
> +
> +       /* Declare the events generated by this driver */
> +       __set_bit(EV_ABS, input_dev->evbit);
> +       __set_bit(EV_KEY, input_dev->evbit);
> +
> +       /* Nunchuck ranges */
> +       __set_bit(ABS_X, input_dev->absbit); /* joystick */
> +       __set_bit(ABS_Y, input_dev->absbit);
> +       input_set_abs_params(input_dev, ABS_X, 30, 220, 4, 8);
> +       input_set_abs_params(input_dev, ABS_Y, 40, 200, 4, 8);
> +
> +       __set_bit(ABS_RX, input_dev->absbit); /* accelerometer */
> +       __set_bit(ABS_RY, input_dev->absbit);
> +       __set_bit(ABS_RZ, input_dev->absbit);
> +       input_set_abs_params(input_dev, ABS_RX, 0, 0x3ff, 4, 8);
> +       input_set_abs_params(input_dev, ABS_RY, 0, 0x3ff, 4, 8);
> +       input_set_abs_params(input_dev, ABS_RZ, 0, 0x3ff, 4, 8);
> +
> +       /* Nunchuck buttons */
> +       __set_bit(BTN_C, input_dev->keybit);
> +       __set_bit(BTN_Z, input_dev->keybit);
> +
> +       /* Classic ranges */
> +       __set_bit(ABS_HAT0X, input_dev->absbit); /* Left Joystick */
> +       __set_bit(ABS_HAT0Y, input_dev->absbit);
> +       input_set_abs_params(input_dev, ABS_HAT0X, 0, 63, 2, 4);
> +       input_set_abs_params(input_dev, ABS_HAT0Y, 0, 63, 2, 4);
> +
> +       __set_bit(ABS_HAT1X, input_dev->absbit); /* Right Joystick */
> +       __set_bit(ABS_HAT1Y, input_dev->absbit);
> +       input_set_abs_params(input_dev, ABS_HAT1X, 0, 31, 1, 2);
> +       input_set_abs_params(input_dev, ABS_HAT1Y, 0, 31, 1, 2);
> +
> +       __set_bit(ABS_HAT2X, input_dev->absbit); /* D-pad */
> +       __set_bit(ABS_HAT2Y, input_dev->absbit);
> +       input_set_abs_params(input_dev, ABS_HAT2X, -1, 1, 0, 0);
> +       input_set_abs_params(input_dev, ABS_HAT2Y, -1, 1, 0, 0);
> +
> +       __set_bit(ABS_BRAKE, input_dev->absbit); /* Left trigger */
> +       input_set_abs_params(input_dev, ABS_BRAKE, 0, 31, 1, 2);
> +       __set_bit(ABS_GAS, input_dev->absbit);
> +       input_set_abs_params(input_dev, ABS_GAS, 0, 31, 1, 2);
> +
> +       /* Classic buttons */
> +       __set_bit(BTN_TL, input_dev->keybit);
> +       __set_bit(BTN_SELECT, input_dev->keybit);
> +       __set_bit(BTN_MODE, input_dev->keybit);
> +       __set_bit(BTN_START, input_dev->keybit);
> +       __set_bit(BTN_TR, input_dev->keybit);
> +       __set_bit(BTN_TL2, input_dev->keybit);
> +       __set_bit(BTN_B, input_dev->keybit);
> +       __set_bit(BTN_Y, input_dev->keybit);
> +       __set_bit(BTN_A, input_dev->keybit);
> +       __set_bit(BTN_X, input_dev->keybit);
> +       __set_bit(BTN_TR2, input_dev->keybit);
> +
> +       i2c_set_clientdata(client, wiichuck);
> +
> +       /* Register the device; it is 'live' after this point */
> +       rc = input_register_polled_device(wiichuck->poll_dev);
> +       if (rc) {
> +               dev_err(&client->dev, "Failed to register input device\n");
> +               goto err_register;
> +       }
> +
> +       return 0;
> +
> + err_register:
> +       i2c_set_clientdata(client, NULL);
> +       input_free_polled_device(poll_dev);
> + err_alloc:
> +       kfree(wiichuck);
> +
> +       return rc;
> +}
> +
> +static int __devexit wiichuck_remove(struct i2c_client *client)
> +{
> +       struct wiichuck_device *wiichuck = i2c_get_clientdata(client);
> +
> +       i2c_set_clientdata(client, NULL);
> +       input_unregister_polled_device(wiichuck->poll_dev);
> +       input_free_polled_device(wiichuck->poll_dev);
> +       kfree(wiichuck);
> +
> +       return 0;
> +}
> +
> +static const struct i2c_device_id wiichuck_id[] = {
> +       { "wiichuck", 0 },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(i2c, wiichuck_id);
> +
> +#ifdef CONFIG_OF
> +static struct of_device_id wiichuck_match_table[] __devinitdata = {
> +       { .compatible = "nintendo,wiimote-extension", },
> +       { }
> +};
> +#else
> +#define wiichuck_match_table NULL
> +#endif
> +
> +static struct i2c_driver wiichuck_driver = {
> +       .driver = {
> +               .name = "wiichuck",
> +               .owner = THIS_MODULE,
> +               .of_match_table = wiichuck_match_table,
> +       },
> +       .probe          = wiichuck_probe,
> +       .remove         = __devexit_p(wiichuck_remove),
> +       .id_table       = wiichuck_id,
> +};
> +
> +static int __init wiichuck_init(void)
> +{
> +       return i2c_add_driver(&wiichuck_driver);
> +}
> +module_init(wiichuck_init);
> +
> +static void __exit wiichuck_exit(void)
> +{
> +       i2c_del_driver(&wiichuck_driver);
> +}
> +module_exit(wiichuck_exit);
>
>
Dmitry Torokhov Aug. 1, 2011, 4:38 p.m. UTC | #2
Hi Grant,

On Mon, Aug 01, 2011 at 05:04:37PM +0100, Grant Likely wrote:
> Hi Dmitry,
> 
> Any thoughts on this patch?  Can you send it on to Linus?
> 

I thought I responded earlier but apparently not...

The issue with the driver is that it pre-registers an input device with
a super-set of capabilities instead of waiting until the device is
properly identified and registering input device with the exact
capabilities.

The input policy is that device's stated capabilities should match the
physical device (within the constraints of input framework).

Thanks.
Grant Likely Aug. 1, 2011, 4:44 p.m. UTC | #3
On Mon, Aug 01, 2011 at 09:38:24AM -0700, Dmitry Torokhov wrote:
> Hi Grant,
> 
> On Mon, Aug 01, 2011 at 05:04:37PM +0100, Grant Likely wrote:
> > Hi Dmitry,
> > 
> > Any thoughts on this patch?  Can you send it on to Linus?
> > 
> 
> I thought I responded earlier but apparently not...
> 
> The issue with the driver is that it pre-registers an input device with
> a super-set of capabilities instead of waiting until the device is
> properly identified and registering input device with the exact
> capabilities.
> 
> The input policy is that device's stated capabilities should match the
> physical device (within the constraints of input framework).

Okay, that's solvable.  I'll rework and repost with a goal to merge in v3.2

g.

--
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/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 56eb471..5b7fba7 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -193,6 +193,19 @@  config JOYSTICK_TWIDJOY
 	  To compile this driver as a module, choose M here: the
 	  module will be called twidjoy.
 
+config JOYSTICK_WIICHUCK
+	tristate "Nintendo Wiimote Extension connector on i2c bus"
+	depends on I2C
+	select INPUT_POLLDEV
+	help
+	  Say Y here if you have a Nintendo Wiimote extension connector
+	  attached directly to an i2c bus, like the Sparcfun Wiichuck adapter
+	  board.  This driver supports both the Nunchuck and the Classic
+	  Controller extensions.
+
+	  To compile this driver as a module, choose M here: the
+	  modules will be called wiichuck.
+
 config JOYSTICK_ZHENHUA
 	tristate "5-byte Zhenhua RC transmitter"
 	select SERIO
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index 92dc0de..78466d6 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -29,6 +29,7 @@  obj-$(CONFIG_JOYSTICK_TMDC)		+= tmdc.o
 obj-$(CONFIG_JOYSTICK_TURBOGRAFX)	+= turbografx.o
 obj-$(CONFIG_JOYSTICK_TWIDJOY)		+= twidjoy.o
 obj-$(CONFIG_JOYSTICK_WARRIOR)		+= warrior.o
+obj-$(CONFIG_JOYSTICK_WIICHUCK)		+= wiichuck.o
 obj-$(CONFIG_JOYSTICK_XPAD)		+= xpad.o
 obj-$(CONFIG_JOYSTICK_ZHENHUA)		+= zhenhua.o
 obj-$(CONFIG_JOYSTICK_WALKERA0701)	+= walkera0701.o
diff --git a/drivers/input/joystick/wiichuck.c b/drivers/input/joystick/wiichuck.c
new file mode 100644
index 0000000..ab26c34b
--- /dev/null
+++ b/drivers/input/joystick/wiichuck.c
@@ -0,0 +1,435 @@ 
+/*
+ * i2c Wiichuck driver (Nintendo Wiimote accessory connector)
+ *
+ * This driver supports Nintendo Wiimote accessories like the Nunchuck and
+ * the Classic Controller connected to an i2c bus.
+ *
+ * Copyright (c) 2011 Secret Lab Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This driver uses the polled input device abstraction to implement an
+ * input driver for Nintendo expansion devices wired up to an i2c bus.
+ *
+ * A state machine implements the protocol handling.  It starts in the
+ * DISCONNECTED state initially and polls every second waiting for a
+ * device to get attached, and reading the device id when one does.
+ * If the device is recognized, then the polling period is bumped
+ * to 100ms, and the state machine enters into a loop reading the data
+ * capture reports out of the controller.  If at any time the device
+ * is disconnected, then it goes back to DISCONNECTED state and bumps
+ * the polling frequency back to 1 second.
+ *
+ * A callback is implemented for each supported devices, currently the
+ * Nunchuck and the Classic Controller.  Wii Motion Plus has yet to be
+ * added.
+ */
+
+#include <linux/cache.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/mod_devicetable.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_DESCRIPTION("Wiichuck (i2c Nintendo Wiimote accessory) driver");
+MODULE_LICENSE("GPL");
+
+#define WIICHUCK_POLL_PERIOD	(1000)	/* 1 second */
+#ifdef DEBUG
+#define WIICHUCK_CAPTURE_PERIOD (500)	/* 1/2 second for debug */
+#else
+#define WIICHUCK_CAPTURE_PERIOD (100)	/* 100 milliseconds */
+#endif /* DEBUG */
+
+enum wiichuck_state {
+	WIICHUCK_STATE_DISCONNECTED = 0,
+	WIICHUCK_STATE_DATA,
+};
+
+struct wiichuck_device {
+	struct input_polled_dev *poll_dev;
+	struct i2c_client *i2c_client;
+	int (*process)(struct wiichuck_device *wiichuck);
+	enum wiichuck_state state;
+
+	/* transfer buffer, aligned to cache line so it can be used with DMA */
+	uint8_t buf[6] ____cacheline_aligned;
+};
+
+static int wiichuck_transfer(struct wiichuck_device *wiichuck,
+			     int len, bool read)
+{
+	struct i2c_client *i2c = wiichuck->i2c_client;
+	struct i2c_msg msg = {
+		.addr = i2c->addr, .len = len, .buf = wiichuck->buf };
+
+	if (read)
+		msg.flags = I2C_M_RD;
+	return i2c_transfer(i2c->adapter, &msg, 1);
+}
+
+static int wiichuck_write(struct wiichuck_device *wiichuck,
+			   uint8_t addr, uint8_t val)
+{
+	wiichuck->buf[0] = addr;
+	wiichuck->buf[1] = val;
+	return wiichuck_transfer(wiichuck, 2, false);
+}
+
+static inline int wiichuck_setaddr(struct wiichuck_device *wiichuck,
+				    uint8_t addr)
+{
+	wiichuck->buf[0] = addr;
+	return wiichuck_transfer(wiichuck, 1, false);
+}
+
+static inline int wiichuck_read(struct wiichuck_device *wiichuck, int len)
+{
+	return wiichuck_transfer(wiichuck, len, true);
+}
+
+static inline bool WIIBTN(int val, int bit)
+{
+	return (val & BIT(bit)) == 0;
+}
+
+static int wiichuck_process_unknown(struct wiichuck_device *wiichuck)
+{
+	return 0;
+}
+
+static int wiichuck_process_nunchuck(struct wiichuck_device *wiichuck)
+{
+	struct input_dev *input_dev = wiichuck->poll_dev->input;
+	uint8_t *b = wiichuck->buf;
+	int ax, ay, az, rc;
+
+	rc = wiichuck_read(wiichuck, 6);
+	if (rc < 0)
+		return rc;
+
+	ax = (b[2] << 2) | ((b[5] >> 2) & 0x3);
+	ay = (b[3] << 2) | ((b[5] >> 4) & 0x3);
+	az = (b[4] << 2) | ((b[5] >> 6) & 0x3);
+
+	input_report_abs(input_dev, ABS_X, b[0]);
+	input_report_abs(input_dev, ABS_Y, b[1]);
+	input_report_abs(input_dev, ABS_RX, ax);
+	input_report_abs(input_dev, ABS_RY, ax);
+	input_report_abs(input_dev, ABS_RZ, ay);
+	input_report_key(input_dev, BTN_C, WIIBTN(b[5], 1));
+	input_report_key(input_dev, BTN_Z, WIIBTN(b[5], 0));
+	input_sync(input_dev);
+
+	dev_dbg(&wiichuck->i2c_client->dev,
+		"nunchuck: j=%.3i,%.3i a=%.3x,%.3x,%.3x c,z=%i,%i\n",
+		b[0],b[1], ax,ay,az, WIIBTN(b[5], 1), WIIBTN(b[5], 0));
+	return 0;
+};
+
+static int wiichuck_process_classic(struct wiichuck_device *wiichuck)
+{
+	struct input_dev *input_dev = wiichuck->poll_dev->input;
+	uint8_t *b = wiichuck->buf;
+	int lx, ly, lt, rx, ry, rt, rc;
+
+	rc = wiichuck_read(wiichuck, 6);
+	if (rc < 0)
+		return rc;
+
+	/* Analog measurements; some values are split between registers */
+	rx = ((b[0] >> 3) & 0x18) | ((b[1] >> 5) & 6) | ((b[2] >> 7) & 1);
+	ry = b[2] & 0x1f;
+	rt = b[3] & 0x1f;
+	lx = b[0] & 0x3f;
+	ly = b[1] & 0x3f;
+	lt = ((b[2] >> 2) & 0x18) | ((b[3] >> 5) & 7);
+
+	input_report_abs(input_dev, ABS_HAT0X, lx); /* left joystick */
+	input_report_abs(input_dev, ABS_HAT0Y, ly);
+	input_report_abs(input_dev, ABS_BRAKE, lt); /* left trigger */
+	input_report_abs(input_dev, ABS_HAT1X, rx); /* right joystick */
+	input_report_abs(input_dev, ABS_HAT1Y, ry);
+	input_report_abs(input_dev, ABS_GAS, rt); /* right trigger */
+
+	/* D-pad */
+	input_report_abs(input_dev, ABS_HAT2X, WIIBTN(b[4],7) - WIIBTN(b[5],1));
+	input_report_abs(input_dev, ABS_HAT2Y, WIIBTN(b[4],6) - WIIBTN(b[5],0));
+
+	/* Buttons */
+	input_report_key(input_dev, BTN_TL, WIIBTN(b[4], 5)); /* left trigger */
+	input_report_key(input_dev, BTN_SELECT, WIIBTN(b[4], 4)); /* minus */
+	input_report_key(input_dev, BTN_MODE, WIIBTN(b[4], 3)); /* home */
+	input_report_key(input_dev, BTN_START, WIIBTN(b[4], 2)); /* plus */
+	input_report_key(input_dev, BTN_TR, WIIBTN(b[4], 1)); /* right trigger */
+
+	input_report_key(input_dev, BTN_TL2, WIIBTN(b[5], 7)); /* left z */
+	input_report_key(input_dev, BTN_B, WIIBTN(b[5], 6));
+	input_report_key(input_dev, BTN_Y, WIIBTN(b[5], 5));
+	input_report_key(input_dev, BTN_A, WIIBTN(b[5], 4));
+	input_report_key(input_dev, BTN_X, WIIBTN(b[5], 3));
+	input_report_key(input_dev, BTN_TR2, WIIBTN(b[5], 2)); /* right z */
+	input_sync(input_dev);
+
+	dev_dbg(&wiichuck->i2c_client->dev,
+		"classic: r=%.3i,%.3i,%.3i l=%.3x,%.3x,%.3x b=%.2x,%.2x\n",
+		lx,ly,lt, rx,ry,rt, b[4],b[5]);
+	return 0;
+};
+
+/**
+ * wiichuck_poll() - Protocol state machine implementation
+ *
+ * A state machine is used here to keep the protocol processing
+ * reentrant.  Any of the i2c transactions will indeed sleep because
+ * i2c transactions are slow, but there is a mandatory delay between
+ * triggering the data capture (set address) and reading the data
+ * back.  Using a state machine means the poll function can return and
+ * free up the worker thread while waiting for the data.
+ */
+static void wiichuck_poll(struct input_polled_dev *poll_dev)
+{
+	struct wiichuck_device *wiichuck = poll_dev->private;
+	int id;
+
+	switch (wiichuck->state) {
+	case WIICHUCK_STATE_DISCONNECTED:
+		/* Disable encryption */
+		if (wiichuck_write(wiichuck, 0xf0, 0x55) < 0)
+			return;
+		if (wiichuck_write(wiichuck, 0xfb, 0x00) < 0)
+			return;
+
+		/* Read device id */
+		if (wiichuck_setaddr(wiichuck, 0xfe) < 0)
+			return;
+		if (wiichuck_read(wiichuck, 2) < 0)
+			return;
+		id = (wiichuck->buf[0] << 8) | wiichuck->buf[1];
+
+		switch (id) {
+			case 0x0000: /* Nunchuck */
+				wiichuck->process = wiichuck_process_nunchuck;
+				dev_info(&wiichuck->i2c_client->dev,
+					 "Connected Nunchuck\n");
+				break;
+
+			case 0x0101: /* Classic Controller */
+				wiichuck->process = wiichuck_process_classic;
+				dev_info(&wiichuck->i2c_client->dev,
+					 "Connected Classic Controller\n");
+				break;
+
+			default: /* No connection or unsupported device */
+				wiichuck->process = wiichuck_process_unknown;
+				dev_dbg(&wiichuck->i2c_client->dev,
+					 "Connected unknown id: %x\n", id);
+				return;
+		}
+
+		/* Setup the first data transfer */
+		if (wiichuck_setaddr(wiichuck, 0) < 0)
+			return;
+
+		wiichuck->state = WIICHUCK_STATE_DATA;
+		poll_dev->poll_interval = WIICHUCK_CAPTURE_PERIOD;
+		break;
+
+	case WIICHUCK_STATE_DATA:
+		/* Read the input report */
+		if (wiichuck->process(wiichuck) < 0)
+			goto disconnect;
+
+		/* Setup the next transfer */
+		if (wiichuck_setaddr(wiichuck, 0) < 0)
+			goto disconnect;
+		break;
+
+	default:
+		goto disconnect;
+	}
+	return;
+
+ disconnect:
+	dev_info(&wiichuck->i2c_client->dev, "disconnected\n");
+	wiichuck->state = WIICHUCK_STATE_DISCONNECTED;
+	poll_dev->poll_interval = WIICHUCK_POLL_PERIOD;
+}
+
+/**
+ * wiichuck_open() - Set up initial state machine state
+ *
+ * Called exactly once the first time the device is opened by
+ * userspace.  Will not be called again unless all users close it
+ * before reopening.  This simply clears the state to disconnected and
+ * sets the poll rate back to the slow speed
+ */
+static void wiichuck_open(struct input_polled_dev *poll_dev)
+{
+	struct wiichuck_device *wiichuck = poll_dev->private;
+
+	wiichuck->process = wiichuck_process_unknown;
+	wiichuck->state = WIICHUCK_STATE_DISCONNECTED;
+	wiichuck->poll_dev->poll_interval = WIICHUCK_POLL_PERIOD;
+}
+
+static int __devinit wiichuck_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	struct wiichuck_device *wiichuck;
+	struct input_polled_dev *poll_dev;
+	struct input_dev *input_dev;
+	int rc;
+
+	wiichuck = kzalloc(sizeof(*wiichuck), GFP_KERNEL);
+	if (!wiichuck)
+		return -ENOMEM;
+
+	poll_dev = input_allocate_polled_device();
+	if (!poll_dev) {
+		rc = -ENOMEM;
+		goto err_alloc;
+	}
+
+	wiichuck->i2c_client = client;
+	wiichuck->poll_dev = poll_dev;
+
+	poll_dev->private = wiichuck;
+	poll_dev->poll = wiichuck_poll;
+	poll_dev->open = wiichuck_open;
+
+	input_dev = poll_dev->input;
+	input_dev->name = "Wiichuck expansion connector";
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &client->dev;
+
+	/* Declare the events generated by this driver */
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+
+	/* Nunchuck ranges */
+	__set_bit(ABS_X, input_dev->absbit); /* joystick */
+	__set_bit(ABS_Y, input_dev->absbit);
+	input_set_abs_params(input_dev, ABS_X, 30, 220, 4, 8);
+	input_set_abs_params(input_dev, ABS_Y, 40, 200, 4, 8);
+
+	__set_bit(ABS_RX, input_dev->absbit); /* accelerometer */
+	__set_bit(ABS_RY, input_dev->absbit);
+	__set_bit(ABS_RZ, input_dev->absbit);
+	input_set_abs_params(input_dev, ABS_RX, 0, 0x3ff, 4, 8);
+	input_set_abs_params(input_dev, ABS_RY, 0, 0x3ff, 4, 8);
+	input_set_abs_params(input_dev, ABS_RZ, 0, 0x3ff, 4, 8);
+
+	/* Nunchuck buttons */
+	__set_bit(BTN_C, input_dev->keybit);
+	__set_bit(BTN_Z, input_dev->keybit);
+
+	/* Classic ranges */
+	__set_bit(ABS_HAT0X, input_dev->absbit); /* Left Joystick */
+	__set_bit(ABS_HAT0Y, input_dev->absbit);
+	input_set_abs_params(input_dev, ABS_HAT0X, 0, 63, 2, 4);
+	input_set_abs_params(input_dev, ABS_HAT0Y, 0, 63, 2, 4);
+
+	__set_bit(ABS_HAT1X, input_dev->absbit); /* Right Joystick */
+	__set_bit(ABS_HAT1Y, input_dev->absbit);
+	input_set_abs_params(input_dev, ABS_HAT1X, 0, 31, 1, 2);
+	input_set_abs_params(input_dev, ABS_HAT1Y, 0, 31, 1, 2);
+
+	__set_bit(ABS_HAT2X, input_dev->absbit); /* D-pad */
+	__set_bit(ABS_HAT2Y, input_dev->absbit);
+	input_set_abs_params(input_dev, ABS_HAT2X, -1, 1, 0, 0);
+	input_set_abs_params(input_dev, ABS_HAT2Y, -1, 1, 0, 0);
+
+	__set_bit(ABS_BRAKE, input_dev->absbit); /* Left trigger */
+	input_set_abs_params(input_dev, ABS_BRAKE, 0, 31, 1, 2);
+	__set_bit(ABS_GAS, input_dev->absbit);
+	input_set_abs_params(input_dev, ABS_GAS, 0, 31, 1, 2);
+
+	/* Classic buttons */
+	__set_bit(BTN_TL, input_dev->keybit);
+	__set_bit(BTN_SELECT, input_dev->keybit);
+	__set_bit(BTN_MODE, input_dev->keybit);
+	__set_bit(BTN_START, input_dev->keybit);
+	__set_bit(BTN_TR, input_dev->keybit);
+	__set_bit(BTN_TL2, input_dev->keybit);
+	__set_bit(BTN_B, input_dev->keybit);
+	__set_bit(BTN_Y, input_dev->keybit);
+	__set_bit(BTN_A, input_dev->keybit);
+	__set_bit(BTN_X, input_dev->keybit);
+	__set_bit(BTN_TR2, input_dev->keybit);
+
+	i2c_set_clientdata(client, wiichuck);
+
+	/* Register the device; it is 'live' after this point */
+	rc = input_register_polled_device(wiichuck->poll_dev);
+	if (rc) {
+		dev_err(&client->dev, "Failed to register input device\n");
+		goto err_register;
+	}
+
+	return 0;
+
+ err_register:
+	i2c_set_clientdata(client, NULL);
+	input_free_polled_device(poll_dev);
+ err_alloc:
+	kfree(wiichuck);
+
+	return rc;
+}
+
+static int __devexit wiichuck_remove(struct i2c_client *client)
+{
+	struct wiichuck_device *wiichuck = i2c_get_clientdata(client);
+
+	i2c_set_clientdata(client, NULL);
+	input_unregister_polled_device(wiichuck->poll_dev);
+	input_free_polled_device(wiichuck->poll_dev);
+	kfree(wiichuck);
+
+	return 0;
+}
+
+static const struct i2c_device_id wiichuck_id[] = {
+	{ "wiichuck", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wiichuck_id);
+
+#ifdef CONFIG_OF
+static struct of_device_id wiichuck_match_table[] __devinitdata = {
+	{ .compatible = "nintendo,wiimote-extension", },
+	{ }
+};
+#else
+#define wiichuck_match_table NULL
+#endif
+
+static struct i2c_driver wiichuck_driver = {
+	.driver = {
+		.name = "wiichuck",
+		.owner = THIS_MODULE,
+		.of_match_table = wiichuck_match_table,
+	},
+	.probe		= wiichuck_probe,
+	.remove		= __devexit_p(wiichuck_remove),
+	.id_table	= wiichuck_id,
+};
+
+static int __init wiichuck_init(void)
+{
+	return i2c_add_driver(&wiichuck_driver);
+}
+module_init(wiichuck_init);
+
+static void __exit wiichuck_exit(void)
+{
+	i2c_del_driver(&wiichuck_driver);
+}
+module_exit(wiichuck_exit);