diff mbox

[v1] i2c-hid: introduce HID over i2c specification implementation

Message ID 1347630103-4105-1-git-send-email-benjamin.tissoires@gmail.com (mailing list archive)
State New, archived
Delegated to: Jiri Kosina
Headers show

Commit Message

Benjamin Tissoires Sept. 14, 2012, 1:41 p.m. UTC
From: Benjamin Tissoires <benjamin.tissoires@enac.fr>

Microsoft published the protocol specification of HID over i2c:
http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx

This patch introduces an implementation of this protocol.

This implementation does not includes the ACPI part of the specification.
This will come when ACPI 5.0 devices will be available.

Once the ACPI part will be done, OEM will not have to declare HID over I2C
devices in their platform specific driver.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
---

Hi,

this is finally my first implementation of HID over I2C.

This has been tested on an Elan Microelectronics HID over I2C device, with
a Samsung Exynos 4412 board.

Any comments are welcome.

Cheers,
Benjamin

 drivers/i2c/Kconfig         |    8 +
 drivers/i2c/Makefile        |    1 +
 drivers/i2c/i2c-hid.c       | 1027 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/i2c/i2c-hid.h |   35 ++
 4 files changed, 1071 insertions(+)
 create mode 100644 drivers/i2c/i2c-hid.c
 create mode 100644 include/linux/i2c/i2c-hid.h

Comments

JJ Ding Oct. 3, 2012, 3:08 a.m. UTC | #1
Hi Benjamin,

I have one little question about __i2chid_command(), please see below.

"benjamin.tissoires" <benjamin.tissoires@gmail.com> writes:
> From: Benjamin Tissoires <benjamin.tissoires@enac.fr>
>
> Microsoft published the protocol specification of HID over i2c:
> http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
>
> This patch introduces an implementation of this protocol.
>
> This implementation does not includes the ACPI part of the specification.
> This will come when ACPI 5.0 devices will be available.
>
> Once the ACPI part will be done, OEM will not have to declare HID over I2C
> devices in their platform specific driver.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
> ---
>
> Hi,
>
> this is finally my first implementation of HID over I2C.
>
> This has been tested on an Elan Microelectronics HID over I2C device, with
> a Samsung Exynos 4412 board.
>
> Any comments are welcome.
>
> Cheers,
> Benjamin
>
>  drivers/i2c/Kconfig         |    8 +
>  drivers/i2c/Makefile        |    1 +
>  drivers/i2c/i2c-hid.c       | 1027 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/i2c/i2c-hid.h |   35 ++
>  4 files changed, 1071 insertions(+)
>  create mode 100644 drivers/i2c/i2c-hid.c
>  create mode 100644 include/linux/i2c/i2c-hid.h
>
> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> index 5a3bb3d..5adf65a 100644
> --- a/drivers/i2c/Kconfig
> +++ b/drivers/i2c/Kconfig
> @@ -47,6 +47,14 @@ config I2C_CHARDEV
>  	  This support is also available as a module.  If so, the module 
>  	  will be called i2c-dev.
>  
> +config I2C_HID
> +	tristate "HID over I2C bus"
> +	help
> +	  Say Y here to use the HID over i2c protocol implementation.
> +
> +	  This support is also available as a module.  If so, the module
> +	  will be called i2c-hid.
> +
>  config I2C_MUX
>  	tristate "I2C bus multiplexing support"
>  	help
> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> index beee6b2..8f38116 100644
> --- a/drivers/i2c/Makefile
> +++ b/drivers/i2c/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_BOARDINFO)	+= i2c-boardinfo.o
>  obj-$(CONFIG_I2C)		+= i2c-core.o
>  obj-$(CONFIG_I2C_SMBUS)		+= i2c-smbus.o
>  obj-$(CONFIG_I2C_CHARDEV)	+= i2c-dev.o
> +obj-$(CONFIG_I2C_HID)		+= i2c-hid.o
>  obj-$(CONFIG_I2C_MUX)		+= i2c-mux.o
>  obj-y				+= algos/ busses/ muxes/
>  
> diff --git a/drivers/i2c/i2c-hid.c b/drivers/i2c/i2c-hid.c
> new file mode 100644
> index 0000000..eb17d8c
> --- /dev/null
> +++ b/drivers/i2c/i2c-hid.c
> @@ -0,0 +1,1027 @@
> +/*
> + * HID over I2C protocol implementation
> + *
> + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
> + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
> + *
> + * This code is partly based on "USB HID support for Linux":
> + *
> + *  Copyright (c) 1999 Andreas Gal
> + *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
> + *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
> + *  Copyright (c) 2007-2008 Oliver Neukum
> + *  Copyright (c) 2006-2010 Jiri Kosina
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/gpio.h>
> +#include <linux/interrupt.h>
> +#include <linux/input.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/pm.h>
> +
> +#include <linux/i2c/i2c-hid.h>
> +
> +#include <linux/hid.h>
> +#include <linux/hiddev.h>
> +#include <linux/hidraw.h>
> +
> +#define DRIVER_NAME		"i2chid"
> +#define DRIVER_DESC		"HID over I2C core driver"
> +
> +#define I2C_HID_COMMAND_TRIES	3
> +
> +/* flags */
> +#define I2C_HID_STARTED		(1 << 0)
> +#define I2C_HID_OUT_RUNNING	(1 << 1)
> +#define I2C_HID_IN_RUNNING	(1 << 2)
> +#define I2C_HID_RESET_PENDING	(1 << 3)
> +#define I2C_HID_SUSPENDED	(1 << 4)
> +
> +#define I2C_HID_PWR_ON		0x00
> +#define I2C_HID_PWR_SLEEP	0x01
> +
> +/* debug option */
> +static bool debug = false;
> +module_param(debug, bool, 0444);
> +MODULE_PARM_DESC(debug, "print a lot of debug informations");
> +
> +struct i2chid_desc {
> +	__le16 wHIDDescLength;
> +	__le16 bcdVersion;
> +	__le16 wReportDescLength;
> +	__le16 wReportDescRegister;
> +	__le16 wInputRegister;
> +	__le16 wMaxInputLength;
> +	__le16 wOutputRegister;
> +	__le16 wMaxOutputLength;
> +	__le16 wCommandRegister;
> +	__le16 wDataRegister;
> +	__le16 wVendorID;
> +	__le16 wProductID;
> +	__le16 wVersionID;
> +} __packed;
> +
> +struct i2chid_cmd {
> +	enum {
> +		/* fecth HID descriptor */
> +		HID_DESCR_CMD,
> +
> +		/* fetch report descriptors */
> +		HID_REPORT_DESCR_CMD,
> +
> +		/* commands */
> +		HID_RESET_CMD,
> +		HID_GET_REPORT_CMD,
> +		HID_SET_REPORT_CMD,
> +		HID_GET_IDLE_CMD,
> +		HID_SET_IDLE_CMD,
> +		HID_GET_PROTOCOL_CMD,
> +		HID_SET_PROTOCOL_CMD,
> +		HID_SET_POWER_CMD,
> +
> +		/* read/write data register */
> +		HID_DATA_CMD,
> +	} name;
> +	unsigned int registerIndex;
> +	__u8 opcode;
> +	unsigned int length;
> +	bool wait;
> +};
> +
> +union command {
> +	u8 data[0];
> +	struct cmd {
> +		__le16 reg;
> +		__u8 reportTypeID;
> +		__u8 opcode;
> +	} __packed c;
> +};
> +
> +#define I2C_HID_CMD(name_, opcode_) \
> +	.name = name_, .opcode = opcode_, .length = 4, \
> +	.registerIndex = offsetof(struct i2chid_desc, wCommandRegister)
> +
> +static const struct i2chid_cmd cmds[] = {
> +	{ I2C_HID_CMD(HID_RESET_CMD,		0x01), .wait = true },
> +	{ I2C_HID_CMD(HID_GET_REPORT_CMD,	0x02) },
> +	{ I2C_HID_CMD(HID_SET_REPORT_CMD,	0x03) },
> +	{ I2C_HID_CMD(HID_GET_IDLE_CMD,		0x04) },
> +	{ I2C_HID_CMD(HID_SET_IDLE_CMD,		0x05) },
> +	{ I2C_HID_CMD(HID_GET_PROTOCOL_CMD,	0x06) },
> +	{ I2C_HID_CMD(HID_SET_PROTOCOL_CMD,	0x07) },
> +	{ I2C_HID_CMD(HID_SET_POWER_CMD,	0x08) },
> +
> +	{.name = HID_DESCR_CMD,
> +	 .length = 2},
> +
> +	{.name = HID_REPORT_DESCR_CMD,
> +	 .registerIndex = offsetof(struct i2chid_desc, wReportDescRegister),
> +	 .opcode = 0x00,
> +	 .length = 2},
> +
> +	{.name = HID_DATA_CMD,
> +	 .registerIndex = offsetof(struct i2chid_desc, wDataRegister),
> +	 .opcode = 0x00,
> +	 .length = 2},
> +
> +	{}, /* terminating */
> +};
> +
> +/* The main device structure */
> +struct i2chid {
> +	struct i2c_client	*client;	/* i2c client */
> +	struct hid_device	*hid;	/* pointer to corresponding HID dev */
> +	union {
> +		__u8 hdesc_buffer[sizeof(struct i2chid_desc)];
> +		struct i2chid_desc hdesc;	/* the HID Descriptor */
> +	};
> +	__le16			wHIDDescRegister; /* location of the i2c
> +						   * register of the HID
> +						   * descriptor. */
> +	unsigned int		bufsize;	/* i2c buffer size */
> +	char			*inbuf;		/* Input buffer */
> +
> +	spinlock_t		flock;		/* flags spinlock */
> +	unsigned long		flags;		/* device flags */
> +
> +	int			irq;		/* the interrupt line irq */
> +
> +	wait_queue_head_t	wait;		/* For waiting the interrupt */
> +};
> +
> +void i2chid_print_buffer(struct i2chid *ihid, u8 *buf, unsigned int n)
> +{
> +	int i;
> +	char *string = kzalloc((n*3+1)*sizeof(char), GFP_KERNEL);
> +	char tmpbuf[4] = "";
> +
> +	for (i = 0; i < n; ++i) {
> +		sprintf(tmpbuf, "%02x ", buf[i] & 0xFF);
> +		strcat(string, tmpbuf);
> +	}
> +
> +	dev_err(&ihid->client->dev, "%s\n", string);
> +	kfree(string);
> +}
> +
> +static int __i2chid_command(struct i2c_client *client, int command, u8 reportID,
> +		u8 reportType, u8 *args, int args_len,
> +		unsigned char *buf_recv, int data_len)
> +{
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	union command *cmd;
> +	unsigned char *rec_buf = buf_recv;
> +	int ret;
> +	int tries = I2C_HID_COMMAND_TRIES;
> +	int length = 0;
> +	struct i2c_msg msg[2];
> +	int msg_num = 0;
> +	int i;
> +	bool wait = false;
> +
> +	if (WARN_ON(!ihid))
> +		return -ENODEV;
> +
> +	cmd = kmalloc(args_len + sizeof(union command), GFP_KERNEL);
> +	if (!cmd)
> +		return -ENOMEM;
> +
> +	for (i = 0; cmds[i].length; i++) {
> +		unsigned int registerIndex;
> +
> +		if (cmds[i].name != command)
> +			continue;
> +
> +		registerIndex = cmds[i].registerIndex;
> +		cmd->data[0] = ihid->hdesc_buffer[registerIndex];
> +		cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1];
> +		cmd->c.opcode = cmds[i].opcode;
> +		length = cmds[i].length;
> +		wait = cmds[i].wait;
> +	}
> +
> +	/* special case for HID_DESCR_CMD */
> +	if (command == HID_DESCR_CMD)
> +		cmd->c.reg = ihid->wHIDDescRegister;
> +
> +	cmd->c.reportTypeID = reportID | reportType << 4;
> +
> +	memcpy(cmd->data + length, args, args_len);
> +	length += args_len;
> +
> +	if (debug) {
> +		char tmpstr[4] = "";
> +		char strbuf[50] = "";
> +		int i;
> +		for (i = 0; i < length; i++) {
> +			sprintf(tmpstr, "%02x ", cmd->data[i] & 0xFF);
> +			strcat(strbuf, tmpstr);
> +		}
> +		dev_err(&client->dev, "%s, cmd=%s\n", __func__, strbuf);
> +	}
> +
> +	msg[0].addr = client->addr;
> +	msg[0].flags = client->flags & I2C_M_TEN;
> +	msg[0].len = length;
> +	msg[0].buf = (char *) cmd->data;
> +	msg[1].addr = client->addr;
> +	msg[1].flags = client->flags & I2C_M_TEN;
> +	msg[1].flags |= I2C_M_RD;
> +	msg[1].len = data_len;
> +	msg[1].buf = rec_buf;
> +
> +	msg_num = data_len > 0 ? 2 : 1;
> +
> +	if (wait) {
> +		spin_lock_irq(&ihid->flock);
> +		set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
> +		spin_unlock_irq(&ihid->flock);
> +	}
> +
> +	do {
> +		ret = i2c_transfer(client->adapter, msg, msg_num);
> +		if (ret > 0)
> +			break;
> +		tries--;
> +		dev_dbg(&client->dev, "retrying i2chid_command:%d (%d)\n",
> +			command, tries);
> +	} while (tries > 0);

Do we have to do this retry here?  In i2c_transfer() it already does
retry automatically on arbitration loss (please see __i2c_transfer() in
drivers/i2c/i2c-core.c) that's defined by individual bus driver.  Maybe
we don't have to retry here and make the code a bit simpler.

Thanks,
JJ

> +	if (wait && ret > 0) {
> +		if (debug)
> +			dev_err(&client->dev, "%s: waiting....\n", __func__);
> +		if (!wait_event_timeout(ihid->wait,
> +				!test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
> +				msecs_to_jiffies(5000)))
> +			ret = -ENODATA;
> +		if (debug)
> +			dev_err(&client->dev, "%s: finished.\n", __func__);
> +	}
> +
> +	kfree(cmd);
> +
> +	return ret > 0 ? ret != msg_num : ret;
> +}
> +
> +static int i2chid_command(struct i2c_client *client, int command,
> +		unsigned char *buf_recv, int data_len)
> +{
> +	return __i2chid_command(client, command, 0, 0, NULL, 0,
> +				buf_recv, data_len);
> +}
--
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
Shubhrajyoti Datta Oct. 3, 2012, 6:05 a.m. UTC | #2
On Fri, Sep 14, 2012 at 7:11 PM, benjamin.tissoires
<benjamin.tissoires@gmail.com> wrote:
> From: Benjamin Tissoires <benjamin.tissoires@enac.fr>
>
> Microsoft published the protocol specification of HID over i2c:
> http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
>
> This patch introduces an implementation of this protocol.
>
> This implementation does not includes the ACPI part of the specification.
> This will come when ACPI 5.0 devices will be available.
>
> Once the ACPI part will be done, OEM will not have to declare HID over I2C
> devices in their platform specific driver.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
> ---
>
> Hi,
>
> this is finally my first implementation of HID over I2C.
>
> This has been tested on an Elan Microelectronics HID over I2C device, with
> a Samsung Exynos 4412 board.
>
> Any comments are welcome.
>
> Cheers,
> Benjamin
>
>  drivers/i2c/Kconfig         |    8 +
>  drivers/i2c/Makefile        |    1 +
>  drivers/i2c/i2c-hid.c       | 1027 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/i2c/i2c-hid.h |   35 ++
>  4 files changed, 1071 insertions(+)
>  create mode 100644 drivers/i2c/i2c-hid.c
>  create mode 100644 include/linux/i2c/i2c-hid.h
>
> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> index 5a3bb3d..5adf65a 100644
> --- a/drivers/i2c/Kconfig
> +++ b/drivers/i2c/Kconfig
> @@ -47,6 +47,14 @@ config I2C_CHARDEV
>           This support is also available as a module.  If so, the module
>           will be called i2c-dev.
>
> +config I2C_HID
> +       tristate "HID over I2C bus"
> +       help
> +         Say Y here to use the HID over i2c protocol implementation.
> +
> +         This support is also available as a module.  If so, the module
> +         will be called i2c-hid.
> +
>  config I2C_MUX
>         tristate "I2C bus multiplexing support"
>         help
> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> index beee6b2..8f38116 100644
> --- a/drivers/i2c/Makefile
> +++ b/drivers/i2c/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_BOARDINFO)     += i2c-boardinfo.o
>  obj-$(CONFIG_I2C)              += i2c-core.o
>  obj-$(CONFIG_I2C_SMBUS)                += i2c-smbus.o
>  obj-$(CONFIG_I2C_CHARDEV)      += i2c-dev.o
> +obj-$(CONFIG_I2C_HID)          += i2c-hid.o
>  obj-$(CONFIG_I2C_MUX)          += i2c-mux.o
>  obj-y                          += algos/ busses/ muxes/
>
> diff --git a/drivers/i2c/i2c-hid.c b/drivers/i2c/i2c-hid.c
> new file mode 100644
> index 0000000..eb17d8c
> --- /dev/null
> +++ b/drivers/i2c/i2c-hid.c
> @@ -0,0 +1,1027 @@
> +/*
> + * HID over I2C protocol implementation
> + *
> + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
> + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
> + *
> + * This code is partly based on "USB HID support for Linux":
> + *
> + *  Copyright (c) 1999 Andreas Gal
> + *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
> + *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
> + *  Copyright (c) 2007-2008 Oliver Neukum
> + *  Copyright (c) 2006-2010 Jiri Kosina
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/gpio.h>
> +#include <linux/interrupt.h>
> +#include <linux/input.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/pm.h>
> +
> +#include <linux/i2c/i2c-hid.h>
> +
> +#include <linux/hid.h>
> +#include <linux/hiddev.h>
> +#include <linux/hidraw.h>
> +
> +#define DRIVER_NAME            "i2chid"
> +#define DRIVER_DESC            "HID over I2C core driver"
> +
> +#define I2C_HID_COMMAND_TRIES  3
> +
> +/* flags */
> +#define I2C_HID_STARTED                (1 << 0)
> +#define I2C_HID_OUT_RUNNING    (1 << 1)
> +#define I2C_HID_IN_RUNNING     (1 << 2)
> +#define I2C_HID_RESET_PENDING  (1 << 3)
> +#define I2C_HID_SUSPENDED      (1 << 4)
> +
> +#define I2C_HID_PWR_ON         0x00
> +#define I2C_HID_PWR_SLEEP      0x01
> +
> +/* debug option */
> +static bool debug = false;
> +module_param(debug, bool, 0444);
> +MODULE_PARM_DESC(debug, "print a lot of debug informations");
> +
> +struct i2chid_desc {
> +       __le16 wHIDDescLength;
> +       __le16 bcdVersion;
> +       __le16 wReportDescLength;
> +       __le16 wReportDescRegister;
> +       __le16 wInputRegister;
> +       __le16 wMaxInputLength;
> +       __le16 wOutputRegister;
> +       __le16 wMaxOutputLength;
> +       __le16 wCommandRegister;
> +       __le16 wDataRegister;
> +       __le16 wVendorID;
> +       __le16 wProductID;
> +       __le16 wVersionID;
> +} __packed;
> +
> +struct i2chid_cmd {
> +       enum {
> +               /* fecth HID descriptor */
> +               HID_DESCR_CMD,
> +
> +               /* fetch report descriptors */
> +               HID_REPORT_DESCR_CMD,
> +
> +               /* commands */
> +               HID_RESET_CMD,
> +               HID_GET_REPORT_CMD,
> +               HID_SET_REPORT_CMD,
> +               HID_GET_IDLE_CMD,
> +               HID_SET_IDLE_CMD,
> +               HID_GET_PROTOCOL_CMD,
> +               HID_SET_PROTOCOL_CMD,
> +               HID_SET_POWER_CMD,
> +
> +               /* read/write data register */
> +               HID_DATA_CMD,
> +       } name;
> +       unsigned int registerIndex;
> +       __u8 opcode;
> +       unsigned int length;
> +       bool wait;
> +};
> +
> +union command {
> +       u8 data[0];
> +       struct cmd {
> +               __le16 reg;
> +               __u8 reportTypeID;
> +               __u8 opcode;
> +       } __packed c;
> +};
> +
> +#define I2C_HID_CMD(name_, opcode_) \
> +       .name = name_, .opcode = opcode_, .length = 4, \
> +       .registerIndex = offsetof(struct i2chid_desc, wCommandRegister)
> +
> +static const struct i2chid_cmd cmds[] = {
> +       { I2C_HID_CMD(HID_RESET_CMD,            0x01), .wait = true },
> +       { I2C_HID_CMD(HID_GET_REPORT_CMD,       0x02) },
> +       { I2C_HID_CMD(HID_SET_REPORT_CMD,       0x03) },
> +       { I2C_HID_CMD(HID_GET_IDLE_CMD,         0x04) },
> +       { I2C_HID_CMD(HID_SET_IDLE_CMD,         0x05) },
> +       { I2C_HID_CMD(HID_GET_PROTOCOL_CMD,     0x06) },
> +       { I2C_HID_CMD(HID_SET_PROTOCOL_CMD,     0x07) },
> +       { I2C_HID_CMD(HID_SET_POWER_CMD,        0x08) },
> +
> +       {.name = HID_DESCR_CMD,
> +        .length = 2},
> +
> +       {.name = HID_REPORT_DESCR_CMD,
> +        .registerIndex = offsetof(struct i2chid_desc, wReportDescRegister),
> +        .opcode = 0x00,
> +        .length = 2},
> +
> +       {.name = HID_DATA_CMD,
> +        .registerIndex = offsetof(struct i2chid_desc, wDataRegister),
> +        .opcode = 0x00,
> +        .length = 2},
> +
> +       {}, /* terminating */
> +};
> +
> +/* The main device structure */
> +struct i2chid {
> +       struct i2c_client       *client;        /* i2c client */
> +       struct hid_device       *hid;   /* pointer to corresponding HID dev */
> +       union {
> +               __u8 hdesc_buffer[sizeof(struct i2chid_desc)];
> +               struct i2chid_desc hdesc;       /* the HID Descriptor */
> +       };
> +       __le16                  wHIDDescRegister; /* location of the i2c
> +                                                  * register of the HID
> +                                                  * descriptor. */
> +       unsigned int            bufsize;        /* i2c buffer size */
> +       char                    *inbuf;         /* Input buffer */
> +
> +       spinlock_t              flock;          /* flags spinlock */
> +       unsigned long           flags;          /* device flags */
> +
> +       int                     irq;            /* the interrupt line irq */
> +
> +       wait_queue_head_t       wait;           /* For waiting the interrupt */
> +};
> +
> +void i2chid_print_buffer(struct i2chid *ihid, u8 *buf, unsigned int n)
> +{
> +       int i;
> +       char *string = kzalloc((n*3+1)*sizeof(char), GFP_KERNEL);
> +       char tmpbuf[4] = "";
> +
> +       for (i = 0; i < n; ++i) {
> +               sprintf(tmpbuf, "%02x ", buf[i] & 0xFF);
> +               strcat(string, tmpbuf);
> +       }
> +
> +       dev_err(&ihid->client->dev, "%s\n", string);
> +       kfree(string);
> +}
> +
> +static int __i2chid_command(struct i2c_client *client, int command, u8 reportID,
> +               u8 reportType, u8 *args, int args_len,
> +               unsigned char *buf_recv, int data_len)
> +{
> +       struct i2chid *ihid = i2c_get_clientdata(client);
> +       union command *cmd;
> +       unsigned char *rec_buf = buf_recv;
> +       int ret;
> +       int tries = I2C_HID_COMMAND_TRIES;
> +       int length = 0;
> +       struct i2c_msg msg[2];
> +       int msg_num = 0;
> +       int i;
> +       bool wait = false;
> +
> +       if (WARN_ON(!ihid))
> +               return -ENODEV;
> +
> +       cmd = kmalloc(args_len + sizeof(union command), GFP_KERNEL);
> +       if (!cmd)
> +               return -ENOMEM;
> +
> +       for (i = 0; cmds[i].length; i++) {
> +               unsigned int registerIndex;
> +
> +               if (cmds[i].name != command)
> +                       continue;
> +
> +               registerIndex = cmds[i].registerIndex;
> +               cmd->data[0] = ihid->hdesc_buffer[registerIndex];
> +               cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1];
> +               cmd->c.opcode = cmds[i].opcode;
> +               length = cmds[i].length;
> +               wait = cmds[i].wait;
> +       }
> +
> +       /* special case for HID_DESCR_CMD */
> +       if (command == HID_DESCR_CMD)
> +               cmd->c.reg = ihid->wHIDDescRegister;
> +
> +       cmd->c.reportTypeID = reportID | reportType << 4;
> +
> +       memcpy(cmd->data + length, args, args_len);
> +       length += args_len;
> +
> +       if (debug) {
> +               char tmpstr[4] = "";
> +               char strbuf[50] = "";
> +               int i;
> +               for (i = 0; i < length; i++) {
> +                       sprintf(tmpstr, "%02x ", cmd->data[i] & 0xFF);
> +                       strcat(strbuf, tmpstr);
> +               }
> +               dev_err(&client->dev, "%s, cmd=%s\n", __func__, strbuf);
> +       }
> +
> +       msg[0].addr = client->addr;
> +       msg[0].flags = client->flags & I2C_M_TEN;
> +       msg[0].len = length;
> +       msg[0].buf = (char *) cmd->data;
> +       msg[1].addr = client->addr;
> +       msg[1].flags = client->flags & I2C_M_TEN;
> +       msg[1].flags |= I2C_M_RD;
> +       msg[1].len = data_len;
> +       msg[1].buf = rec_buf;
> +
> +       msg_num = data_len > 0 ? 2 : 1;
> +
> +       if (wait) {
> +               spin_lock_irq(&ihid->flock);
> +               set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
> +               spin_unlock_irq(&ihid->flock);
> +       }
> +
> +       do {
> +               ret = i2c_transfer(client->adapter, msg, msg_num);
> +               if (ret > 0)
> +                       break;
> +               tries--;
> +               dev_dbg(&client->dev, "retrying i2chid_command:%d (%d)\n",
> +                       command, tries);
> +       } while (tries > 0);
> +
> +       if (wait && ret > 0) {
> +               if (debug)
> +                       dev_err(&client->dev, "%s: waiting....\n", __func__);
> +               if (!wait_event_timeout(ihid->wait,
> +                               !test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
> +                               msecs_to_jiffies(5000)))
> +                       ret = -ENODATA;
> +               if (debug)
> +                       dev_err(&client->dev, "%s: finished.\n", __func__);
> +       }
> +
> +       kfree(cmd);
> +
> +       return ret > 0 ? ret != msg_num : ret;
> +}
> +
> +static int i2chid_command(struct i2c_client *client, int command,
> +               unsigned char *buf_recv, int data_len)
> +{
> +       return __i2chid_command(client, command, 0, 0, NULL, 0,
> +                               buf_recv, data_len);
> +}
> +
> +static int i2chid_get_report(struct i2c_client *client, u8 reportType,
> +               u32 reportID, unsigned char *buf_recv, int data_len)
> +{
> +       struct i2chid *ihid = i2c_get_clientdata(client);
> +       u8 args[6];
> +       int ret;
> +       int args_len = 0;
> +       u16 readRegister = ihid->hdesc.wDataRegister;
> +
> +       if (debug)
> +               dev_err(&client->dev, "%s\n", __func__);
> +
> +       if (reportID >= 15) {
> +               args[args_len++] = (reportID >> 0) & 0xFF;
> +               args[args_len++] = (reportID >> 8) & 0xFF;
> +               args[args_len++] = (reportID >> 16) & 0xFF;
> +               args[args_len++] = (reportID >> 24) & 0xFF;
> +               reportID = 0x0F;
> +       }
> +
> +       args[args_len++] = readRegister & 0xff;
> +       args[args_len++] = (readRegister >> 8) & 0xff;
> +
> +       ret = __i2chid_command(client, HID_GET_REPORT_CMD, reportID & 0x0F,
> +               reportType, args, args_len, buf_recv, data_len);
> +       if (ret) {
> +               dev_err(&client->dev, "HID_GET_REPORT_CMD Fail\n");
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static int i2chid_set_report(struct i2c_client *client, u8 reportType,
> +               u32 reportID, unsigned char *buf, int data_len)
> +{
> +       struct i2chid *ihid = i2c_get_clientdata(client);
> +       u8 *args;
> +       int ret;
> +       u16 dataRegister = ihid->hdesc.wDataRegister;
> +       int args_len =  (reportID >= 15 ? 4 : 0) +
> +                       2 /* dataRegister */ +
> +                       2 /* size */ +
> +                       data_len;
> +       int index = 0;
> +
> +       if (debug)
> +               dev_err(&client->dev, "%s\n", __func__);
> +
> +       args = kmalloc(args_len, GFP_KERNEL);
> +
> +       if (reportID >= 15) {
> +               args[index++] = (reportID >> 0) & 0xFF;
> +               args[index++] = (reportID >> 8) & 0xFF;
> +               args[index++] = (reportID >> 16) & 0xFF;
> +               args[index++] = (reportID >> 24) & 0xFF;
> +               reportID = 0x0F;
> +       }
> +
> +       args[index++] = dataRegister & 0xff;
> +       args[index++] = (dataRegister >> 8) & 0xff;
> +
> +       args[index++] = data_len & 0xff;
> +       args[index++] = (data_len >> 8) & 0xff;
> +
> +       memcpy(&args[index], buf, data_len);
> +
> +       ret = __i2chid_command(client, HID_SET_REPORT_CMD, reportID & 0x0F,
> +               reportType, args, args_len, NULL, 0);
> +       if (ret) {
> +               dev_err(&client->dev, "HID_SET_REPORT_CMD Fail\n");
> +               return -EINVAL;
> +       }
> +
> +       kfree(args);
> +       return data_len;
> +}
> +
> +static int i2chid_set_power(struct i2c_client *client, int power_state)
> +{
> +       int ret;
> +
> +       if (debug)
> +               dev_err(&client->dev, "%s\n", __func__);
> +
> +       ret = __i2chid_command(client, HID_SET_POWER_CMD, power_state & 0x01,
> +               0, NULL, 0, NULL, 0);
> +       if (ret) {
> +               dev_err(&client->dev, "HID_SET_POWER_CMD Fail\n");
> +               return -EINVAL;
may be errors can be passed on.

> +       }
> +
> +       return 0;
> +}
> +
> +static int i2chid_hwreset(struct i2c_client *client)
> +{
> +       uint8_t buf_recv[79];
> +       int ret;
> +
> +       if (debug)
> +               dev_err(&client->dev, "%s\n", __func__);
> +
> +       ret = i2chid_set_power(client, I2C_HID_PWR_ON);
> +       if (ret)
> +               return -EINVAL;
why not just pass on the error?

> +
> +       if (debug)
> +               dev_err(&client->dev, "resetting...\n");
this is a little uncustomary.

May be consider  bdg
> +
> +       ret = i2chid_command(client, HID_RESET_CMD, buf_recv, 0);
> +       if (ret) {
> +               dev_err(&client->dev, "HID_RESET_CMD Fail\n");
> +               return -EINVAL;
> +       }
> +
> +       ret = i2c_master_recv(client, buf_recv, 2);
> +       if (ret != 2 || (buf_recv[0] != 0 && buf_recv[1] != 0)) {
> +               dev_err(&client->dev,
> +                       "HID_RESET_CMD Failed: got %02x %02x, size=%d\n",
> +                       buf_recv[0], buf_recv[1], ret);
> +
> +               i2chid_set_power(client, I2C_HID_PWR_SLEEP);
> +               return -EINVAL;
> +       }
> +       return 0;
> +}
> +
> +static bool i2chid_get_input(struct i2chid *ihid)
> +{
> +       int ret;
> +       int size = ihid->hdesc.wMaxInputLength;
> +
> +       ret = i2c_master_recv(ihid->client, ihid->inbuf, size);
> +       if (ret != size) {
> +               dev_err(&ihid->client->dev, "%s: got %d data instead of %d\n",
> +                       __func__, ret, size);
> +               return false;
> +       }
> +
> +       if (debug)
> +               i2chid_print_buffer(ihid, ihid->inbuf, size);
> +
> +       ret = hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2,
> +                       size - 2, 1);
> +       if (ret)
> +               return false;
> +
> +       return true;
> +}
> +
> +static irqreturn_t i2chid_irq(int irq, void *dev_id)
> +{
> +       struct i2chid *ihid = dev_id;
> +       int ready;
> +
> +       if (!ihid)
> +               return IRQ_HANDLED;
> +
> +       spin_lock_irq(&ihid->flock);
> +       if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags)) {
> +               spin_unlock_irq(&ihid->flock);
> +
> +               wake_up(&ihid->wait);
> +               return IRQ_HANDLED;
> +       }
> +
> +       ready = test_bit(I2C_HID_STARTED, &ihid->flags);
> +       spin_unlock_irq(&ihid->flock);
> +
> +       if (ready)
> +               i2chid_get_input(ihid);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int i2chid_get_report_length(struct hid_report *report)
> +{
> +       return ((report->size - 1) >> 3) + 1 +
> +               report->device->report_enum[report->type].numbered + 2;
> +}
> +
> +void i2chid_init_report(struct hid_report *report)
> +{
> +       struct hid_device *hid = report->device;
> +       struct i2c_client *client = hid->driver_data;
> +       struct i2chid *ihid = i2c_get_clientdata(client);
> +       unsigned int size, ret_size;
> +
> +       size = i2chid_get_report_length(report);
> +       i2chid_get_report(client,
> +                       report->type == HID_FEATURE_REPORT ? 0x03 : 0x01,
> +                       report->id, ihid->inbuf, size);
> +
> +       if (debug)
> +               i2chid_print_buffer(ihid, ihid->inbuf, size);
> +
> +       ret_size = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
> +
> +       if (ret_size != size) {
> +               dev_err(&client->dev, "error in %s size:%d / ret_size:%d\n",
> +                       __func__, size, ret_size);
> +               return;
> +       }
> +
> +       /* hid->driver_lock is held as we are in probe function,
> +        * we just need to setup the input fields, so using
> +        * hid_report_raw_event is safe. */
> +       hid_report_raw_event(hid, report->type, ihid->inbuf + 2, size - 2, 1);
> +}
> +
> +/*
> + * Initialize all reports
> + */
> +void i2chid_init_reports(struct hid_device *hid)
> +{
> +       struct hid_report *report;
> +
> +       list_for_each_entry(report,
> +               &hid->report_enum[HID_INPUT_REPORT].report_list, list)
> +               i2chid_init_report(report);
> +
> +       list_for_each_entry(report,
> +               &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
> +               i2chid_init_report(report);
> +}
> +
> +/*
> + * Traverse the supplied list of reports and find the longest
> + */
> +static void i2chid_find_max_report(struct hid_device *hid, unsigned int type,
> +               unsigned int *max)
> +{
> +       struct hid_report *report;
> +       unsigned int size;
> +
> +       /* We should not rely on wMaxInputLength, as some devices may set it to
> +        * a wrong length. */
> +       list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
> +               size = i2chid_get_report_length(report);
> +               if (*max < size)
> +                       *max = size;
> +       }
> +}
> +
> +static int i2chid_alloc_buffers(struct i2chid *ihid)
> +{
> +       ihid->inbuf = kzalloc(ihid->bufsize, GFP_KERNEL);
> +
> +       if (!ihid->inbuf)
> +               return -1;
> +
> +       return 0;
> +}
> +
> +static void i2chid_free_buffers(struct i2chid *ihid)
> +{
> +       kfree(ihid->inbuf);
> +}
> +
> +static int i2chid_get_raw_report(struct hid_device *hid,
> +               unsigned char report_number, __u8 *buf, size_t count,
> +               unsigned char report_type)
> +{
> +       struct i2c_client *client = hid->driver_data;
> +       struct i2chid *ihid = i2c_get_clientdata(client);
> +       int ret;
> +
> +       if (count > ihid->bufsize)
> +               count = ihid->bufsize;
> +
> +       ret = i2chid_get_report(client,
> +                       report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
> +                       report_number, ihid->inbuf, count);
> +
> +       if (ret < 0)
> +               return ret;
> +
> +       count = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
> +
> +       memcpy(buf, ihid->inbuf + 2, count);
> +
> +       return count;
> +}
> +
> +static int i2chid_output_raw_report(struct hid_device *hid, __u8 *buf,
> +               size_t count, unsigned char report_type)
> +{
> +       struct i2c_client *client = hid->driver_data;
> +       int ret;
> +       int report_id = buf[0];
> +
> +       ret = i2chid_set_report(client,
> +                               report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
> +                               report_id, buf, count);
> +
> +       return ret;
> +
> +}
> +
> +static int i2chid_fetch_hid_descriptor(struct i2chid *ihid)
> +{
> +       struct i2c_client *client = ihid->client;
> +       struct i2chid_desc *hdesc = &ihid->hdesc;
> +       unsigned int dsize = 0;
> +       int ret;
> +
> +       /* Fetch the length of HID description, retrieve the 4 first bytes:
> +        * bytes 0-1 -> length
> +        * bytes 2-3 -> bcdVersion (has to be 1.00) */
> +       ret = i2chid_command(client, HID_DESCR_CMD, ihid->hdesc_buffer, 4);
> +
> +       if (debug)
> +               dev_err(&client->dev,
> +                       "%s, ihid->hdesc_buffer: %02x %02x %02x %02x\n",
> +                       __func__,
> +                       ihid->hdesc_buffer[0],
> +                       ihid->hdesc_buffer[1],
> +                       ihid->hdesc_buffer[2],
> +                       ihid->hdesc_buffer[3]);
> +
> +       if (ret) {
> +               dev_err(&client->dev, "HID_DESCR_LENGTH_CMD Fail (ret=%d)\n",
> +                       ret);
> +               return -EINVAL;
> +       }
> +
> +       dsize = le16_to_cpu(hdesc->wHIDDescLength);
> +       if (!dsize || dsize > HID_MAX_DESCRIPTOR_SIZE) {
> +               dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
> +                       dsize);
> +               return -EINVAL;
> +       }
> +
> +       /* check bcdVersion == 1.0 */
> +       if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
> +               dev_err(&client->dev,
> +                       "unexpected HID descriptor bcdVersion (0x%04x)\n",
> +                       le16_to_cpu(hdesc->bcdVersion));
> +               return -EINVAL;
> +       }
> +
> +       if (debug)
> +               dev_err(&client->dev, "Fetching the HID descriptor\n");
> +
> +       ret = i2chid_command(client, HID_DESCR_CMD, ihid->hdesc_buffer, dsize);
> +       if (ret) {
> +               dev_err(&client->dev, "HID_DESCR_CMD Fail\n");
> +               return -EINVAL;
> +       }
> +
> +       if (debug)
> +               i2chid_print_buffer(ihid, ihid->hdesc_buffer, dsize);
> +
> +       return 0;
> +}
> +
> +static int i2chid_parse(struct hid_device *hid)
> +{
> +       struct i2c_client *client = hid->driver_data;
> +       struct i2chid *ihid = i2c_get_clientdata(client);
> +       struct i2chid_desc *hdesc = &ihid->hdesc;
> +       unsigned int rsize = 0;
> +       char *rdesc;
> +       int ret;
> +       int tries = 3;
> +
> +       if (debug)
> +               dev_err(&client->dev, "entering %s\n", __func__);
> +
> +       rsize = le16_to_cpu(hdesc->wReportDescLength);
> +       if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
> +               dbg_hid("weird size of report descriptor (%u)\n", rsize);
> +               return -EINVAL;
> +       }
> +
> +       do {
> +               ret = i2chid_hwreset(client);
> +               if (ret)
> +                       msleep(1000);
> +       } while (tries-- > 0 && ret);
> +
> +       if (ret)
> +               return ret;
> +
> +       rdesc = kmalloc(rsize, GFP_KERNEL);
> +
> +       if (!rdesc) {
> +               dbg_hid("couldn't allocate rdesc memory\n");
> +               return -ENOMEM;
> +       }
> +
> +       if (debug)
> +               dev_err(&client->dev, "asking HID report descriptor\n");
> +
> +       ret = i2chid_command(client, HID_REPORT_DESCR_CMD, rdesc, rsize);
> +       if (ret) {
> +               hid_err(hid, "reading report descriptor failed\n");
> +               kfree(rdesc);
> +               ret = -ENOMEM;
> +               goto err;
> +       }
> +
> +       if (debug)
> +               i2chid_print_buffer(ihid, rdesc, rsize);
> +
> +       ret = hid_parse_report(hid, rdesc, rsize);
> +       kfree(rdesc);
> +       if (ret) {
> +               dbg_hid("parsing report descriptor failed\n");
> +               goto err;
> +       }
> +
> +       return 0;
> +err:
> +       return ret;
> +}
> +
> +static int i2chid_start(struct hid_device *hid)
> +{
> +       struct i2c_client *client = hid->driver_data;
> +       struct i2chid *ihid = i2c_get_clientdata(client);
> +       int ret;
> +
> +       ihid->bufsize = HID_MIN_BUFFER_SIZE;
> +       i2chid_find_max_report(hid, HID_INPUT_REPORT, &ihid->bufsize);
> +       i2chid_find_max_report(hid, HID_OUTPUT_REPORT, &ihid->bufsize);
> +       i2chid_find_max_report(hid, HID_FEATURE_REPORT, &ihid->bufsize);
> +
> +       if (ihid->bufsize > HID_MAX_BUFFER_SIZE)
> +               ihid->bufsize = HID_MAX_BUFFER_SIZE;
> +
> +       if (i2chid_alloc_buffers(ihid)) {
> +               ret = -ENOMEM;
> +               goto fail;
> +       }
> +
> +       if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
> +               i2chid_init_reports(hid);
> +
> +       return 0;
> +
> +fail:
> +       i2chid_free_buffers(ihid);
> +       return ret;
> +}
> +
> +static void i2chid_stop(struct hid_device *hid)
> +{
> +       struct i2c_client *client = hid->driver_data;
> +       struct i2chid *ihid = i2c_get_clientdata(client);
> +
> +       if (WARN_ON(!ihid))
> +               return;
> +
> +       hid->claimed = 0;
> +
> +       i2chid_free_buffers(ihid);
> +}
> +
> +static int i2chid_open(struct hid_device *hid)
> +{
> +       struct i2c_client *client = hid->driver_data;
> +       struct i2chid *ihid = i2c_get_clientdata(client);
> +       int ret;
> +
> +       if (!hid->open++) {
> +               ret = i2chid_set_power(client, I2C_HID_PWR_ON);
> +               if (ret) {
> +                       hid->open--;
> +                       return -EIO;
> +               }
> +               spin_lock_irq(&ihid->flock);
> +               set_bit(I2C_HID_STARTED, &ihid->flags);
> +               spin_unlock_irq(&ihid->flock);
> +       }
> +       return 0;
> +}
> +
> +static void i2chid_close(struct hid_device *hid)
> +{
> +       struct i2c_client *client = hid->driver_data;
> +       struct i2chid *ihid = i2c_get_clientdata(client);
> +
> +       /* protecting hid->open to make sure we don't restart
> +        * data acquistion due to a resumption we no longer
> +        * care about
> +        */
> +       if (!--hid->open) {
> +               spin_lock_irq(&ihid->flock);
> +               clear_bit(I2C_HID_STARTED, &ihid->flags);
> +               spin_unlock_irq(&ihid->flock);
> +
> +               /* Save some power */
> +               i2chid_set_power(client, I2C_HID_PWR_SLEEP);
> +       }
> +}
> +
> +static int i2chid_power(struct hid_device *hid, int lvl)
> +{
> +       struct i2c_client *client = hid->driver_data;
> +       int r = 0;
> +
> +       if (debug)
> +               dev_err(&client->dev, "%s lvl:%d\n", __func__, lvl);
> +
> +       switch (lvl) {
> +       case PM_HINT_FULLON:
> +               r = i2chid_set_power(client, I2C_HID_PWR_ON);
> +               break;
> +       case PM_HINT_NORMAL:
> +               r = i2chid_set_power(client, I2C_HID_PWR_SLEEP);
> +               break;
> +       }
> +       return r;
> +}
> +
> +static int i2chid_hidinput_input_event(struct input_dev *dev, unsigned int type,
> +               unsigned int code, int value)
> +{
> +       struct hid_device *hid = input_get_drvdata(dev);
> +       struct hid_field *field;
> +       int offset;
> +
> +       if (type == EV_FF)
> +               return input_ff_event(dev, type, code, value);
> +
> +       if (type != EV_LED)
> +               return -1;
> +
> +       offset = hidinput_find_field(hid, type, code, &field);
> +
> +       if (offset == -1) {
> +               hid_warn(dev, "event field not found\n");
> +               return -1;
> +       }
> +
> +       hid_set_field(field, offset, value);
> +
> +       return 0;
> +}
> +
> +static struct hid_ll_driver i2c_hid_driver = {
> +       .parse = i2chid_parse,
> +       .start = i2chid_start,
> +       .stop = i2chid_stop,
> +       .open = i2chid_open,
> +       .close = i2chid_close,
> +       .power = i2chid_power,
> +       .hidinput_input_event = i2chid_hidinput_input_event,
> +};
> +
> +static int i2chid_init_irq(struct i2c_client *client)
> +{
> +       struct i2chid *ihid = i2c_get_clientdata(client);
> +       int rc;
> +
> +       dev_dbg(&client->dev, "Requesting IRQ: %d\n",
> +               client->irq);
> +
> +       rc = request_threaded_irq(client->irq, NULL, i2chid_irq,
> +                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> +                       DRIVER_NAME, ihid);
> +       if (rc < 0) {
> +               dev_dbg(&client->dev,
> +                       "Could not register for %s interrupt, irq = %d,"
> +                       " rc = %d\n",
> +               DRIVER_NAME, client->irq, rc);
> +
> +               return rc;
> +       }
> +
> +       return client->irq;
> +}
> +
> +static int __devinit i2chid_probe(struct i2c_client *client,
> +               const struct i2c_device_id *dev_id)
> +{
> +       int ret;
> +       struct i2chid *ihid;
> +       struct hid_device *hid;
> +       __u16 hidRegister;
> +       unsigned char tmpstr[11];
> +       struct i2chid_platform_data *platform_data = client->dev.platform_data;
> +
> +       dbg_hid("HID probe called for i2c %d\n", client->addr);
> +
> +       if (!platform_data) {
> +               dev_err(&client->dev, "HID register address not provided\n");
> +               return -EINVAL;
> +       }
> +
> +       if (client->irq < 1) {
> +               dev_err(&client->dev,
> +                       "HID over i2c has not been provided an Int IRQ\n");
> +               return -EINVAL;
> +       }
> +
> +       ihid = kzalloc(sizeof(struct i2chid), GFP_KERNEL);
> +       if (!ihid)
> +               return -ENOMEM;

you may consider devm_* function here.

> +
> +       i2c_set_clientdata(client, ihid);
> +
> +       ihid->client = client;
> +       ihid->flags = 0;
> +
> +       hidRegister = platform_data->hid_descriptor_address;
> +       ihid->wHIDDescRegister = cpu_to_le16(hidRegister);
> +
> +       init_waitqueue_head(&ihid->wait);
> +       spin_lock_init(&ihid->flock);
> +
> +       ret = i2chid_fetch_hid_descriptor(ihid);
> +       if (ret < 0)
> +               goto err;
> +
> +       ihid->irq = i2chid_init_irq(client);
> +       if (ihid->irq < 0)
> +               goto err;
> +
> +       hid = hid_allocate_device();
> +       if (IS_ERR(hid)) {
> +               ret = -ENOMEM;
> +               goto err;
> +       }
> +
> +       ihid->hid = hid;
> +
> +       hid->driver_data = client;
> +       hid->ll_driver = &i2c_hid_driver;
> +       hid->hid_get_raw_report = i2chid_get_raw_report;
> +       hid->hid_output_raw_report = i2chid_output_raw_report;
> +       hid->ff_init = hid_pidff_init;
> +#ifdef CONFIG_USB_HIDDEV
> +       hid->hiddev_connect = hiddev_connect;
> +       hid->hiddev_disconnect = hiddev_disconnect;
> +       hid->hiddev_hid_event = hiddev_hid_event;
> +       hid->hiddev_report_event = hiddev_report_event;
> +#endif
> +       hid->dev.parent = &client->dev;
> +       hid->bus = BUS_I2C;
> +       hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
> +       hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
> +       hid->product = le16_to_cpu(ihid->hdesc.wProductID);
> +
> +       snprintf(tmpstr, sizeof(tmpstr), " %04X:%04X",
> +                hid->vendor, hid->product);
> +       strlcpy(hid->name, client->name, sizeof(hid->name));
> +       strlcat(hid->name, tmpstr, sizeof(hid->name));
> +
> +       ret = hid_add_device(hid);
> +       if (ret) {
> +               if (ret != -ENODEV)
> +                       hid_err(client, "can't add hid device: %d\n", ret);
> +               goto err_mem_free;
> +       }
> +
> +       return 0;
> +
> +err_mem_free:
> +       hid_destroy_device(hid);
> +
> +err:
> +       if (ihid->irq >= 0)
> +               free_irq(ihid->irq, ihid);
> +
> +       kfree(ihid);
> +       return ret;
> +}
> +
> +static int __devexit i2chid_remove(struct i2c_client *client)
> +{
> +       struct i2chid *ihid = i2c_get_clientdata(client);
> +       struct hid_device *hid;
> +
> +       if (WARN_ON(!ihid))
> +               return -1;
> +
> +       hid = ihid->hid;
> +       hid_destroy_device(hid);
> +
> +       free_irq(client->irq, ihid);
> +
> +       kfree(ihid);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int i2chid_suspend(struct device *dev)
> +{
> +       struct i2c_client *client = to_i2c_client(dev);
> +       struct i2chid *ihid = i2c_get_clientdata(client);
> +
> +       if (device_may_wakeup(&client->dev))
> +               enable_irq_wake(ihid->irq);
> +
> +       /* Save some power */
> +       i2chid_set_power(client, I2C_HID_PWR_SLEEP);
> +
> +       return 0;
> +}
> +
> +static int i2chid_resume(struct device *dev)
> +{
> +       int ret;
> +       struct i2c_client *client = to_i2c_client(dev);
> +
> +       ret = i2chid_hwreset(client);
> +       if (ret)
> +               return ret;
> +
> +       if (device_may_wakeup(&client->dev))
> +               enable_irq_wake(client->irq);
> +
> +       return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(i2chid_pm, i2chid_suspend, i2chid_resume);
> +
> +static const struct i2c_device_id i2chid_id_table[] = {
> +       { "i2chid", 0 },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(i2c, i2chid_id_table);
> +
> +
> +static struct i2c_driver i2chid_driver = {
> +       .driver = {
> +               .name   = DRIVER_NAME,
> +               .owner  = THIS_MODULE,
> +               .pm     = &i2chid_pm,
> +       },
> +
> +       .probe          = i2chid_probe,
> +       .remove         = __devexit_p(i2chid_remove),
> +
> +       .id_table       = i2chid_id_table,
> +};
> +
> +module_i2c_driver(i2chid_driver);
> +
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/i2c/i2c-hid.h b/include/linux/i2c/i2c-hid.h
> new file mode 100644
> index 0000000..6685605
> --- /dev/null
> +++ b/include/linux/i2c/i2c-hid.h
> @@ -0,0 +1,35 @@
> +/*
> + * HID over I2C protocol implementation
> + *
> + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
> + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#ifndef __LINUX_I2C_HID_H
> +#define __LINUX_I2C_HID_H
> +
> +#include <linux/types.h>
> +
> +/**
> + * struct i2chid_platform_data - used by hid over i2c implementation.
> + * @hid_descriptor_address: i2c register where the HID descriptor is stored.
> + *
> + * Note that it is the responsibility for the platform driver (or the acpi 5.0
> + * driver) to setup the irq related to the gpio in the struct i2c_board_info.
> + * The platform driver should also setup the gpio according to the device:
> + *
> + * A typical example is the following:
> + *     irq = gpio_to_irq(intr_gpio);
> + *     hkdk4412_i2c_devs5[0].irq = irq; // store the irq in i2c_board_info
> + *     gpio_request(intr_gpio, "elan-irq");
> + *     s3c_gpio_setpull(intr_gpio, S3C_GPIO_PULL_UP);
> + */
> +struct i2chid_platform_data {
> +       u16 hid_descriptor_address;
> +};
> +
> +#endif /* __LINUX_I2C_HID_H */
> --
> 1.7.11.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
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
Benjamin Tissoires Oct. 3, 2012, 3:13 p.m. UTC | #3
Hi JJ,

On Wed, Oct 3, 2012 at 5:08 AM, Jian-Jhong Ding <jj_ding@emc.com.tw> wrote:
> Hi Benjamin,
>
> I have one little question about __i2chid_command(), please see below.
>
> "benjamin.tissoires" <benjamin.tissoires@gmail.com> writes:
>> From: Benjamin Tissoires <benjamin.tissoires@enac.fr>
>>
>> Microsoft published the protocol specification of HID over i2c:
>> http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
>>
>> This patch introduces an implementation of this protocol.
>>
>> This implementation does not includes the ACPI part of the specification.
>> This will come when ACPI 5.0 devices will be available.
>>
>> Once the ACPI part will be done, OEM will not have to declare HID over I2C
>> devices in their platform specific driver.
>>
>> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
>> ---
>>
[...] snipped
>> +     if (wait) {
>> +             spin_lock_irq(&ihid->flock);
>> +             set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
>> +             spin_unlock_irq(&ihid->flock);
>> +     }
>> +
>> +     do {
>> +             ret = i2c_transfer(client->adapter, msg, msg_num);
>> +             if (ret > 0)
>> +                     break;
>> +             tries--;
>> +             dev_dbg(&client->dev, "retrying i2chid_command:%d (%d)\n",
>> +                     command, tries);
>> +     } while (tries > 0);
>
> Do we have to do this retry here?  In i2c_transfer() it already does
> retry automatically on arbitration loss (please see __i2c_transfer() in
> drivers/i2c/i2c-core.c) that's defined by individual bus driver.  Maybe
> we don't have to retry here and make the code a bit simpler.
>

Indeed, this retry is not necessary. I'll remove it in v2.
Thanks for the review.

Cheers,
Benjamin

>> +     if (wait && ret > 0) {
>> +             if (debug)
>> +                     dev_err(&client->dev, "%s: waiting....\n", __func__);
>> +             if (!wait_event_timeout(ihid->wait,
>> +                             !test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
>> +                             msecs_to_jiffies(5000)))
>> +                     ret = -ENODATA;
>> +             if (debug)
>> +                     dev_err(&client->dev, "%s: finished.\n", __func__);
>> +     }
>> +
>> +     kfree(cmd);
>> +
>> +     return ret > 0 ? ret != msg_num : ret;
>> +}
>> +
>> +static int i2chid_command(struct i2c_client *client, int command,
>> +             unsigned char *buf_recv, int data_len)
>> +{
>> +     return __i2chid_command(client, command, 0, 0, NULL, 0,
>> +                             buf_recv, data_len);
>> +}
--
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
Benjamin Tissoires Oct. 3, 2012, 3:33 p.m. UTC | #4
Hi,

thanks also for the review. Two in the same day! I was about to send a
ping on that patch.... ;-)

On Wed, Oct 3, 2012 at 8:05 AM, Shubhrajyoti Datta
<omaplinuxkernel@gmail.com> wrote:
> On Fri, Sep 14, 2012 at 7:11 PM, benjamin.tissoires
> <benjamin.tissoires@gmail.com> wrote:
>> From: Benjamin Tissoires <benjamin.tissoires@enac.fr>
>>
>> Microsoft published the protocol specification of HID over i2c:
>> http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
>>
>> This patch introduces an implementation of this protocol.
>>
>> This implementation does not includes the ACPI part of the specification.
>> This will come when ACPI 5.0 devices will be available.
>>
>> Once the ACPI part will be done, OEM will not have to declare HID over I2C
>> devices in their platform specific driver.
>>
>> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
>> ---
>>
>> Hi,
>>
>> this is finally my first implementation of HID over I2C.
>>
>> This has been tested on an Elan Microelectronics HID over I2C device, with
>> a Samsung Exynos 4412 board.
>>
>> Any comments are welcome.
>>
>> Cheers,
>> Benjamin
>>
>>  drivers/i2c/Kconfig         |    8 +
>>  drivers/i2c/Makefile        |    1 +
>>  drivers/i2c/i2c-hid.c       | 1027 +++++++++++++++++++++++++++++++++++++++++++
>>  include/linux/i2c/i2c-hid.h |   35 ++
>>  4 files changed, 1071 insertions(+)
>>  create mode 100644 drivers/i2c/i2c-hid.c
>>  create mode 100644 include/linux/i2c/i2c-hid.h
>>
>> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
>> index 5a3bb3d..5adf65a 100644
>> --- a/drivers/i2c/Kconfig
>> +++ b/drivers/i2c/Kconfig
>> @@ -47,6 +47,14 @@ config I2C_CHARDEV
>>           This support is also available as a module.  If so, the module
>>           will be called i2c-dev.
>>
>> +config I2C_HID
>> +       tristate "HID over I2C bus"
>> +       help
>> +         Say Y here to use the HID over i2c protocol implementation.
>> +
>> +         This support is also available as a module.  If so, the module
>> +         will be called i2c-hid.
>> +
>>  config I2C_MUX
>>         tristate "I2C bus multiplexing support"
>>         help
>> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
>> index beee6b2..8f38116 100644
>> --- a/drivers/i2c/Makefile
>> +++ b/drivers/i2c/Makefile
>> @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_BOARDINFO)     += i2c-boardinfo.o
>>  obj-$(CONFIG_I2C)              += i2c-core.o
>>  obj-$(CONFIG_I2C_SMBUS)                += i2c-smbus.o
>>  obj-$(CONFIG_I2C_CHARDEV)      += i2c-dev.o
>> +obj-$(CONFIG_I2C_HID)          += i2c-hid.o
>>  obj-$(CONFIG_I2C_MUX)          += i2c-mux.o
>>  obj-y                          += algos/ busses/ muxes/
>>
>> diff --git a/drivers/i2c/i2c-hid.c b/drivers/i2c/i2c-hid.c
>> new file mode 100644
>> index 0000000..eb17d8c
>> --- /dev/null
>> +++ b/drivers/i2c/i2c-hid.c
>> @@ -0,0 +1,1027 @@
>> +/*
>> + * HID over I2C protocol implementation
>> + *
>> + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
>> + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
>> + *
>> + * This code is partly based on "USB HID support for Linux":
>> + *
>> + *  Copyright (c) 1999 Andreas Gal
>> + *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
>> + *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
>> + *  Copyright (c) 2007-2008 Oliver Neukum
>> + *  Copyright (c) 2006-2010 Jiri Kosina
>> + *
>> + * This file is subject to the terms and conditions of the GNU General Public
>> + * License.  See the file COPYING in the main directory of this archive for
>> + * more details.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/i2c.h>
>> +#include <linux/irq.h>
>> +#include <linux/gpio.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/input.h>
>> +#include <linux/delay.h>
>> +#include <linux/slab.h>
>> +#include <linux/pm.h>
>> +
>> +#include <linux/i2c/i2c-hid.h>
>> +
>> +#include <linux/hid.h>
>> +#include <linux/hiddev.h>
>> +#include <linux/hidraw.h>
>> +
>> +#define DRIVER_NAME            "i2chid"
>> +#define DRIVER_DESC            "HID over I2C core driver"
>> +
>> +#define I2C_HID_COMMAND_TRIES  3
>> +
>> +/* flags */
>> +#define I2C_HID_STARTED                (1 << 0)
>> +#define I2C_HID_OUT_RUNNING    (1 << 1)
>> +#define I2C_HID_IN_RUNNING     (1 << 2)
>> +#define I2C_HID_RESET_PENDING  (1 << 3)
>> +#define I2C_HID_SUSPENDED      (1 << 4)
>> +
>> +#define I2C_HID_PWR_ON         0x00
>> +#define I2C_HID_PWR_SLEEP      0x01
>> +
>> +/* debug option */
>> +static bool debug = false;
>> +module_param(debug, bool, 0444);
>> +MODULE_PARM_DESC(debug, "print a lot of debug informations");
>> +
>> +struct i2chid_desc {
>> +       __le16 wHIDDescLength;
>> +       __le16 bcdVersion;
>> +       __le16 wReportDescLength;
>> +       __le16 wReportDescRegister;
>> +       __le16 wInputRegister;
>> +       __le16 wMaxInputLength;
>> +       __le16 wOutputRegister;
>> +       __le16 wMaxOutputLength;
>> +       __le16 wCommandRegister;
>> +       __le16 wDataRegister;
>> +       __le16 wVendorID;
>> +       __le16 wProductID;
>> +       __le16 wVersionID;
>> +} __packed;
>> +
>> +struct i2chid_cmd {
>> +       enum {
>> +               /* fecth HID descriptor */
>> +               HID_DESCR_CMD,
>> +
>> +               /* fetch report descriptors */
>> +               HID_REPORT_DESCR_CMD,
>> +
>> +               /* commands */
>> +               HID_RESET_CMD,
>> +               HID_GET_REPORT_CMD,
>> +               HID_SET_REPORT_CMD,
>> +               HID_GET_IDLE_CMD,
>> +               HID_SET_IDLE_CMD,
>> +               HID_GET_PROTOCOL_CMD,
>> +               HID_SET_PROTOCOL_CMD,
>> +               HID_SET_POWER_CMD,
>> +
>> +               /* read/write data register */
>> +               HID_DATA_CMD,
>> +       } name;
>> +       unsigned int registerIndex;
>> +       __u8 opcode;
>> +       unsigned int length;
>> +       bool wait;
>> +};
>> +
>> +union command {
>> +       u8 data[0];
>> +       struct cmd {
>> +               __le16 reg;
>> +               __u8 reportTypeID;
>> +               __u8 opcode;
>> +       } __packed c;
>> +};
>> +
>> +#define I2C_HID_CMD(name_, opcode_) \
>> +       .name = name_, .opcode = opcode_, .length = 4, \
>> +       .registerIndex = offsetof(struct i2chid_desc, wCommandRegister)
>> +
>> +static const struct i2chid_cmd cmds[] = {
>> +       { I2C_HID_CMD(HID_RESET_CMD,            0x01), .wait = true },
>> +       { I2C_HID_CMD(HID_GET_REPORT_CMD,       0x02) },
>> +       { I2C_HID_CMD(HID_SET_REPORT_CMD,       0x03) },
>> +       { I2C_HID_CMD(HID_GET_IDLE_CMD,         0x04) },
>> +       { I2C_HID_CMD(HID_SET_IDLE_CMD,         0x05) },
>> +       { I2C_HID_CMD(HID_GET_PROTOCOL_CMD,     0x06) },
>> +       { I2C_HID_CMD(HID_SET_PROTOCOL_CMD,     0x07) },
>> +       { I2C_HID_CMD(HID_SET_POWER_CMD,        0x08) },
>> +
>> +       {.name = HID_DESCR_CMD,
>> +        .length = 2},
>> +
>> +       {.name = HID_REPORT_DESCR_CMD,
>> +        .registerIndex = offsetof(struct i2chid_desc, wReportDescRegister),
>> +        .opcode = 0x00,
>> +        .length = 2},
>> +
>> +       {.name = HID_DATA_CMD,
>> +        .registerIndex = offsetof(struct i2chid_desc, wDataRegister),
>> +        .opcode = 0x00,
>> +        .length = 2},
>> +
>> +       {}, /* terminating */
>> +};
>> +
>> +/* The main device structure */
>> +struct i2chid {
>> +       struct i2c_client       *client;        /* i2c client */
>> +       struct hid_device       *hid;   /* pointer to corresponding HID dev */
>> +       union {
>> +               __u8 hdesc_buffer[sizeof(struct i2chid_desc)];
>> +               struct i2chid_desc hdesc;       /* the HID Descriptor */
>> +       };
>> +       __le16                  wHIDDescRegister; /* location of the i2c
>> +                                                  * register of the HID
>> +                                                  * descriptor. */
>> +       unsigned int            bufsize;        /* i2c buffer size */
>> +       char                    *inbuf;         /* Input buffer */
>> +
>> +       spinlock_t              flock;          /* flags spinlock */
>> +       unsigned long           flags;          /* device flags */
>> +
>> +       int                     irq;            /* the interrupt line irq */
>> +
>> +       wait_queue_head_t       wait;           /* For waiting the interrupt */
>> +};
>> +
>> +void i2chid_print_buffer(struct i2chid *ihid, u8 *buf, unsigned int n)
>> +{
>> +       int i;
>> +       char *string = kzalloc((n*3+1)*sizeof(char), GFP_KERNEL);
>> +       char tmpbuf[4] = "";
>> +
>> +       for (i = 0; i < n; ++i) {
>> +               sprintf(tmpbuf, "%02x ", buf[i] & 0xFF);
>> +               strcat(string, tmpbuf);
>> +       }
>> +
>> +       dev_err(&ihid->client->dev, "%s\n", string);
>> +       kfree(string);
>> +}
>> +
>> +static int __i2chid_command(struct i2c_client *client, int command, u8 reportID,
>> +               u8 reportType, u8 *args, int args_len,
>> +               unsigned char *buf_recv, int data_len)
>> +{
>> +       struct i2chid *ihid = i2c_get_clientdata(client);
>> +       union command *cmd;
>> +       unsigned char *rec_buf = buf_recv;
>> +       int ret;
>> +       int tries = I2C_HID_COMMAND_TRIES;
>> +       int length = 0;
>> +       struct i2c_msg msg[2];
>> +       int msg_num = 0;
>> +       int i;
>> +       bool wait = false;
>> +
>> +       if (WARN_ON(!ihid))
>> +               return -ENODEV;
>> +
>> +       cmd = kmalloc(args_len + sizeof(union command), GFP_KERNEL);
>> +       if (!cmd)
>> +               return -ENOMEM;
>> +
>> +       for (i = 0; cmds[i].length; i++) {
>> +               unsigned int registerIndex;
>> +
>> +               if (cmds[i].name != command)
>> +                       continue;
>> +
>> +               registerIndex = cmds[i].registerIndex;
>> +               cmd->data[0] = ihid->hdesc_buffer[registerIndex];
>> +               cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1];
>> +               cmd->c.opcode = cmds[i].opcode;
>> +               length = cmds[i].length;
>> +               wait = cmds[i].wait;
>> +       }
>> +
>> +       /* special case for HID_DESCR_CMD */
>> +       if (command == HID_DESCR_CMD)
>> +               cmd->c.reg = ihid->wHIDDescRegister;
>> +
>> +       cmd->c.reportTypeID = reportID | reportType << 4;
>> +
>> +       memcpy(cmd->data + length, args, args_len);
>> +       length += args_len;
>> +
>> +       if (debug) {
>> +               char tmpstr[4] = "";
>> +               char strbuf[50] = "";
>> +               int i;
>> +               for (i = 0; i < length; i++) {
>> +                       sprintf(tmpstr, "%02x ", cmd->data[i] & 0xFF);
>> +                       strcat(strbuf, tmpstr);
>> +               }
>> +               dev_err(&client->dev, "%s, cmd=%s\n", __func__, strbuf);
>> +       }
>> +
>> +       msg[0].addr = client->addr;
>> +       msg[0].flags = client->flags & I2C_M_TEN;
>> +       msg[0].len = length;
>> +       msg[0].buf = (char *) cmd->data;
>> +       msg[1].addr = client->addr;
>> +       msg[1].flags = client->flags & I2C_M_TEN;
>> +       msg[1].flags |= I2C_M_RD;
>> +       msg[1].len = data_len;
>> +       msg[1].buf = rec_buf;
>> +
>> +       msg_num = data_len > 0 ? 2 : 1;
>> +
>> +       if (wait) {
>> +               spin_lock_irq(&ihid->flock);
>> +               set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
>> +               spin_unlock_irq(&ihid->flock);
>> +       }
>> +
>> +       do {
>> +               ret = i2c_transfer(client->adapter, msg, msg_num);
>> +               if (ret > 0)
>> +                       break;
>> +               tries--;
>> +               dev_dbg(&client->dev, "retrying i2chid_command:%d (%d)\n",
>> +                       command, tries);
>> +       } while (tries > 0);
>> +
>> +       if (wait && ret > 0) {
>> +               if (debug)
>> +                       dev_err(&client->dev, "%s: waiting....\n", __func__);
>> +               if (!wait_event_timeout(ihid->wait,
>> +                               !test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
>> +                               msecs_to_jiffies(5000)))
>> +                       ret = -ENODATA;
>> +               if (debug)
>> +                       dev_err(&client->dev, "%s: finished.\n", __func__);
>> +       }
>> +
>> +       kfree(cmd);
>> +
>> +       return ret > 0 ? ret != msg_num : ret;
>> +}
>> +
>> +static int i2chid_command(struct i2c_client *client, int command,
>> +               unsigned char *buf_recv, int data_len)
>> +{
>> +       return __i2chid_command(client, command, 0, 0, NULL, 0,
>> +                               buf_recv, data_len);
>> +}
>> +
>> +static int i2chid_get_report(struct i2c_client *client, u8 reportType,
>> +               u32 reportID, unsigned char *buf_recv, int data_len)
>> +{
>> +       struct i2chid *ihid = i2c_get_clientdata(client);
>> +       u8 args[6];
>> +       int ret;
>> +       int args_len = 0;
>> +       u16 readRegister = ihid->hdesc.wDataRegister;
>> +
>> +       if (debug)
>> +               dev_err(&client->dev, "%s\n", __func__);
>> +
>> +       if (reportID >= 15) {
>> +               args[args_len++] = (reportID >> 0) & 0xFF;
>> +               args[args_len++] = (reportID >> 8) & 0xFF;
>> +               args[args_len++] = (reportID >> 16) & 0xFF;
>> +               args[args_len++] = (reportID >> 24) & 0xFF;
>> +               reportID = 0x0F;
>> +       }
>> +
>> +       args[args_len++] = readRegister & 0xff;
>> +       args[args_len++] = (readRegister >> 8) & 0xff;
>> +
>> +       ret = __i2chid_command(client, HID_GET_REPORT_CMD, reportID & 0x0F,
>> +               reportType, args, args_len, buf_recv, data_len);
>> +       if (ret) {
>> +               dev_err(&client->dev, "HID_GET_REPORT_CMD Fail\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int i2chid_set_report(struct i2c_client *client, u8 reportType,
>> +               u32 reportID, unsigned char *buf, int data_len)
>> +{
>> +       struct i2chid *ihid = i2c_get_clientdata(client);
>> +       u8 *args;
>> +       int ret;
>> +       u16 dataRegister = ihid->hdesc.wDataRegister;
>> +       int args_len =  (reportID >= 15 ? 4 : 0) +
>> +                       2 /* dataRegister */ +
>> +                       2 /* size */ +
>> +                       data_len;
>> +       int index = 0;
>> +
>> +       if (debug)
>> +               dev_err(&client->dev, "%s\n", __func__);
>> +
>> +       args = kmalloc(args_len, GFP_KERNEL);
>> +
>> +       if (reportID >= 15) {
>> +               args[index++] = (reportID >> 0) & 0xFF;
>> +               args[index++] = (reportID >> 8) & 0xFF;
>> +               args[index++] = (reportID >> 16) & 0xFF;
>> +               args[index++] = (reportID >> 24) & 0xFF;
>> +               reportID = 0x0F;
>> +       }
>> +
>> +       args[index++] = dataRegister & 0xff;
>> +       args[index++] = (dataRegister >> 8) & 0xff;
>> +
>> +       args[index++] = data_len & 0xff;
>> +       args[index++] = (data_len >> 8) & 0xff;
>> +
>> +       memcpy(&args[index], buf, data_len);
>> +
>> +       ret = __i2chid_command(client, HID_SET_REPORT_CMD, reportID & 0x0F,
>> +               reportType, args, args_len, NULL, 0);
>> +       if (ret) {
>> +               dev_err(&client->dev, "HID_SET_REPORT_CMD Fail\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       kfree(args);
>> +       return data_len;
>> +}
>> +
>> +static int i2chid_set_power(struct i2c_client *client, int power_state)
>> +{
>> +       int ret;
>> +
>> +       if (debug)
>> +               dev_err(&client->dev, "%s\n", __func__);
>> +
>> +       ret = __i2chid_command(client, HID_SET_POWER_CMD, power_state & 0x01,
>> +               0, NULL, 0, NULL, 0);
>> +       if (ret) {
>> +               dev_err(&client->dev, "HID_SET_POWER_CMD Fail\n");
>> +               return -EINVAL;
> may be errors can be passed on.

Yep, good catch.

>
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int i2chid_hwreset(struct i2c_client *client)
>> +{
>> +       uint8_t buf_recv[79];
>> +       int ret;
>> +
>> +       if (debug)
>> +               dev_err(&client->dev, "%s\n", __func__);
>> +
>> +       ret = i2chid_set_power(client, I2C_HID_PWR_ON);
>> +       if (ret)
>> +               return -EINVAL;
> why not just pass on the error?

same thing, will change it in v2.

>
>> +
>> +       if (debug)
>> +               dev_err(&client->dev, "resetting...\n");
> this is a little uncustomary.
>
> May be consider  bdg

Sorry for that. I don't get your point here. You don't like the whole
"if(debug) dev_err(...)" or just the "dev_err(...)" call?

Anyway, can you be a little bit more explicit in what you mean by bdg?
I found bdg in the sources with "bdget" -> 'block device get', but I
doubt this is what you meant.


>> +
>> +       ret = i2chid_command(client, HID_RESET_CMD, buf_recv, 0);
>> +       if (ret) {
>> +               dev_err(&client->dev, "HID_RESET_CMD Fail\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       ret = i2c_master_recv(client, buf_recv, 2);
>> +       if (ret != 2 || (buf_recv[0] != 0 && buf_recv[1] != 0)) {
>> +               dev_err(&client->dev,
>> +                       "HID_RESET_CMD Failed: got %02x %02x, size=%d\n",
>> +                       buf_recv[0], buf_recv[1], ret);
>> +
>> +               i2chid_set_power(client, I2C_HID_PWR_SLEEP);
>> +               return -EINVAL;
>> +       }
>> +       return 0;
>> +}
>> +
>> +static bool i2chid_get_input(struct i2chid *ihid)
>> +{
>> +       int ret;
>> +       int size = ihid->hdesc.wMaxInputLength;
>> +
>> +       ret = i2c_master_recv(ihid->client, ihid->inbuf, size);
>> +       if (ret != size) {
>> +               dev_err(&ihid->client->dev, "%s: got %d data instead of %d\n",
>> +                       __func__, ret, size);
>> +               return false;
>> +       }
>> +
>> +       if (debug)
>> +               i2chid_print_buffer(ihid, ihid->inbuf, size);
>> +
>> +       ret = hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2,
>> +                       size - 2, 1);
>> +       if (ret)
>> +               return false;
>> +
>> +       return true;
>> +}
>> +
>> +static irqreturn_t i2chid_irq(int irq, void *dev_id)
>> +{
>> +       struct i2chid *ihid = dev_id;
>> +       int ready;
>> +
>> +       if (!ihid)
>> +               return IRQ_HANDLED;
>> +
>> +       spin_lock_irq(&ihid->flock);
>> +       if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags)) {
>> +               spin_unlock_irq(&ihid->flock);
>> +
>> +               wake_up(&ihid->wait);
>> +               return IRQ_HANDLED;
>> +       }
>> +
>> +       ready = test_bit(I2C_HID_STARTED, &ihid->flags);
>> +       spin_unlock_irq(&ihid->flock);
>> +
>> +       if (ready)
>> +               i2chid_get_input(ihid);
>> +
>> +       return IRQ_HANDLED;
>> +}
>> +
>> +static int i2chid_get_report_length(struct hid_report *report)
>> +{
>> +       return ((report->size - 1) >> 3) + 1 +
>> +               report->device->report_enum[report->type].numbered + 2;
>> +}
>> +
>> +void i2chid_init_report(struct hid_report *report)
>> +{
>> +       struct hid_device *hid = report->device;
>> +       struct i2c_client *client = hid->driver_data;
>> +       struct i2chid *ihid = i2c_get_clientdata(client);
>> +       unsigned int size, ret_size;
>> +
>> +       size = i2chid_get_report_length(report);
>> +       i2chid_get_report(client,
>> +                       report->type == HID_FEATURE_REPORT ? 0x03 : 0x01,
>> +                       report->id, ihid->inbuf, size);
>> +
>> +       if (debug)
>> +               i2chid_print_buffer(ihid, ihid->inbuf, size);
>> +
>> +       ret_size = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
>> +
>> +       if (ret_size != size) {
>> +               dev_err(&client->dev, "error in %s size:%d / ret_size:%d\n",
>> +                       __func__, size, ret_size);
>> +               return;
>> +       }
>> +
>> +       /* hid->driver_lock is held as we are in probe function,
>> +        * we just need to setup the input fields, so using
>> +        * hid_report_raw_event is safe. */
>> +       hid_report_raw_event(hid, report->type, ihid->inbuf + 2, size - 2, 1);
>> +}
>> +
>> +/*
>> + * Initialize all reports
>> + */
>> +void i2chid_init_reports(struct hid_device *hid)
>> +{
>> +       struct hid_report *report;
>> +
>> +       list_for_each_entry(report,
>> +               &hid->report_enum[HID_INPUT_REPORT].report_list, list)
>> +               i2chid_init_report(report);
>> +
>> +       list_for_each_entry(report,
>> +               &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
>> +               i2chid_init_report(report);
>> +}
>> +
>> +/*
>> + * Traverse the supplied list of reports and find the longest
>> + */
>> +static void i2chid_find_max_report(struct hid_device *hid, unsigned int type,
>> +               unsigned int *max)
>> +{
>> +       struct hid_report *report;
>> +       unsigned int size;
>> +
>> +       /* We should not rely on wMaxInputLength, as some devices may set it to
>> +        * a wrong length. */
>> +       list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
>> +               size = i2chid_get_report_length(report);
>> +               if (*max < size)
>> +                       *max = size;
>> +       }
>> +}
>> +
>> +static int i2chid_alloc_buffers(struct i2chid *ihid)
>> +{
>> +       ihid->inbuf = kzalloc(ihid->bufsize, GFP_KERNEL);
>> +
>> +       if (!ihid->inbuf)
>> +               return -1;
>> +
>> +       return 0;
>> +}
>> +
>> +static void i2chid_free_buffers(struct i2chid *ihid)
>> +{
>> +       kfree(ihid->inbuf);
>> +}
>> +
>> +static int i2chid_get_raw_report(struct hid_device *hid,
>> +               unsigned char report_number, __u8 *buf, size_t count,
>> +               unsigned char report_type)
>> +{
>> +       struct i2c_client *client = hid->driver_data;
>> +       struct i2chid *ihid = i2c_get_clientdata(client);
>> +       int ret;
>> +
>> +       if (count > ihid->bufsize)
>> +               count = ihid->bufsize;
>> +
>> +       ret = i2chid_get_report(client,
>> +                       report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
>> +                       report_number, ihid->inbuf, count);
>> +
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       count = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
>> +
>> +       memcpy(buf, ihid->inbuf + 2, count);
>> +
>> +       return count;
>> +}
>> +
>> +static int i2chid_output_raw_report(struct hid_device *hid, __u8 *buf,
>> +               size_t count, unsigned char report_type)
>> +{
>> +       struct i2c_client *client = hid->driver_data;
>> +       int ret;
>> +       int report_id = buf[0];
>> +
>> +       ret = i2chid_set_report(client,
>> +                               report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
>> +                               report_id, buf, count);
>> +
>> +       return ret;
>> +
>> +}
>> +
>> +static int i2chid_fetch_hid_descriptor(struct i2chid *ihid)
>> +{
>> +       struct i2c_client *client = ihid->client;
>> +       struct i2chid_desc *hdesc = &ihid->hdesc;
>> +       unsigned int dsize = 0;
>> +       int ret;
>> +
>> +       /* Fetch the length of HID description, retrieve the 4 first bytes:
>> +        * bytes 0-1 -> length
>> +        * bytes 2-3 -> bcdVersion (has to be 1.00) */
>> +       ret = i2chid_command(client, HID_DESCR_CMD, ihid->hdesc_buffer, 4);
>> +
>> +       if (debug)
>> +               dev_err(&client->dev,
>> +                       "%s, ihid->hdesc_buffer: %02x %02x %02x %02x\n",
>> +                       __func__,
>> +                       ihid->hdesc_buffer[0],
>> +                       ihid->hdesc_buffer[1],
>> +                       ihid->hdesc_buffer[2],
>> +                       ihid->hdesc_buffer[3]);
>> +
>> +       if (ret) {
>> +               dev_err(&client->dev, "HID_DESCR_LENGTH_CMD Fail (ret=%d)\n",
>> +                       ret);
>> +               return -EINVAL;
>> +       }
>> +
>> +       dsize = le16_to_cpu(hdesc->wHIDDescLength);
>> +       if (!dsize || dsize > HID_MAX_DESCRIPTOR_SIZE) {
>> +               dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
>> +                       dsize);
>> +               return -EINVAL;
>> +       }
>> +
>> +       /* check bcdVersion == 1.0 */
>> +       if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
>> +               dev_err(&client->dev,
>> +                       "unexpected HID descriptor bcdVersion (0x%04x)\n",
>> +                       le16_to_cpu(hdesc->bcdVersion));
>> +               return -EINVAL;
>> +       }
>> +
>> +       if (debug)
>> +               dev_err(&client->dev, "Fetching the HID descriptor\n");
>> +
>> +       ret = i2chid_command(client, HID_DESCR_CMD, ihid->hdesc_buffer, dsize);
>> +       if (ret) {
>> +               dev_err(&client->dev, "HID_DESCR_CMD Fail\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       if (debug)
>> +               i2chid_print_buffer(ihid, ihid->hdesc_buffer, dsize);
>> +
>> +       return 0;
>> +}
>> +
>> +static int i2chid_parse(struct hid_device *hid)
>> +{
>> +       struct i2c_client *client = hid->driver_data;
>> +       struct i2chid *ihid = i2c_get_clientdata(client);
>> +       struct i2chid_desc *hdesc = &ihid->hdesc;
>> +       unsigned int rsize = 0;
>> +       char *rdesc;
>> +       int ret;
>> +       int tries = 3;
>> +
>> +       if (debug)
>> +               dev_err(&client->dev, "entering %s\n", __func__);
>> +
>> +       rsize = le16_to_cpu(hdesc->wReportDescLength);
>> +       if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
>> +               dbg_hid("weird size of report descriptor (%u)\n", rsize);
>> +               return -EINVAL;
>> +       }
>> +
>> +       do {
>> +               ret = i2chid_hwreset(client);
>> +               if (ret)
>> +                       msleep(1000);
>> +       } while (tries-- > 0 && ret);
>> +
>> +       if (ret)
>> +               return ret;
>> +
>> +       rdesc = kmalloc(rsize, GFP_KERNEL);
>> +
>> +       if (!rdesc) {
>> +               dbg_hid("couldn't allocate rdesc memory\n");
>> +               return -ENOMEM;
>> +       }
>> +
>> +       if (debug)
>> +               dev_err(&client->dev, "asking HID report descriptor\n");
>> +
>> +       ret = i2chid_command(client, HID_REPORT_DESCR_CMD, rdesc, rsize);
>> +       if (ret) {
>> +               hid_err(hid, "reading report descriptor failed\n");
>> +               kfree(rdesc);
>> +               ret = -ENOMEM;
>> +               goto err;
>> +       }
>> +
>> +       if (debug)
>> +               i2chid_print_buffer(ihid, rdesc, rsize);
>> +
>> +       ret = hid_parse_report(hid, rdesc, rsize);
>> +       kfree(rdesc);
>> +       if (ret) {
>> +               dbg_hid("parsing report descriptor failed\n");
>> +               goto err;
>> +       }
>> +
>> +       return 0;
>> +err:
>> +       return ret;
>> +}
>> +
>> +static int i2chid_start(struct hid_device *hid)
>> +{
>> +       struct i2c_client *client = hid->driver_data;
>> +       struct i2chid *ihid = i2c_get_clientdata(client);
>> +       int ret;
>> +
>> +       ihid->bufsize = HID_MIN_BUFFER_SIZE;
>> +       i2chid_find_max_report(hid, HID_INPUT_REPORT, &ihid->bufsize);
>> +       i2chid_find_max_report(hid, HID_OUTPUT_REPORT, &ihid->bufsize);
>> +       i2chid_find_max_report(hid, HID_FEATURE_REPORT, &ihid->bufsize);
>> +
>> +       if (ihid->bufsize > HID_MAX_BUFFER_SIZE)
>> +               ihid->bufsize = HID_MAX_BUFFER_SIZE;
>> +
>> +       if (i2chid_alloc_buffers(ihid)) {
>> +               ret = -ENOMEM;
>> +               goto fail;
>> +       }
>> +
>> +       if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
>> +               i2chid_init_reports(hid);
>> +
>> +       return 0;
>> +
>> +fail:
>> +       i2chid_free_buffers(ihid);
>> +       return ret;
>> +}
>> +
>> +static void i2chid_stop(struct hid_device *hid)
>> +{
>> +       struct i2c_client *client = hid->driver_data;
>> +       struct i2chid *ihid = i2c_get_clientdata(client);
>> +
>> +       if (WARN_ON(!ihid))
>> +               return;
>> +
>> +       hid->claimed = 0;
>> +
>> +       i2chid_free_buffers(ihid);
>> +}
>> +
>> +static int i2chid_open(struct hid_device *hid)
>> +{
>> +       struct i2c_client *client = hid->driver_data;
>> +       struct i2chid *ihid = i2c_get_clientdata(client);
>> +       int ret;
>> +
>> +       if (!hid->open++) {
>> +               ret = i2chid_set_power(client, I2C_HID_PWR_ON);
>> +               if (ret) {
>> +                       hid->open--;
>> +                       return -EIO;
>> +               }
>> +               spin_lock_irq(&ihid->flock);
>> +               set_bit(I2C_HID_STARTED, &ihid->flags);
>> +               spin_unlock_irq(&ihid->flock);
>> +       }
>> +       return 0;
>> +}
>> +
>> +static void i2chid_close(struct hid_device *hid)
>> +{
>> +       struct i2c_client *client = hid->driver_data;
>> +       struct i2chid *ihid = i2c_get_clientdata(client);
>> +
>> +       /* protecting hid->open to make sure we don't restart
>> +        * data acquistion due to a resumption we no longer
>> +        * care about
>> +        */
>> +       if (!--hid->open) {
>> +               spin_lock_irq(&ihid->flock);
>> +               clear_bit(I2C_HID_STARTED, &ihid->flags);
>> +               spin_unlock_irq(&ihid->flock);
>> +
>> +               /* Save some power */
>> +               i2chid_set_power(client, I2C_HID_PWR_SLEEP);
>> +       }
>> +}
>> +
>> +static int i2chid_power(struct hid_device *hid, int lvl)
>> +{
>> +       struct i2c_client *client = hid->driver_data;
>> +       int r = 0;
>> +
>> +       if (debug)
>> +               dev_err(&client->dev, "%s lvl:%d\n", __func__, lvl);
>> +
>> +       switch (lvl) {
>> +       case PM_HINT_FULLON:
>> +               r = i2chid_set_power(client, I2C_HID_PWR_ON);
>> +               break;
>> +       case PM_HINT_NORMAL:
>> +               r = i2chid_set_power(client, I2C_HID_PWR_SLEEP);
>> +               break;
>> +       }
>> +       return r;
>> +}
>> +
>> +static int i2chid_hidinput_input_event(struct input_dev *dev, unsigned int type,
>> +               unsigned int code, int value)
>> +{
>> +       struct hid_device *hid = input_get_drvdata(dev);
>> +       struct hid_field *field;
>> +       int offset;
>> +
>> +       if (type == EV_FF)
>> +               return input_ff_event(dev, type, code, value);
>> +
>> +       if (type != EV_LED)
>> +               return -1;
>> +
>> +       offset = hidinput_find_field(hid, type, code, &field);
>> +
>> +       if (offset == -1) {
>> +               hid_warn(dev, "event field not found\n");
>> +               return -1;
>> +       }
>> +
>> +       hid_set_field(field, offset, value);
>> +
>> +       return 0;
>> +}
>> +
>> +static struct hid_ll_driver i2c_hid_driver = {
>> +       .parse = i2chid_parse,
>> +       .start = i2chid_start,
>> +       .stop = i2chid_stop,
>> +       .open = i2chid_open,
>> +       .close = i2chid_close,
>> +       .power = i2chid_power,
>> +       .hidinput_input_event = i2chid_hidinput_input_event,
>> +};
>> +
>> +static int i2chid_init_irq(struct i2c_client *client)
>> +{
>> +       struct i2chid *ihid = i2c_get_clientdata(client);
>> +       int rc;
>> +
>> +       dev_dbg(&client->dev, "Requesting IRQ: %d\n",
>> +               client->irq);
>> +
>> +       rc = request_threaded_irq(client->irq, NULL, i2chid_irq,
>> +                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
>> +                       DRIVER_NAME, ihid);
>> +       if (rc < 0) {
>> +               dev_dbg(&client->dev,
>> +                       "Could not register for %s interrupt, irq = %d,"
>> +                       " rc = %d\n",
>> +               DRIVER_NAME, client->irq, rc);
>> +
>> +               return rc;
>> +       }
>> +
>> +       return client->irq;
>> +}
>> +
>> +static int __devinit i2chid_probe(struct i2c_client *client,
>> +               const struct i2c_device_id *dev_id)
>> +{
>> +       int ret;
>> +       struct i2chid *ihid;
>> +       struct hid_device *hid;
>> +       __u16 hidRegister;
>> +       unsigned char tmpstr[11];
>> +       struct i2chid_platform_data *platform_data = client->dev.platform_data;
>> +
>> +       dbg_hid("HID probe called for i2c %d\n", client->addr);
>> +
>> +       if (!platform_data) {
>> +               dev_err(&client->dev, "HID register address not provided\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       if (client->irq < 1) {
>> +               dev_err(&client->dev,
>> +                       "HID over i2c has not been provided an Int IRQ\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       ihid = kzalloc(sizeof(struct i2chid), GFP_KERNEL);
>> +       if (!ihid)
>> +               return -ENOMEM;
>
> you may consider devm_* function here.

I'd rather not without Dmitry's agreement. Apparently, there were many
different threads on the input-LKML about devm_ functions, and most of
the time the answer is: 'stick to un-managed memory'.

for instance, in https://patchwork.kernel.org/patch/1235881/, quoting Dmitry:

"""
Another botched devm_ conversion. *sigh*

Input device gone, IRQ arrives, kernel goes oops, machine hangs hard.

Please, do not use devm_ interfaces unless... Actually, just do not use
nor suggest devm_interfaces until all resources are devm-ized. Mixing 2
styles of releasing resources leads to trouble.
"""

And I didn't saw Dmitry's patch in which he devm-ized the input layer.

Cheers,
Benjamin

>
>> +
>> +       i2c_set_clientdata(client, ihid);
>> +
>> +       ihid->client = client;
>> +       ihid->flags = 0;
>> +
>> +       hidRegister = platform_data->hid_descriptor_address;
>> +       ihid->wHIDDescRegister = cpu_to_le16(hidRegister);
>> +
>> +       init_waitqueue_head(&ihid->wait);
>> +       spin_lock_init(&ihid->flock);
>> +
>> +       ret = i2chid_fetch_hid_descriptor(ihid);
>> +       if (ret < 0)
>> +               goto err;
>> +
>> +       ihid->irq = i2chid_init_irq(client);
>> +       if (ihid->irq < 0)
>> +               goto err;
>> +
>> +       hid = hid_allocate_device();
>> +       if (IS_ERR(hid)) {
>> +               ret = -ENOMEM;
>> +               goto err;
>> +       }
>> +
>> +       ihid->hid = hid;
>> +
>> +       hid->driver_data = client;
>> +       hid->ll_driver = &i2c_hid_driver;
>> +       hid->hid_get_raw_report = i2chid_get_raw_report;
>> +       hid->hid_output_raw_report = i2chid_output_raw_report;
>> +       hid->ff_init = hid_pidff_init;
>> +#ifdef CONFIG_USB_HIDDEV
>> +       hid->hiddev_connect = hiddev_connect;
>> +       hid->hiddev_disconnect = hiddev_disconnect;
>> +       hid->hiddev_hid_event = hiddev_hid_event;
>> +       hid->hiddev_report_event = hiddev_report_event;
>> +#endif
>> +       hid->dev.parent = &client->dev;
>> +       hid->bus = BUS_I2C;
>> +       hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
>> +       hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
>> +       hid->product = le16_to_cpu(ihid->hdesc.wProductID);
>> +
>> +       snprintf(tmpstr, sizeof(tmpstr), " %04X:%04X",
>> +                hid->vendor, hid->product);
>> +       strlcpy(hid->name, client->name, sizeof(hid->name));
>> +       strlcat(hid->name, tmpstr, sizeof(hid->name));
>> +
>> +       ret = hid_add_device(hid);
>> +       if (ret) {
>> +               if (ret != -ENODEV)
>> +                       hid_err(client, "can't add hid device: %d\n", ret);
>> +               goto err_mem_free;
>> +       }
>> +
>> +       return 0;
>> +
>> +err_mem_free:
>> +       hid_destroy_device(hid);
>> +
>> +err:
>> +       if (ihid->irq >= 0)
>> +               free_irq(ihid->irq, ihid);
>> +
>> +       kfree(ihid);
>> +       return ret;
>> +}
>> +
>> +static int __devexit i2chid_remove(struct i2c_client *client)
>> +{
>> +       struct i2chid *ihid = i2c_get_clientdata(client);
>> +       struct hid_device *hid;
>> +
>> +       if (WARN_ON(!ihid))
>> +               return -1;
>> +
>> +       hid = ihid->hid;
>> +       hid_destroy_device(hid);
>> +
>> +       free_irq(client->irq, ihid);
>> +
>> +       kfree(ihid);
>> +
>> +       return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int i2chid_suspend(struct device *dev)
>> +{
>> +       struct i2c_client *client = to_i2c_client(dev);
>> +       struct i2chid *ihid = i2c_get_clientdata(client);
>> +
>> +       if (device_may_wakeup(&client->dev))
>> +               enable_irq_wake(ihid->irq);
>> +
>> +       /* Save some power */
>> +       i2chid_set_power(client, I2C_HID_PWR_SLEEP);
>> +
>> +       return 0;
>> +}
>> +
>> +static int i2chid_resume(struct device *dev)
>> +{
>> +       int ret;
>> +       struct i2c_client *client = to_i2c_client(dev);
>> +
>> +       ret = i2chid_hwreset(client);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (device_may_wakeup(&client->dev))
>> +               enable_irq_wake(client->irq);
>> +
>> +       return 0;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(i2chid_pm, i2chid_suspend, i2chid_resume);
>> +
>> +static const struct i2c_device_id i2chid_id_table[] = {
>> +       { "i2chid", 0 },
>> +       { },
>> +};
>> +MODULE_DEVICE_TABLE(i2c, i2chid_id_table);
>> +
>> +
>> +static struct i2c_driver i2chid_driver = {
>> +       .driver = {
>> +               .name   = DRIVER_NAME,
>> +               .owner  = THIS_MODULE,
>> +               .pm     = &i2chid_pm,
>> +       },
>> +
>> +       .probe          = i2chid_probe,
>> +       .remove         = __devexit_p(i2chid_remove),
>> +
>> +       .id_table       = i2chid_id_table,
>> +};
>> +
>> +module_i2c_driver(i2chid_driver);
>> +
>> +MODULE_DESCRIPTION(DRIVER_DESC);
>> +MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
>> +MODULE_LICENSE("GPL");
>> diff --git a/include/linux/i2c/i2c-hid.h b/include/linux/i2c/i2c-hid.h
>> new file mode 100644
>> index 0000000..6685605
>> --- /dev/null
>> +++ b/include/linux/i2c/i2c-hid.h
>> @@ -0,0 +1,35 @@
>> +/*
>> + * HID over I2C protocol implementation
>> + *
>> + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
>> + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
>> + *
>> + * This file is subject to the terms and conditions of the GNU General Public
>> + * License.  See the file COPYING in the main directory of this archive for
>> + * more details.
>> + */
>> +
>> +#ifndef __LINUX_I2C_HID_H
>> +#define __LINUX_I2C_HID_H
>> +
>> +#include <linux/types.h>
>> +
>> +/**
>> + * struct i2chid_platform_data - used by hid over i2c implementation.
>> + * @hid_descriptor_address: i2c register where the HID descriptor is stored.
>> + *
>> + * Note that it is the responsibility for the platform driver (or the acpi 5.0
>> + * driver) to setup the irq related to the gpio in the struct i2c_board_info.
>> + * The platform driver should also setup the gpio according to the device:
>> + *
>> + * A typical example is the following:
>> + *     irq = gpio_to_irq(intr_gpio);
>> + *     hkdk4412_i2c_devs5[0].irq = irq; // store the irq in i2c_board_info
>> + *     gpio_request(intr_gpio, "elan-irq");
>> + *     s3c_gpio_setpull(intr_gpio, S3C_GPIO_PULL_UP);
>> + */
>> +struct i2chid_platform_data {
>> +       u16 hid_descriptor_address;
>> +};
>> +
>> +#endif /* __LINUX_I2C_HID_H */
>> --
>> 1.7.11.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
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
Shubhrajyoti Datta Oct. 3, 2012, 4:14 p.m. UTC | #5
On Wed, Oct 3, 2012 at 9:03 PM, Benjamin Tissoires
<benjamin.tissoires@gmail.com> wrote:
> Hi,
>
> thanks also for the review. Two in the same day! I was about to send a
> ping on that patch.... ;-)
>
> On Wed, Oct 3, 2012 at 8:05 AM, Shubhrajyoti Datta
> <omaplinuxkernel@gmail.com> wrote:
>> On Fri, Sep 14, 2012 at 7:11 PM, benjamin.tissoires
>> <benjamin.tissoires@gmail.com> wrote:
>>> From: Benjamin Tissoires <benjamin.tissoires@enac.fr>
>>>
>>> Microsoft published the protocol specification of HID over i2c:
>>> http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
>>>
>>> This patch introduces an implementation of this protocol.
>>>
>>> This implementation does not includes the ACPI part of the specification.
>>> This will come when ACPI 5.0 devices will be available.
>>>
>>> Once the ACPI part will be done, OEM will not have to declare HID over I2C
>>> devices in their platform specific driver.
>>>
>>> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
>>> ---
>>>
>>> Hi,
>>>
>>> this is finally my first implementation of HID over I2C.
>>>
>>> This has been tested on an Elan Microelectronics HID over I2C device, with
>>> a Samsung Exynos 4412 board.
>>>
>>> Any comments are welcome.
>>>
>>> Cheers,
>>> Benjamin
>>>
>>>  drivers/i2c/Kconfig         |    8 +
>>>  drivers/i2c/Makefile        |    1 +
>>>  drivers/i2c/i2c-hid.c       | 1027 +++++++++++++++++++++++++++++++++++++++++++
>>>  include/linux/i2c/i2c-hid.h |   35 ++
>>>  4 files changed, 1071 insertions(+)
>>>  create mode 100644 drivers/i2c/i2c-hid.c
>>>  create mode 100644 include/linux/i2c/i2c-hid.h
>>>
>>> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
>>
[...]
>
> same thing, will change it in v2.
>
>>
>>> +
>>> +       if (debug)
>>> +               dev_err(&client->dev, "resetting...\n");
>> this is a little uncustomary.
>>
>> May be consider  bdg
>
> Sorry for that. I don't get your point here. You don't like the whole
> "if(debug) dev_err(...)" or just the "dev_err(...)" call?
>
Apologies for the typo dev_dbg.
--
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
Dmitry Torokhov Oct. 3, 2012, 4:43 p.m. UTC | #6
Hi Benjamin,

A few random comments...

On Fri, Sep 14, 2012 at 03:41:43PM +0200, benjamin.tissoires wrote:
> From: Benjamin Tissoires <benjamin.tissoires@enac.fr>
> 
> Microsoft published the protocol specification of HID over i2c:
> http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
> 
> This patch introduces an implementation of this protocol.
> 
> This implementation does not includes the ACPI part of the specification.
> This will come when ACPI 5.0 devices will be available.
> 
> Once the ACPI part will be done, OEM will not have to declare HID over I2C
> devices in their platform specific driver.
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
> ---
> 
> Hi,
> 
> this is finally my first implementation of HID over I2C.
> 
> This has been tested on an Elan Microelectronics HID over I2C device, with
> a Samsung Exynos 4412 board.
> 
> Any comments are welcome.
> 
> Cheers,
> Benjamin
> 
>  drivers/i2c/Kconfig         |    8 +
>  drivers/i2c/Makefile        |    1 +
>  drivers/i2c/i2c-hid.c       | 1027 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/i2c/i2c-hid.h |   35 ++
>  4 files changed, 1071 insertions(+)
>  create mode 100644 drivers/i2c/i2c-hid.c
>  create mode 100644 include/linux/i2c/i2c-hid.h
> 
> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> index 5a3bb3d..5adf65a 100644
> --- a/drivers/i2c/Kconfig
> +++ b/drivers/i2c/Kconfig
> @@ -47,6 +47,14 @@ config I2C_CHARDEV
>  	  This support is also available as a module.  If so, the module 
>  	  will be called i2c-dev.
>  
> +config I2C_HID
> +	tristate "HID over I2C bus"
> +	help
> +	  Say Y here to use the HID over i2c protocol implementation.
> +
> +	  This support is also available as a module.  If so, the module
> +	  will be called i2c-hid.
> +
>  config I2C_MUX
>  	tristate "I2C bus multiplexing support"
>  	help
> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> index beee6b2..8f38116 100644
> --- a/drivers/i2c/Makefile
> +++ b/drivers/i2c/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_BOARDINFO)	+= i2c-boardinfo.o
>  obj-$(CONFIG_I2C)		+= i2c-core.o
>  obj-$(CONFIG_I2C_SMBUS)		+= i2c-smbus.o
>  obj-$(CONFIG_I2C_CHARDEV)	+= i2c-dev.o
> +obj-$(CONFIG_I2C_HID)		+= i2c-hid.o
>  obj-$(CONFIG_I2C_MUX)		+= i2c-mux.o
>  obj-y				+= algos/ busses/ muxes/
>  
> diff --git a/drivers/i2c/i2c-hid.c b/drivers/i2c/i2c-hid.c
> new file mode 100644
> index 0000000..eb17d8c
> --- /dev/null
> +++ b/drivers/i2c/i2c-hid.c
> @@ -0,0 +1,1027 @@
> +/*
> + * HID over I2C protocol implementation
> + *
> + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
> + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
> + *
> + * This code is partly based on "USB HID support for Linux":
> + *
> + *  Copyright (c) 1999 Andreas Gal
> + *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
> + *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
> + *  Copyright (c) 2007-2008 Oliver Neukum
> + *  Copyright (c) 2006-2010 Jiri Kosina
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/gpio.h>
> +#include <linux/interrupt.h>
> +#include <linux/input.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/pm.h>
> +
> +#include <linux/i2c/i2c-hid.h>
> +
> +#include <linux/hid.h>
> +#include <linux/hiddev.h>
> +#include <linux/hidraw.h>
> +
> +#define DRIVER_NAME		"i2chid"
> +#define DRIVER_DESC		"HID over I2C core driver"
> +
> +#define I2C_HID_COMMAND_TRIES	3
> +
> +/* flags */
> +#define I2C_HID_STARTED		(1 << 0)
> +#define I2C_HID_OUT_RUNNING	(1 << 1)
> +#define I2C_HID_IN_RUNNING	(1 << 2)
> +#define I2C_HID_RESET_PENDING	(1 << 3)
> +#define I2C_HID_SUSPENDED	(1 << 4)
> +
> +#define I2C_HID_PWR_ON		0x00
> +#define I2C_HID_PWR_SLEEP	0x01
> +
> +/* debug option */
> +static bool debug = false;
> +module_param(debug, bool, 0444);
> +MODULE_PARM_DESC(debug, "print a lot of debug informations");
> +
> +struct i2chid_desc {
> +	__le16 wHIDDescLength;
> +	__le16 bcdVersion;
> +	__le16 wReportDescLength;
> +	__le16 wReportDescRegister;
> +	__le16 wInputRegister;
> +	__le16 wMaxInputLength;
> +	__le16 wOutputRegister;
> +	__le16 wMaxOutputLength;
> +	__le16 wCommandRegister;
> +	__le16 wDataRegister;
> +	__le16 wVendorID;
> +	__le16 wProductID;
> +	__le16 wVersionID;
> +} __packed;
> +
> +struct i2chid_cmd {
> +	enum {
> +		/* fecth HID descriptor */
> +		HID_DESCR_CMD,
> +
> +		/* fetch report descriptors */
> +		HID_REPORT_DESCR_CMD,
> +
> +		/* commands */
> +		HID_RESET_CMD,
> +		HID_GET_REPORT_CMD,
> +		HID_SET_REPORT_CMD,
> +		HID_GET_IDLE_CMD,
> +		HID_SET_IDLE_CMD,
> +		HID_GET_PROTOCOL_CMD,
> +		HID_SET_PROTOCOL_CMD,
> +		HID_SET_POWER_CMD,
> +
> +		/* read/write data register */
> +		HID_DATA_CMD,
> +	} name;
> +	unsigned int registerIndex;
> +	__u8 opcode;
> +	unsigned int length;
> +	bool wait;
> +};
> +
> +union command {
> +	u8 data[0];
> +	struct cmd {
> +		__le16 reg;
> +		__u8 reportTypeID;
> +		__u8 opcode;
> +	} __packed c;
> +};
> +
> +#define I2C_HID_CMD(name_, opcode_) \
> +	.name = name_, .opcode = opcode_, .length = 4, \
> +	.registerIndex = offsetof(struct i2chid_desc, wCommandRegister)
> +
> +static const struct i2chid_cmd cmds[] = {
> +	{ I2C_HID_CMD(HID_RESET_CMD,		0x01), .wait = true },
> +	{ I2C_HID_CMD(HID_GET_REPORT_CMD,	0x02) },
> +	{ I2C_HID_CMD(HID_SET_REPORT_CMD,	0x03) },
> +	{ I2C_HID_CMD(HID_GET_IDLE_CMD,		0x04) },
> +	{ I2C_HID_CMD(HID_SET_IDLE_CMD,		0x05) },
> +	{ I2C_HID_CMD(HID_GET_PROTOCOL_CMD,	0x06) },
> +	{ I2C_HID_CMD(HID_SET_PROTOCOL_CMD,	0x07) },
> +	{ I2C_HID_CMD(HID_SET_POWER_CMD,	0x08) },
> +
> +	{.name = HID_DESCR_CMD,
> +	 .length = 2},
> +
> +	{.name = HID_REPORT_DESCR_CMD,
> +	 .registerIndex = offsetof(struct i2chid_desc, wReportDescRegister),
> +	 .opcode = 0x00,
> +	 .length = 2},
> +
> +	{.name = HID_DATA_CMD,
> +	 .registerIndex = offsetof(struct i2chid_desc, wDataRegister),
> +	 .opcode = 0x00,
> +	 .length = 2},
> +
> +	{}, /* terminating */
> +};
> +
> +/* The main device structure */
> +struct i2chid {

struct i2c_hid? And elsewhere s/i2chid/i2c_hid/ maybe?

> +	struct i2c_client	*client;	/* i2c client */
> +	struct hid_device	*hid;	/* pointer to corresponding HID dev */
> +	union {
> +		__u8 hdesc_buffer[sizeof(struct i2chid_desc)];
> +		struct i2chid_desc hdesc;	/* the HID Descriptor */
> +	};
> +	__le16			wHIDDescRegister; /* location of the i2c
> +						   * register of the HID
> +						   * descriptor. */
> +	unsigned int		bufsize;	/* i2c buffer size */
> +	char			*inbuf;		/* Input buffer */
> +
> +	spinlock_t		flock;		/* flags spinlock */
> +	unsigned long		flags;		/* device flags */
> +
> +	int			irq;		/* the interrupt line irq */
> +
> +	wait_queue_head_t	wait;		/* For waiting the interrupt */
> +};
> +
> +void i2chid_print_buffer(struct i2chid *ihid, u8 *buf, unsigned int n)
> +{
> +	int i;
> +	char *string = kzalloc((n*3+1)*sizeof(char), GFP_KERNEL);
> +	char tmpbuf[4] = "";
> +
> +	for (i = 0; i < n; ++i) {
> +		sprintf(tmpbuf, "%02x ", buf[i] & 0xFF);
> +		strcat(string, tmpbuf);
> +	}
> +
> +	dev_err(&ihid->client->dev, "%s\n", string);
> +	kfree(string);

Why not simply

	dev_XXX(&ihid->client->dev, "%*ph\n", n, buf);

> +}
> +
> +static int __i2chid_command(struct i2c_client *client, int command, u8 reportID,
> +		u8 reportType, u8 *args, int args_len,
> +		unsigned char *buf_recv, int data_len)
> +{
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	union command *cmd;
> +	unsigned char *rec_buf = buf_recv;
> +	int ret;
> +	int tries = I2C_HID_COMMAND_TRIES;
> +	int length = 0;
> +	struct i2c_msg msg[2];
> +	int msg_num = 0;
> +	int i;
> +	bool wait = false;
> +
> +	if (WARN_ON(!ihid))
> +		return -ENODEV;

How can this happen?

> +
> +	cmd = kmalloc(args_len + sizeof(union command), GFP_KERNEL);
> +	if (!cmd)
> +		return -ENOMEM;

Can we have several commands pending at once? If not maybe use suffcient
preallocated buffer in i2c_hid structure?

> +
> +	for (i = 0; cmds[i].length; i++) {
> +		unsigned int registerIndex;
> +
> +		if (cmds[i].name != command)
> +			continue;
> +
> +		registerIndex = cmds[i].registerIndex;
> +		cmd->data[0] = ihid->hdesc_buffer[registerIndex];
> +		cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1];
> +		cmd->c.opcode = cmds[i].opcode;
> +		length = cmds[i].length;
> +		wait = cmds[i].wait;
> +	}
> +
> +	/* special case for HID_DESCR_CMD */
> +	if (command == HID_DESCR_CMD)
> +		cmd->c.reg = ihid->wHIDDescRegister;
> +
> +	cmd->c.reportTypeID = reportID | reportType << 4;
> +
> +	memcpy(cmd->data + length, args, args_len);
> +	length += args_len;
> +
> +	if (debug) {
> +		char tmpstr[4] = "";
> +		char strbuf[50] = "";
> +		int i;
> +		for (i = 0; i < length; i++) {
> +			sprintf(tmpstr, "%02x ", cmd->data[i] & 0xFF);
> +			strcat(strbuf, tmpstr);
> +		}
> +		dev_err(&client->dev, "%s, cmd=%s\n", __func__, strbuf);

Agaion, try using '%*ph' format specifier instead.

> +	}
> +
> +	msg[0].addr = client->addr;
> +	msg[0].flags = client->flags & I2C_M_TEN;
> +	msg[0].len = length;
> +	msg[0].buf = (char *) cmd->data;
> +	msg[1].addr = client->addr;
> +	msg[1].flags = client->flags & I2C_M_TEN;
> +	msg[1].flags |= I2C_M_RD;
> +	msg[1].len = data_len;
> +	msg[1].buf = rec_buf;
> +
> +	msg_num = data_len > 0 ? 2 : 1;
> +
> +	if (wait) {
> +		spin_lock_irq(&ihid->flock);
> +		set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
> +		spin_unlock_irq(&ihid->flock);

Why do you need to take this lock? Overall I am not sure why we take
spinlockn this driver when we use atomics to manipulate flags.

> +	}
> +
> +	do {
> +		ret = i2c_transfer(client->adapter, msg, msg_num);
> +		if (ret > 0)
> +			break;
> +		tries--;
> +		dev_dbg(&client->dev, "retrying i2chid_command:%d (%d)\n",
> +			command, tries);
> +	} while (tries > 0);
> +
> +	if (wait && ret > 0) {
> +		if (debug)
> +			dev_err(&client->dev, "%s: waiting....\n", __func__);
> +		if (!wait_event_timeout(ihid->wait,
> +				!test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
> +				msecs_to_jiffies(5000)))
> +			ret = -ENODATA;
> +		if (debug)
> +			dev_err(&client->dev, "%s: finished.\n", __func__);

Why do we have error level messages with debug? I know dev_dbg is
compiled out if !DEBUG, but there must be a better way. Maybe define
i2c_hid_dbg() via dev_printk() and also check debug condition there?

> +	}
> +
> +	kfree(cmd);
> +
> +	return ret > 0 ? ret != msg_num : ret;
> +}
> +
> +static int i2chid_command(struct i2c_client *client, int command,
> +		unsigned char *buf_recv, int data_len)
> +{
> +	return __i2chid_command(client, command, 0, 0, NULL, 0,
> +				buf_recv, data_len);
> +}
> +
> +static int i2chid_get_report(struct i2c_client *client, u8 reportType,
> +		u32 reportID, unsigned char *buf_recv, int data_len)
> +{
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	u8 args[6];
> +	int ret;
> +	int args_len = 0;
> +	u16 readRegister = ihid->hdesc.wDataRegister;
> +
> +	if (debug)
> +		dev_err(&client->dev, "%s\n", __func__);
> +
> +	if (reportID >= 15) {
> +		args[args_len++] = (reportID >> 0) & 0xFF;
> +		args[args_len++] = (reportID >> 8) & 0xFF;
> +		args[args_len++] = (reportID >> 16) & 0xFF;
> +		args[args_len++] = (reportID >> 24) & 0xFF;
> +		reportID = 0x0F;
> +	}
> +
> +	args[args_len++] = readRegister & 0xff;
> +	args[args_len++] = (readRegister >> 8) & 0xff;
> +
> +	ret = __i2chid_command(client, HID_GET_REPORT_CMD, reportID & 0x0F,
> +		reportType, args, args_len, buf_recv, data_len);
> +	if (ret) {
> +		dev_err(&client->dev, "HID_GET_REPORT_CMD Fail\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int i2chid_set_report(struct i2c_client *client, u8 reportType,
> +		u32 reportID, unsigned char *buf, int data_len)
> +{
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	u8 *args;
> +	int ret;
> +	u16 dataRegister = ihid->hdesc.wDataRegister;
> +	int args_len =  (reportID >= 15 ? 4 : 0) +
> +			2 /* dataRegister */ +
> +			2 /* size */ +
> +			data_len;
> +	int index = 0;
> +
> +	if (debug)
> +		dev_err(&client->dev, "%s\n", __func__);
> +
> +	args = kmalloc(args_len, GFP_KERNEL);
> +
> +	if (reportID >= 15) {
> +		args[index++] = (reportID >> 0) & 0xFF;
> +		args[index++] = (reportID >> 8) & 0xFF;
> +		args[index++] = (reportID >> 16) & 0xFF;
> +		args[index++] = (reportID >> 24) & 0xFF;
> +		reportID = 0x0F;
> +	}
> +
> +	args[index++] = dataRegister & 0xff;
> +	args[index++] = (dataRegister >> 8) & 0xff;
> +
> +	args[index++] = data_len & 0xff;
> +	args[index++] = (data_len >> 8) & 0xff;
> +
> +	memcpy(&args[index], buf, data_len);
> +
> +	ret = __i2chid_command(client, HID_SET_REPORT_CMD, reportID & 0x0F,
> +		reportType, args, args_len, NULL, 0);
> +	if (ret) {
> +		dev_err(&client->dev, "HID_SET_REPORT_CMD Fail\n");
> +		return -EINVAL;
> +	}
> +
> +	kfree(args);
> +	return data_len;
> +}
> +
> +static int i2chid_set_power(struct i2c_client *client, int power_state)
> +{
> +	int ret;
> +
> +	if (debug)
> +		dev_err(&client->dev, "%s\n", __func__);
> +
> +	ret = __i2chid_command(client, HID_SET_POWER_CMD, power_state & 0x01,
> +		0, NULL, 0, NULL, 0);
> +	if (ret) {
> +		dev_err(&client->dev, "HID_SET_POWER_CMD Fail\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int i2chid_hwreset(struct i2c_client *client)
> +{
> +	uint8_t buf_recv[79];
> +	int ret;
> +
> +	if (debug)
> +		dev_err(&client->dev, "%s\n", __func__);
> +
> +	ret = i2chid_set_power(client, I2C_HID_PWR_ON);
> +	if (ret)
> +		return -EINVAL;
> +
> +	if (debug)
> +		dev_err(&client->dev, "resetting...\n");
> +
> +	ret = i2chid_command(client, HID_RESET_CMD, buf_recv, 0);
> +	if (ret) {
> +		dev_err(&client->dev, "HID_RESET_CMD Fail\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = i2c_master_recv(client, buf_recv, 2);
> +	if (ret != 2 || (buf_recv[0] != 0 && buf_recv[1] != 0)) {
> +		dev_err(&client->dev,
> +			"HID_RESET_CMD Failed: got %02x %02x, size=%d\n",
> +			buf_recv[0], buf_recv[1], ret);
> +
> +		i2chid_set_power(client, I2C_HID_PWR_SLEEP);
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static bool i2chid_get_input(struct i2chid *ihid)
> +{
> +	int ret;
> +	int size = ihid->hdesc.wMaxInputLength;
> +
> +	ret = i2c_master_recv(ihid->client, ihid->inbuf, size);
> +	if (ret != size) {
> +		dev_err(&ihid->client->dev, "%s: got %d data instead of %d\n",
> +			__func__, ret, size);
> +		return false;
> +	}
> +
> +	if (debug)
> +		i2chid_print_buffer(ihid, ihid->inbuf, size);
> +
> +	ret = hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2,
> +			size - 2, 1);
> +	if (ret)
> +		return false;
> +
> +	return true;
> +}
> +
> +static irqreturn_t i2chid_irq(int irq, void *dev_id)
> +{
> +	struct i2chid *ihid = dev_id;
> +	int ready;
> +
> +	if (!ihid)
> +		return IRQ_HANDLED;
> +
> +	spin_lock_irq(&ihid->flock);
> +	if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags)) {
> +		spin_unlock_irq(&ihid->flock);
> +
> +		wake_up(&ihid->wait);
> +		return IRQ_HANDLED;
> +	}
> +
> +	ready = test_bit(I2C_HID_STARTED, &ihid->flags);
> +	spin_unlock_irq(&ihid->flock);

Why this lock? What does it protect? You just dropped it and make a
decision on something that you retrieved while holding the lock. But now
that you dropped it the condition could change by the time you get to
test it, so why bother?

> +
> +	if (ready)
> +		i2chid_get_input(ihid);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int i2chid_get_report_length(struct hid_report *report)
> +{
> +	return ((report->size - 1) >> 3) + 1 +
> +		report->device->report_enum[report->type].numbered + 2;
> +}
> +
> +void i2chid_init_report(struct hid_report *report)
> +{
> +	struct hid_device *hid = report->device;
> +	struct i2c_client *client = hid->driver_data;
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	unsigned int size, ret_size;
> +
> +	size = i2chid_get_report_length(report);
> +	i2chid_get_report(client,
> +			report->type == HID_FEATURE_REPORT ? 0x03 : 0x01,
> +			report->id, ihid->inbuf, size);
> +
> +	if (debug)
> +		i2chid_print_buffer(ihid, ihid->inbuf, size);
> +
> +	ret_size = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
> +
> +	if (ret_size != size) {
> +		dev_err(&client->dev, "error in %s size:%d / ret_size:%d\n",
> +			__func__, size, ret_size);
> +		return;
> +	}
> +
> +	/* hid->driver_lock is held as we are in probe function,
> +	 * we just need to setup the input fields, so using
> +	 * hid_report_raw_event is safe. */
> +	hid_report_raw_event(hid, report->type, ihid->inbuf + 2, size - 2, 1);
> +}
> +
> +/*
> + * Initialize all reports
> + */
> +void i2chid_init_reports(struct hid_device *hid)
> +{
> +	struct hid_report *report;
> +
> +	list_for_each_entry(report,
> +		&hid->report_enum[HID_INPUT_REPORT].report_list, list)
> +		i2chid_init_report(report);
> +
> +	list_for_each_entry(report,
> +		&hid->report_enum[HID_FEATURE_REPORT].report_list, list)
> +		i2chid_init_report(report);
> +}
> +
> +/*
> + * Traverse the supplied list of reports and find the longest
> + */
> +static void i2chid_find_max_report(struct hid_device *hid, unsigned int type,
> +		unsigned int *max)
> +{
> +	struct hid_report *report;
> +	unsigned int size;
> +
> +	/* We should not rely on wMaxInputLength, as some devices may set it to
> +	 * a wrong length. */
> +	list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
> +		size = i2chid_get_report_length(report);
> +		if (*max < size)
> +			*max = size;
> +	}
> +}
> +
> +static int i2chid_alloc_buffers(struct i2chid *ihid)
> +{
> +	ihid->inbuf = kzalloc(ihid->bufsize, GFP_KERNEL);
> +
> +	if (!ihid->inbuf)
> +		return -1;
> +
> +	return 0;
> +}
> +
> +static void i2chid_free_buffers(struct i2chid *ihid)
> +{
> +	kfree(ihid->inbuf);
> +}
> +
> +static int i2chid_get_raw_report(struct hid_device *hid,
> +		unsigned char report_number, __u8 *buf, size_t count,
> +		unsigned char report_type)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	int ret;
> +
> +	if (count > ihid->bufsize)
> +		count = ihid->bufsize;
> +
> +	ret = i2chid_get_report(client,
> +			report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
> +			report_number, ihid->inbuf, count);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	count = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
> +
> +	memcpy(buf, ihid->inbuf + 2, count);
> +
> +	return count;
> +}
> +
> +static int i2chid_output_raw_report(struct hid_device *hid, __u8 *buf,
> +		size_t count, unsigned char report_type)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	int ret;
> +	int report_id = buf[0];
> +
> +	ret = i2chid_set_report(client,
> +				report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
> +				report_id, buf, count);
> +
> +	return ret;
> +
> +}
> +
> +static int i2chid_fetch_hid_descriptor(struct i2chid *ihid)
> +{
> +	struct i2c_client *client = ihid->client;
> +	struct i2chid_desc *hdesc = &ihid->hdesc;
> +	unsigned int dsize = 0;
> +	int ret;
> +
> +	/* Fetch the length of HID description, retrieve the 4 first bytes:
> +	 * bytes 0-1 -> length
> +	 * bytes 2-3 -> bcdVersion (has to be 1.00) */
> +	ret = i2chid_command(client, HID_DESCR_CMD, ihid->hdesc_buffer, 4);
> +
> +	if (debug)
> +		dev_err(&client->dev,
> +			"%s, ihid->hdesc_buffer: %02x %02x %02x %02x\n",

%*ph again.

> +			__func__,
> +			ihid->hdesc_buffer[0],
> +			ihid->hdesc_buffer[1],
> +			ihid->hdesc_buffer[2],
> +			ihid->hdesc_buffer[3]);
> +
> +	if (ret) {
> +		dev_err(&client->dev, "HID_DESCR_LENGTH_CMD Fail (ret=%d)\n",
> +			ret);
> +		return -EINVAL;
> +	}
> +
> +	dsize = le16_to_cpu(hdesc->wHIDDescLength);
> +	if (!dsize || dsize > HID_MAX_DESCRIPTOR_SIZE) {
> +		dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
> +			dsize);
> +		return -EINVAL;
> +	}
> +
> +	/* check bcdVersion == 1.0 */
> +	if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
> +		dev_err(&client->dev,
> +			"unexpected HID descriptor bcdVersion (0x%04x)\n",
> +			le16_to_cpu(hdesc->bcdVersion));
> +		return -EINVAL;
> +	}
> +
> +	if (debug)
> +		dev_err(&client->dev, "Fetching the HID descriptor\n");
> +
> +	ret = i2chid_command(client, HID_DESCR_CMD, ihid->hdesc_buffer, dsize);
> +	if (ret) {
> +		dev_err(&client->dev, "HID_DESCR_CMD Fail\n");
> +		return -EINVAL;
> +	}
> +
> +	if (debug)
> +		i2chid_print_buffer(ihid, ihid->hdesc_buffer, dsize);
> +
> +	return 0;
> +}
> +
> +static int i2chid_parse(struct hid_device *hid)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	struct i2chid_desc *hdesc = &ihid->hdesc;
> +	unsigned int rsize = 0;
> +	char *rdesc;
> +	int ret;
> +	int tries = 3;
> +
> +	if (debug)
> +		dev_err(&client->dev, "entering %s\n", __func__);
> +
> +	rsize = le16_to_cpu(hdesc->wReportDescLength);
> +	if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
> +		dbg_hid("weird size of report descriptor (%u)\n", rsize);
> +		return -EINVAL;
> +	}
> +
> +	do {
> +		ret = i2chid_hwreset(client);
> +		if (ret)
> +			msleep(1000);
> +	} while (tries-- > 0 && ret);
> +
> +	if (ret)
> +		return ret;
> +
> +	rdesc = kmalloc(rsize, GFP_KERNEL);
> +
> +	if (!rdesc) {
> +		dbg_hid("couldn't allocate rdesc memory\n");
> +		return -ENOMEM;
> +	}
> +
> +	if (debug)
> +		dev_err(&client->dev, "asking HID report descriptor\n");
> +
> +	ret = i2chid_command(client, HID_REPORT_DESCR_CMD, rdesc, rsize);
> +	if (ret) {
> +		hid_err(hid, "reading report descriptor failed\n");
> +		kfree(rdesc);
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	if (debug)
> +		i2chid_print_buffer(ihid, rdesc, rsize);
> +
> +	ret = hid_parse_report(hid, rdesc, rsize);
> +	kfree(rdesc);
> +	if (ret) {
> +		dbg_hid("parsing report descriptor failed\n");
> +		goto err;
> +	}
> +
> +	return 0;
> +err:
> +	return ret;
> +}
> +
> +static int i2chid_start(struct hid_device *hid)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	int ret;
> +
> +	ihid->bufsize = HID_MIN_BUFFER_SIZE;
> +	i2chid_find_max_report(hid, HID_INPUT_REPORT, &ihid->bufsize);
> +	i2chid_find_max_report(hid, HID_OUTPUT_REPORT, &ihid->bufsize);
> +	i2chid_find_max_report(hid, HID_FEATURE_REPORT, &ihid->bufsize);
> +
> +	if (ihid->bufsize > HID_MAX_BUFFER_SIZE)
> +		ihid->bufsize = HID_MAX_BUFFER_SIZE;
> +
> +	if (i2chid_alloc_buffers(ihid)) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
> +		i2chid_init_reports(hid);
> +
> +	return 0;
> +
> +fail:
> +	i2chid_free_buffers(ihid);
> +	return ret;
> +}
> +
> +static void i2chid_stop(struct hid_device *hid)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +
> +	if (WARN_ON(!ihid))
> +		return;

How?

> +
> +	hid->claimed = 0;

Should it be here and not in core?

> +
> +	i2chid_free_buffers(ihid);
> +}
> +
> +static int i2chid_open(struct hid_device *hid)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	int ret;
> +
> +	if (!hid->open++) {
> +		ret = i2chid_set_power(client, I2C_HID_PWR_ON);
> +		if (ret) {
> +			hid->open--;
> +			return -EIO;
> +		}
> +		spin_lock_irq(&ihid->flock);
> +		set_bit(I2C_HID_STARTED, &ihid->flags);
> +		spin_unlock_irq(&ihid->flock);
> +	}
> +	return 0;
> +}
> +
> +static void i2chid_close(struct hid_device *hid)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +
> +	/* protecting hid->open to make sure we don't restart
> +	 * data acquistion due to a resumption we no longer
> +	 * care about
> +	 */
> +	if (!--hid->open) {
> +		spin_lock_irq(&ihid->flock);
> +		clear_bit(I2C_HID_STARTED, &ihid->flags);
> +		spin_unlock_irq(&ihid->flock);
> +
> +		/* Save some power */
> +		i2chid_set_power(client, I2C_HID_PWR_SLEEP);
> +	}
> +}
> +
> +static int i2chid_power(struct hid_device *hid, int lvl)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	int r = 0;
> +
> +	if (debug)
> +		dev_err(&client->dev, "%s lvl:%d\n", __func__, lvl);
> +
> +	switch (lvl) {
> +	case PM_HINT_FULLON:
> +		r = i2chid_set_power(client, I2C_HID_PWR_ON);
> +		break;
> +	case PM_HINT_NORMAL:
> +		r = i2chid_set_power(client, I2C_HID_PWR_SLEEP);
> +		break;
> +	}
> +	return r;
> +}
> +
> +static int i2chid_hidinput_input_event(struct input_dev *dev, unsigned int type,
> +		unsigned int code, int value)
> +{
> +	struct hid_device *hid = input_get_drvdata(dev);
> +	struct hid_field *field;
> +	int offset;
> +
> +	if (type == EV_FF)
> +		return input_ff_event(dev, type, code, value);
> +
> +	if (type != EV_LED)
> +		return -1;
> +
> +	offset = hidinput_find_field(hid, type, code, &field);
> +
> +	if (offset == -1) {
> +		hid_warn(dev, "event field not found\n");
> +		return -1;
> +	}
> +
> +	hid_set_field(field, offset, value);
> +
> +	return 0;
> +}
> +
> +static struct hid_ll_driver i2c_hid_driver = {
> +	.parse = i2chid_parse,
> +	.start = i2chid_start,
> +	.stop = i2chid_stop,
> +	.open = i2chid_open,
> +	.close = i2chid_close,
> +	.power = i2chid_power,
> +	.hidinput_input_event = i2chid_hidinput_input_event,
> +};
> +
> +static int i2chid_init_irq(struct i2c_client *client)
> +{
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	int rc;
> +
> +	dev_dbg(&client->dev, "Requesting IRQ: %d\n",
> +		client->irq);
> +
> +	rc = request_threaded_irq(client->irq, NULL, i2chid_irq,
> +			IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> +			DRIVER_NAME, ihid);
> +	if (rc < 0) {
> +		dev_dbg(&client->dev,
> +			"Could not register for %s interrupt, irq = %d,"
> +			" rc = %d\n",
> +		DRIVER_NAME, client->irq, rc);
> +
> +		return rc;
> +	}
> +
> +	return client->irq;
> +}
> +
> +static int __devinit i2chid_probe(struct i2c_client *client,
> +		const struct i2c_device_id *dev_id)
> +{
> +	int ret;
> +	struct i2chid *ihid;
> +	struct hid_device *hid;
> +	__u16 hidRegister;
> +	unsigned char tmpstr[11];
> +	struct i2chid_platform_data *platform_data = client->dev.platform_data;
> +
> +	dbg_hid("HID probe called for i2c %d\n", client->addr);
> +
> +	if (!platform_data) {
> +		dev_err(&client->dev, "HID register address not provided\n");
> +		return -EINVAL;
> +	}
> +
> +	if (client->irq < 1) {o

Maybe !client->irq?

> +		dev_err(&client->dev,
> +			"HID over i2c has not been provided an Int IRQ\n");
> +		return -EINVAL;
> +	}
> +
> +	ihid = kzalloc(sizeof(struct i2chid), GFP_KERNEL);
> +	if (!ihid)
> +		return -ENOMEM;
> +
> +	i2c_set_clientdata(client, ihid);
> +
> +	ihid->client = client;
> +	ihid->flags = 0;
> +
> +	hidRegister = platform_data->hid_descriptor_address;
> +	ihid->wHIDDescRegister = cpu_to_le16(hidRegister);
> +
> +	init_waitqueue_head(&ihid->wait);
> +	spin_lock_init(&ihid->flock);
> +
> +	ret = i2chid_fetch_hid_descriptor(ihid);
> +	if (ret < 0)
> +		goto err;
> +
> +	ihid->irq = i2chid_init_irq(client);
> +	if (ihid->irq < 0)
> +		goto err;
> +
> +	hid = hid_allocate_device();
> +	if (IS_ERR(hid)) {
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	ihid->hid = hid;
> +
> +	hid->driver_data = client;
> +	hid->ll_driver = &i2c_hid_driver;
> +	hid->hid_get_raw_report = i2chid_get_raw_report;
> +	hid->hid_output_raw_report = i2chid_output_raw_report;
> +	hid->ff_init = hid_pidff_init;
> +#ifdef CONFIG_USB_HIDDEV
> +	hid->hiddev_connect = hiddev_connect;
> +	hid->hiddev_disconnect = hiddev_disconnect;
> +	hid->hiddev_hid_event = hiddev_hid_event;
> +	hid->hiddev_report_event = hiddev_report_event;
> +#endif
> +	hid->dev.parent = &client->dev;
> +	hid->bus = BUS_I2C;
> +	hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
> +	hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
> +	hid->product = le16_to_cpu(ihid->hdesc.wProductID);
> +
> +	snprintf(tmpstr, sizeof(tmpstr), " %04X:%04X",
> +		 hid->vendor, hid->product);
> +	strlcpy(hid->name, client->name, sizeof(hid->name));
> +	strlcat(hid->name, tmpstr, sizeof(hid->name));
> +
> +	ret = hid_add_device(hid);
> +	if (ret) {
> +		if (ret != -ENODEV)
> +			hid_err(client, "can't add hid device: %d\n", ret);
> +		goto err_mem_free;
> +	}
> +
> +	return 0;
> +
> +err_mem_free:
> +	hid_destroy_device(hid);
> +
> +err:
> +	if (ihid->irq >= 0)
> +		free_irq(ihid->irq, ihid);
> +
> +	kfree(ihid);
> +	return ret;
> +}
> +
> +static int __devexit i2chid_remove(struct i2c_client *client)
> +{
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	struct hid_device *hid;
> +
> +	if (WARN_ON(!ihid))
> +		return -1;
> +
> +	hid = ihid->hid;
> +	hid_destroy_device(hid);
> +
> +	free_irq(client->irq, ihid);
> +
> +	kfree(ihid);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int i2chid_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +
> +	if (device_may_wakeup(&client->dev))
> +		enable_irq_wake(ihid->irq);
> +
> +	/* Save some power */
> +	i2chid_set_power(client, I2C_HID_PWR_SLEEP);
> +
> +	return 0;
> +}
> +
> +static int i2chid_resume(struct device *dev)
> +{
> +	int ret;
> +	struct i2c_client *client = to_i2c_client(dev);
> +
> +	ret = i2chid_hwreset(client);
> +	if (ret)
> +		return ret;
> +
> +	if (device_may_wakeup(&client->dev))
> +		enable_irq_wake(client->irq);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(i2chid_pm, i2chid_suspend, i2chid_resume);
> +
> +static const struct i2c_device_id i2chid_id_table[] = {
> +	{ "i2chid", 0 },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(i2c, i2chid_id_table);
> +
> +
> +static struct i2c_driver i2chid_driver = {
> +	.driver = {
> +		.name	= DRIVER_NAME,
> +		.owner	= THIS_MODULE,
> +		.pm	= &i2chid_pm,
> +	},
> +
> +	.probe		= i2chid_probe,
> +	.remove		= __devexit_p(i2chid_remove),
> +
> +	.id_table	= i2chid_id_table,
> +};
> +
> +module_i2c_driver(i2chid_driver);
> +
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/i2c/i2c-hid.h b/include/linux/i2c/i2c-hid.h
> new file mode 100644
> index 0000000..6685605
> --- /dev/null
> +++ b/include/linux/i2c/i2c-hid.h
> @@ -0,0 +1,35 @@
> +/*
> + * HID over I2C protocol implementation
> + *
> + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
> + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#ifndef __LINUX_I2C_HID_H
> +#define __LINUX_I2C_HID_H
> +
> +#include <linux/types.h>
> +
> +/**
> + * struct i2chid_platform_data - used by hid over i2c implementation.
> + * @hid_descriptor_address: i2c register where the HID descriptor is stored.
> + *
> + * Note that it is the responsibility for the platform driver (or the acpi 5.0
> + * driver) to setup the irq related to the gpio in the struct i2c_board_info.
> + * The platform driver should also setup the gpio according to the device:
> + *
> + * A typical example is the following:
> + *	irq = gpio_to_irq(intr_gpio);
> + *	hkdk4412_i2c_devs5[0].irq = irq; // store the irq in i2c_board_info
> + *	gpio_request(intr_gpio, "elan-irq");
> + *	s3c_gpio_setpull(intr_gpio, S3C_GPIO_PULL_UP);
> + */
> +struct i2chid_platform_data {
> +	u16 hid_descriptor_address;
> +};
> +
> +#endif /* __LINUX_I2C_HID_H */
> -- 
> 1.7.11.4
> 

Thanks.
Benjamin Tissoires Oct. 4, 2012, 9:16 a.m. UTC | #7
Hi Dmitry,

thanks for the review.

On Wed, Oct 3, 2012 at 6:43 PM, Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> Hi Benjamin,
>
> A few random comments...
>
> On Fri, Sep 14, 2012 at 03:41:43PM +0200, benjamin.tissoires wrote:
>> From: Benjamin Tissoires <benjamin.tissoires@enac.fr>
>>
>> Microsoft published the protocol specification of HID over i2c:
>> http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
>>
>> This patch introduces an implementation of this protocol.
>>
>> This implementation does not includes the ACPI part of the specification.
>> This will come when ACPI 5.0 devices will be available.
>>
>> Once the ACPI part will be done, OEM will not have to declare HID over I2C
>> devices in their platform specific driver.
>>
>> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
>> ---
>>
>> Hi,
>>
>> this is finally my first implementation of HID over I2C.
>>
>> This has been tested on an Elan Microelectronics HID over I2C device, with
>> a Samsung Exynos 4412 board.
>>
>> Any comments are welcome.
>>
>> Cheers,
>> Benjamin
>>
>>  drivers/i2c/Kconfig         |    8 +
>>  drivers/i2c/Makefile        |    1 +
>>  drivers/i2c/i2c-hid.c       | 1027 +++++++++++++++++++++++++++++++++++++++++++
>>  include/linux/i2c/i2c-hid.h |   35 ++
>>  4 files changed, 1071 insertions(+)
>>  create mode 100644 drivers/i2c/i2c-hid.c
>>  create mode 100644 include/linux/i2c/i2c-hid.h
>>
>> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
>> index 5a3bb3d..5adf65a 100644
>> --- a/drivers/i2c/Kconfig
>> +++ b/drivers/i2c/Kconfig
>> @@ -47,6 +47,14 @@ config I2C_CHARDEV
>>         This support is also available as a module.  If so, the module
>>         will be called i2c-dev.
>>
>> +config I2C_HID
>> +     tristate "HID over I2C bus"
>> +     help
>> +       Say Y here to use the HID over i2c protocol implementation.
>> +
>> +       This support is also available as a module.  If so, the module
>> +       will be called i2c-hid.
>> +
>>  config I2C_MUX
>>       tristate "I2C bus multiplexing support"
>>       help
>> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
>> index beee6b2..8f38116 100644
>> --- a/drivers/i2c/Makefile
>> +++ b/drivers/i2c/Makefile
>> @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_BOARDINFO)   += i2c-boardinfo.o
>>  obj-$(CONFIG_I2C)            += i2c-core.o
>>  obj-$(CONFIG_I2C_SMBUS)              += i2c-smbus.o
>>  obj-$(CONFIG_I2C_CHARDEV)    += i2c-dev.o
>> +obj-$(CONFIG_I2C_HID)                += i2c-hid.o
>>  obj-$(CONFIG_I2C_MUX)                += i2c-mux.o
>>  obj-y                                += algos/ busses/ muxes/
>>
>> diff --git a/drivers/i2c/i2c-hid.c b/drivers/i2c/i2c-hid.c
>> new file mode 100644
>> index 0000000..eb17d8c
>> --- /dev/null
>> +++ b/drivers/i2c/i2c-hid.c
>> @@ -0,0 +1,1027 @@
>> +/*
>> + * HID over I2C protocol implementation
>> + *
>> + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
>> + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
>> + *
>> + * This code is partly based on "USB HID support for Linux":
>> + *
>> + *  Copyright (c) 1999 Andreas Gal
>> + *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
>> + *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
>> + *  Copyright (c) 2007-2008 Oliver Neukum
>> + *  Copyright (c) 2006-2010 Jiri Kosina
>> + *
>> + * This file is subject to the terms and conditions of the GNU General Public
>> + * License.  See the file COPYING in the main directory of this archive for
>> + * more details.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/i2c.h>
>> +#include <linux/irq.h>
>> +#include <linux/gpio.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/input.h>
>> +#include <linux/delay.h>
>> +#include <linux/slab.h>
>> +#include <linux/pm.h>
>> +
>> +#include <linux/i2c/i2c-hid.h>
>> +
>> +#include <linux/hid.h>
>> +#include <linux/hiddev.h>
>> +#include <linux/hidraw.h>
>> +
>> +#define DRIVER_NAME          "i2chid"
>> +#define DRIVER_DESC          "HID over I2C core driver"
>> +
>> +#define I2C_HID_COMMAND_TRIES        3
>> +
>> +/* flags */
>> +#define I2C_HID_STARTED              (1 << 0)
>> +#define I2C_HID_OUT_RUNNING  (1 << 1)
>> +#define I2C_HID_IN_RUNNING   (1 << 2)
>> +#define I2C_HID_RESET_PENDING        (1 << 3)
>> +#define I2C_HID_SUSPENDED    (1 << 4)
>> +
>> +#define I2C_HID_PWR_ON               0x00
>> +#define I2C_HID_PWR_SLEEP    0x01
>> +
>> +/* debug option */
>> +static bool debug = false;
>> +module_param(debug, bool, 0444);
>> +MODULE_PARM_DESC(debug, "print a lot of debug informations");
>> +
>> +struct i2chid_desc {
>> +     __le16 wHIDDescLength;
>> +     __le16 bcdVersion;
>> +     __le16 wReportDescLength;
>> +     __le16 wReportDescRegister;
>> +     __le16 wInputRegister;
>> +     __le16 wMaxInputLength;
>> +     __le16 wOutputRegister;
>> +     __le16 wMaxOutputLength;
>> +     __le16 wCommandRegister;
>> +     __le16 wDataRegister;
>> +     __le16 wVendorID;
>> +     __le16 wProductID;
>> +     __le16 wVersionID;
>> +} __packed;
>> +
>> +struct i2chid_cmd {
>> +     enum {
>> +             /* fecth HID descriptor */
>> +             HID_DESCR_CMD,
>> +
>> +             /* fetch report descriptors */
>> +             HID_REPORT_DESCR_CMD,
>> +
>> +             /* commands */
>> +             HID_RESET_CMD,
>> +             HID_GET_REPORT_CMD,
>> +             HID_SET_REPORT_CMD,
>> +             HID_GET_IDLE_CMD,
>> +             HID_SET_IDLE_CMD,
>> +             HID_GET_PROTOCOL_CMD,
>> +             HID_SET_PROTOCOL_CMD,
>> +             HID_SET_POWER_CMD,
>> +
>> +             /* read/write data register */
>> +             HID_DATA_CMD,
>> +     } name;
>> +     unsigned int registerIndex;
>> +     __u8 opcode;
>> +     unsigned int length;
>> +     bool wait;
>> +};
>> +
>> +union command {
>> +     u8 data[0];
>> +     struct cmd {
>> +             __le16 reg;
>> +             __u8 reportTypeID;
>> +             __u8 opcode;
>> +     } __packed c;
>> +};
>> +
>> +#define I2C_HID_CMD(name_, opcode_) \
>> +     .name = name_, .opcode = opcode_, .length = 4, \
>> +     .registerIndex = offsetof(struct i2chid_desc, wCommandRegister)
>> +
>> +static const struct i2chid_cmd cmds[] = {
>> +     { I2C_HID_CMD(HID_RESET_CMD,            0x01), .wait = true },
>> +     { I2C_HID_CMD(HID_GET_REPORT_CMD,       0x02) },
>> +     { I2C_HID_CMD(HID_SET_REPORT_CMD,       0x03) },
>> +     { I2C_HID_CMD(HID_GET_IDLE_CMD,         0x04) },
>> +     { I2C_HID_CMD(HID_SET_IDLE_CMD,         0x05) },
>> +     { I2C_HID_CMD(HID_GET_PROTOCOL_CMD,     0x06) },
>> +     { I2C_HID_CMD(HID_SET_PROTOCOL_CMD,     0x07) },
>> +     { I2C_HID_CMD(HID_SET_POWER_CMD,        0x08) },
>> +
>> +     {.name = HID_DESCR_CMD,
>> +      .length = 2},
>> +
>> +     {.name = HID_REPORT_DESCR_CMD,
>> +      .registerIndex = offsetof(struct i2chid_desc, wReportDescRegister),
>> +      .opcode = 0x00,
>> +      .length = 2},
>> +
>> +     {.name = HID_DATA_CMD,
>> +      .registerIndex = offsetof(struct i2chid_desc, wDataRegister),
>> +      .opcode = 0x00,
>> +      .length = 2},
>> +
>> +     {}, /* terminating */
>> +};
>> +
>> +/* The main device structure */
>> +struct i2chid {
>
> struct i2c_hid? And elsewhere s/i2chid/i2c_hid/ maybe?

Yes, maybe. It's more easier to read. I chose to name it without the
underscore to be closer to the usbhid driver, but it's harder to read.

>
>> +     struct i2c_client       *client;        /* i2c client */
>> +     struct hid_device       *hid;   /* pointer to corresponding HID dev */
>> +     union {
>> +             __u8 hdesc_buffer[sizeof(struct i2chid_desc)];
>> +             struct i2chid_desc hdesc;       /* the HID Descriptor */
>> +     };
>> +     __le16                  wHIDDescRegister; /* location of the i2c
>> +                                                * register of the HID
>> +                                                * descriptor. */
>> +     unsigned int            bufsize;        /* i2c buffer size */
>> +     char                    *inbuf;         /* Input buffer */
>> +
>> +     spinlock_t              flock;          /* flags spinlock */
>> +     unsigned long           flags;          /* device flags */
>> +
>> +     int                     irq;            /* the interrupt line irq */
>> +
>> +     wait_queue_head_t       wait;           /* For waiting the interrupt */
>> +};
>> +
>> +void i2chid_print_buffer(struct i2chid *ihid, u8 *buf, unsigned int n)
>> +{
>> +     int i;
>> +     char *string = kzalloc((n*3+1)*sizeof(char), GFP_KERNEL);
>> +     char tmpbuf[4] = "";
>> +
>> +     for (i = 0; i < n; ++i) {
>> +             sprintf(tmpbuf, "%02x ", buf[i] & 0xFF);
>> +             strcat(string, tmpbuf);
>> +     }
>> +
>> +     dev_err(&ihid->client->dev, "%s\n", string);
>> +     kfree(string);
>
> Why not simply
>
>         dev_XXX(&ihid->client->dev, "%*ph\n", n, buf);

Indeed, it's far more simple.... sorry.

>
>> +}
>> +
>> +static int __i2chid_command(struct i2c_client *client, int command, u8 reportID,
>> +             u8 reportType, u8 *args, int args_len,
>> +             unsigned char *buf_recv, int data_len)
>> +{
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     union command *cmd;
>> +     unsigned char *rec_buf = buf_recv;
>> +     int ret;
>> +     int tries = I2C_HID_COMMAND_TRIES;
>> +     int length = 0;
>> +     struct i2c_msg msg[2];
>> +     int msg_num = 0;
>> +     int i;
>> +     bool wait = false;
>> +
>> +     if (WARN_ON(!ihid))
>> +             return -ENODEV;
>
> How can this happen?

It shouldn't. An excess of checking while writing the first version,
and now it's useless.

>
>> +
>> +     cmd = kmalloc(args_len + sizeof(union command), GFP_KERNEL);
>> +     if (!cmd)
>> +             return -ENOMEM;
>
> Can we have several commands pending at once? If not maybe use suffcient
> preallocated buffer in i2c_hid structure?

At first, I allocated it statically. However, due to some outputs
commands, it's harder to get the exact maximum of this command.
The set_report has the following command: fill the set report + fill
the data register (with headers and size) with the report.

By writing it, I realize that I should be able to get a maximum buffer length...

>
>> +
>> +     for (i = 0; cmds[i].length; i++) {
>> +             unsigned int registerIndex;
>> +
>> +             if (cmds[i].name != command)
>> +                     continue;
>> +
>> +             registerIndex = cmds[i].registerIndex;
>> +             cmd->data[0] = ihid->hdesc_buffer[registerIndex];
>> +             cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1];
>> +             cmd->c.opcode = cmds[i].opcode;
>> +             length = cmds[i].length;
>> +             wait = cmds[i].wait;
>> +     }
>> +
>> +     /* special case for HID_DESCR_CMD */
>> +     if (command == HID_DESCR_CMD)
>> +             cmd->c.reg = ihid->wHIDDescRegister;
>> +
>> +     cmd->c.reportTypeID = reportID | reportType << 4;
>> +
>> +     memcpy(cmd->data + length, args, args_len);
>> +     length += args_len;
>> +
>> +     if (debug) {
>> +             char tmpstr[4] = "";
>> +             char strbuf[50] = "";
>> +             int i;
>> +             for (i = 0; i < length; i++) {
>> +                     sprintf(tmpstr, "%02x ", cmd->data[i] & 0xFF);
>> +                     strcat(strbuf, tmpstr);
>> +             }
>> +             dev_err(&client->dev, "%s, cmd=%s\n", __func__, strbuf);
>
> Agaion, try using '%*ph' format specifier instead.

ok

>
>> +     }
>> +
>> +     msg[0].addr = client->addr;
>> +     msg[0].flags = client->flags & I2C_M_TEN;
>> +     msg[0].len = length;
>> +     msg[0].buf = (char *) cmd->data;
>> +     msg[1].addr = client->addr;
>> +     msg[1].flags = client->flags & I2C_M_TEN;
>> +     msg[1].flags |= I2C_M_RD;
>> +     msg[1].len = data_len;
>> +     msg[1].buf = rec_buf;
>> +
>> +     msg_num = data_len > 0 ? 2 : 1;
>> +
>> +     if (wait) {
>> +             spin_lock_irq(&ihid->flock);
>> +             set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
>> +             spin_unlock_irq(&ihid->flock);
>
> Why do you need to take this lock? Overall I am not sure why we take
> spinlockn this driver when we use atomics to manipulate flags.

Right. The spinlocks were there to protect against the reset command
that must be synchronous.
By re-reading the code, I think I can handle it without the need of spinlocks.
So I'll drop them in v2.

>
>> +     }
>> +
>> +     do {
>> +             ret = i2c_transfer(client->adapter, msg, msg_num);
>> +             if (ret > 0)
>> +                     break;
>> +             tries--;
>> +             dev_dbg(&client->dev, "retrying i2chid_command:%d (%d)\n",
>> +                     command, tries);
>> +     } while (tries > 0);
>> +
>> +     if (wait && ret > 0) {
>> +             if (debug)
>> +                     dev_err(&client->dev, "%s: waiting....\n", __func__);
>> +             if (!wait_event_timeout(ihid->wait,
>> +                             !test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
>> +                             msecs_to_jiffies(5000)))
>> +                     ret = -ENODATA;
>> +             if (debug)
>> +                     dev_err(&client->dev, "%s: finished.\n", __func__);
>
> Why do we have error level messages with debug? I know dev_dbg is
> compiled out if !DEBUG, but there must be a better way. Maybe define
> i2c_hid_dbg() via dev_printk() and also check debug condition there?
>

ooops....
yep, the i2c_hid_dbg is far better. Will do it.

>> +     }
>> +
>> +     kfree(cmd);
>> +
>> +     return ret > 0 ? ret != msg_num : ret;
>> +}
>> +
>> +static int i2chid_command(struct i2c_client *client, int command,
>> +             unsigned char *buf_recv, int data_len)
>> +{
>> +     return __i2chid_command(client, command, 0, 0, NULL, 0,
>> +                             buf_recv, data_len);
>> +}
>> +
>> +static int i2chid_get_report(struct i2c_client *client, u8 reportType,
>> +             u32 reportID, unsigned char *buf_recv, int data_len)
>> +{
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     u8 args[6];
>> +     int ret;
>> +     int args_len = 0;
>> +     u16 readRegister = ihid->hdesc.wDataRegister;
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "%s\n", __func__);
>> +
>> +     if (reportID >= 15) {
>> +             args[args_len++] = (reportID >> 0) & 0xFF;
>> +             args[args_len++] = (reportID >> 8) & 0xFF;
>> +             args[args_len++] = (reportID >> 16) & 0xFF;
>> +             args[args_len++] = (reportID >> 24) & 0xFF;
>> +             reportID = 0x0F;
>> +     }
>> +
>> +     args[args_len++] = readRegister & 0xff;
>> +     args[args_len++] = (readRegister >> 8) & 0xff;
>> +
>> +     ret = __i2chid_command(client, HID_GET_REPORT_CMD, reportID & 0x0F,
>> +             reportType, args, args_len, buf_recv, data_len);
>> +     if (ret) {
>> +             dev_err(&client->dev, "HID_GET_REPORT_CMD Fail\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int i2chid_set_report(struct i2c_client *client, u8 reportType,
>> +             u32 reportID, unsigned char *buf, int data_len)
>> +{
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     u8 *args;
>> +     int ret;
>> +     u16 dataRegister = ihid->hdesc.wDataRegister;
>> +     int args_len =  (reportID >= 15 ? 4 : 0) +
>> +                     2 /* dataRegister */ +
>> +                     2 /* size */ +
>> +                     data_len;
>> +     int index = 0;
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "%s\n", __func__);
>> +
>> +     args = kmalloc(args_len, GFP_KERNEL);
>> +
>> +     if (reportID >= 15) {
>> +             args[index++] = (reportID >> 0) & 0xFF;
>> +             args[index++] = (reportID >> 8) & 0xFF;
>> +             args[index++] = (reportID >> 16) & 0xFF;
>> +             args[index++] = (reportID >> 24) & 0xFF;
>> +             reportID = 0x0F;
>> +     }
>> +
>> +     args[index++] = dataRegister & 0xff;
>> +     args[index++] = (dataRegister >> 8) & 0xff;
>> +
>> +     args[index++] = data_len & 0xff;
>> +     args[index++] = (data_len >> 8) & 0xff;
>> +
>> +     memcpy(&args[index], buf, data_len);
>> +
>> +     ret = __i2chid_command(client, HID_SET_REPORT_CMD, reportID & 0x0F,
>> +             reportType, args, args_len, NULL, 0);
>> +     if (ret) {
>> +             dev_err(&client->dev, "HID_SET_REPORT_CMD Fail\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     kfree(args);
>> +     return data_len;
>> +}
>> +
>> +static int i2chid_set_power(struct i2c_client *client, int power_state)
>> +{
>> +     int ret;
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "%s\n", __func__);
>> +
>> +     ret = __i2chid_command(client, HID_SET_POWER_CMD, power_state & 0x01,
>> +             0, NULL, 0, NULL, 0);
>> +     if (ret) {
>> +             dev_err(&client->dev, "HID_SET_POWER_CMD Fail\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int i2chid_hwreset(struct i2c_client *client)
>> +{
>> +     uint8_t buf_recv[79];
>> +     int ret;
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "%s\n", __func__);
>> +
>> +     ret = i2chid_set_power(client, I2C_HID_PWR_ON);
>> +     if (ret)
>> +             return -EINVAL;
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "resetting...\n");
>> +
>> +     ret = i2chid_command(client, HID_RESET_CMD, buf_recv, 0);
>> +     if (ret) {
>> +             dev_err(&client->dev, "HID_RESET_CMD Fail\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     ret = i2c_master_recv(client, buf_recv, 2);
>> +     if (ret != 2 || (buf_recv[0] != 0 && buf_recv[1] != 0)) {
>> +             dev_err(&client->dev,
>> +                     "HID_RESET_CMD Failed: got %02x %02x, size=%d\n",
>> +                     buf_recv[0], buf_recv[1], ret);
>> +
>> +             i2chid_set_power(client, I2C_HID_PWR_SLEEP);
>> +             return -EINVAL;
>> +     }
>> +     return 0;
>> +}
>> +
>> +static bool i2chid_get_input(struct i2chid *ihid)
>> +{
>> +     int ret;
>> +     int size = ihid->hdesc.wMaxInputLength;
>> +
>> +     ret = i2c_master_recv(ihid->client, ihid->inbuf, size);
>> +     if (ret != size) {
>> +             dev_err(&ihid->client->dev, "%s: got %d data instead of %d\n",
>> +                     __func__, ret, size);
>> +             return false;
>> +     }
>> +
>> +     if (debug)
>> +             i2chid_print_buffer(ihid, ihid->inbuf, size);
>> +
>> +     ret = hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2,
>> +                     size - 2, 1);
>> +     if (ret)
>> +             return false;
>> +
>> +     return true;
>> +}
>> +
>> +static irqreturn_t i2chid_irq(int irq, void *dev_id)
>> +{
>> +     struct i2chid *ihid = dev_id;
>> +     int ready;
>> +
>> +     if (!ihid)
>> +             return IRQ_HANDLED;
>> +
>> +     spin_lock_irq(&ihid->flock);
>> +     if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags)) {
>> +             spin_unlock_irq(&ihid->flock);
>> +
>> +             wake_up(&ihid->wait);
>> +             return IRQ_HANDLED;
>> +     }
>> +
>> +     ready = test_bit(I2C_HID_STARTED, &ihid->flags);
>> +     spin_unlock_irq(&ihid->flock);
>
> Why this lock? What does it protect? You just dropped it and make a
> decision on something that you retrieved while holding the lock. But now
> that you dropped it the condition could change by the time you get to
> test it, so why bother?

As mentioned above, the lock was more against the RESET_PENDING. I
consider that once the device is started, we can handle the input
reports.
Anyway, as mentioned above, I'll try to remove them in v2, it will be
more simple.

>
>> +
>> +     if (ready)
>> +             i2chid_get_input(ihid);
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static int i2chid_get_report_length(struct hid_report *report)
>> +{
>> +     return ((report->size - 1) >> 3) + 1 +
>> +             report->device->report_enum[report->type].numbered + 2;
>> +}
>> +
>> +void i2chid_init_report(struct hid_report *report)
>> +{
>> +     struct hid_device *hid = report->device;
>> +     struct i2c_client *client = hid->driver_data;
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     unsigned int size, ret_size;
>> +
>> +     size = i2chid_get_report_length(report);
>> +     i2chid_get_report(client,
>> +                     report->type == HID_FEATURE_REPORT ? 0x03 : 0x01,
>> +                     report->id, ihid->inbuf, size);
>> +
>> +     if (debug)
>> +             i2chid_print_buffer(ihid, ihid->inbuf, size);
>> +
>> +     ret_size = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
>> +
>> +     if (ret_size != size) {
>> +             dev_err(&client->dev, "error in %s size:%d / ret_size:%d\n",
>> +                     __func__, size, ret_size);
>> +             return;
>> +     }
>> +
>> +     /* hid->driver_lock is held as we are in probe function,
>> +      * we just need to setup the input fields, so using
>> +      * hid_report_raw_event is safe. */
>> +     hid_report_raw_event(hid, report->type, ihid->inbuf + 2, size - 2, 1);
>> +}
>> +
>> +/*
>> + * Initialize all reports
>> + */
>> +void i2chid_init_reports(struct hid_device *hid)
>> +{
>> +     struct hid_report *report;
>> +
>> +     list_for_each_entry(report,
>> +             &hid->report_enum[HID_INPUT_REPORT].report_list, list)
>> +             i2chid_init_report(report);
>> +
>> +     list_for_each_entry(report,
>> +             &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
>> +             i2chid_init_report(report);
>> +}
>> +
>> +/*
>> + * Traverse the supplied list of reports and find the longest
>> + */
>> +static void i2chid_find_max_report(struct hid_device *hid, unsigned int type,
>> +             unsigned int *max)
>> +{
>> +     struct hid_report *report;
>> +     unsigned int size;
>> +
>> +     /* We should not rely on wMaxInputLength, as some devices may set it to
>> +      * a wrong length. */
>> +     list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
>> +             size = i2chid_get_report_length(report);
>> +             if (*max < size)
>> +                     *max = size;
>> +     }
>> +}
>> +
>> +static int i2chid_alloc_buffers(struct i2chid *ihid)
>> +{
>> +     ihid->inbuf = kzalloc(ihid->bufsize, GFP_KERNEL);
>> +
>> +     if (!ihid->inbuf)
>> +             return -1;
>> +
>> +     return 0;
>> +}
>> +
>> +static void i2chid_free_buffers(struct i2chid *ihid)
>> +{
>> +     kfree(ihid->inbuf);
>> +}
>> +
>> +static int i2chid_get_raw_report(struct hid_device *hid,
>> +             unsigned char report_number, __u8 *buf, size_t count,
>> +             unsigned char report_type)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     int ret;
>> +
>> +     if (count > ihid->bufsize)
>> +             count = ihid->bufsize;
>> +
>> +     ret = i2chid_get_report(client,
>> +                     report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
>> +                     report_number, ihid->inbuf, count);
>> +
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     count = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
>> +
>> +     memcpy(buf, ihid->inbuf + 2, count);
>> +
>> +     return count;
>> +}
>> +
>> +static int i2chid_output_raw_report(struct hid_device *hid, __u8 *buf,
>> +             size_t count, unsigned char report_type)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     int ret;
>> +     int report_id = buf[0];
>> +
>> +     ret = i2chid_set_report(client,
>> +                             report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
>> +                             report_id, buf, count);
>> +
>> +     return ret;
>> +
>> +}
>> +
>> +static int i2chid_fetch_hid_descriptor(struct i2chid *ihid)
>> +{
>> +     struct i2c_client *client = ihid->client;
>> +     struct i2chid_desc *hdesc = &ihid->hdesc;
>> +     unsigned int dsize = 0;
>> +     int ret;
>> +
>> +     /* Fetch the length of HID description, retrieve the 4 first bytes:
>> +      * bytes 0-1 -> length
>> +      * bytes 2-3 -> bcdVersion (has to be 1.00) */
>> +     ret = i2chid_command(client, HID_DESCR_CMD, ihid->hdesc_buffer, 4);
>> +
>> +     if (debug)
>> +             dev_err(&client->dev,
>> +                     "%s, ihid->hdesc_buffer: %02x %02x %02x %02x\n",
>
> %*ph again.

Ok.

>
>> +                     __func__,
>> +                     ihid->hdesc_buffer[0],
>> +                     ihid->hdesc_buffer[1],
>> +                     ihid->hdesc_buffer[2],
>> +                     ihid->hdesc_buffer[3]);
>> +
>> +     if (ret) {
>> +             dev_err(&client->dev, "HID_DESCR_LENGTH_CMD Fail (ret=%d)\n",
>> +                     ret);
>> +             return -EINVAL;
>> +     }
>> +
>> +     dsize = le16_to_cpu(hdesc->wHIDDescLength);
>> +     if (!dsize || dsize > HID_MAX_DESCRIPTOR_SIZE) {
>> +             dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
>> +                     dsize);
>> +             return -EINVAL;
>> +     }
>> +
>> +     /* check bcdVersion == 1.0 */
>> +     if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
>> +             dev_err(&client->dev,
>> +                     "unexpected HID descriptor bcdVersion (0x%04x)\n",
>> +                     le16_to_cpu(hdesc->bcdVersion));
>> +             return -EINVAL;
>> +     }
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "Fetching the HID descriptor\n");
>> +
>> +     ret = i2chid_command(client, HID_DESCR_CMD, ihid->hdesc_buffer, dsize);
>> +     if (ret) {
>> +             dev_err(&client->dev, "HID_DESCR_CMD Fail\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     if (debug)
>> +             i2chid_print_buffer(ihid, ihid->hdesc_buffer, dsize);
>> +
>> +     return 0;
>> +}
>> +
>> +static int i2chid_parse(struct hid_device *hid)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     struct i2chid_desc *hdesc = &ihid->hdesc;
>> +     unsigned int rsize = 0;
>> +     char *rdesc;
>> +     int ret;
>> +     int tries = 3;
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "entering %s\n", __func__);
>> +
>> +     rsize = le16_to_cpu(hdesc->wReportDescLength);
>> +     if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
>> +             dbg_hid("weird size of report descriptor (%u)\n", rsize);
>> +             return -EINVAL;
>> +     }
>> +
>> +     do {
>> +             ret = i2chid_hwreset(client);
>> +             if (ret)
>> +                     msleep(1000);
>> +     } while (tries-- > 0 && ret);
>> +
>> +     if (ret)
>> +             return ret;
>> +
>> +     rdesc = kmalloc(rsize, GFP_KERNEL);
>> +
>> +     if (!rdesc) {
>> +             dbg_hid("couldn't allocate rdesc memory\n");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "asking HID report descriptor\n");
>> +
>> +     ret = i2chid_command(client, HID_REPORT_DESCR_CMD, rdesc, rsize);
>> +     if (ret) {
>> +             hid_err(hid, "reading report descriptor failed\n");
>> +             kfree(rdesc);
>> +             ret = -ENOMEM;
>> +             goto err;
>> +     }
>> +
>> +     if (debug)
>> +             i2chid_print_buffer(ihid, rdesc, rsize);
>> +
>> +     ret = hid_parse_report(hid, rdesc, rsize);
>> +     kfree(rdesc);
>> +     if (ret) {
>> +             dbg_hid("parsing report descriptor failed\n");
>> +             goto err;
>> +     }
>> +
>> +     return 0;
>> +err:
>> +     return ret;
>> +}
>> +
>> +static int i2chid_start(struct hid_device *hid)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     int ret;
>> +
>> +     ihid->bufsize = HID_MIN_BUFFER_SIZE;
>> +     i2chid_find_max_report(hid, HID_INPUT_REPORT, &ihid->bufsize);
>> +     i2chid_find_max_report(hid, HID_OUTPUT_REPORT, &ihid->bufsize);
>> +     i2chid_find_max_report(hid, HID_FEATURE_REPORT, &ihid->bufsize);
>> +
>> +     if (ihid->bufsize > HID_MAX_BUFFER_SIZE)
>> +             ihid->bufsize = HID_MAX_BUFFER_SIZE;
>> +
>> +     if (i2chid_alloc_buffers(ihid)) {
>> +             ret = -ENOMEM;
>> +             goto fail;
>> +     }
>> +
>> +     if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
>> +             i2chid_init_reports(hid);
>> +
>> +     return 0;
>> +
>> +fail:
>> +     i2chid_free_buffers(ihid);
>> +     return ret;
>> +}
>> +
>> +static void i2chid_stop(struct hid_device *hid)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +
>> +     if (WARN_ON(!ihid))
>> +             return;
>
> How?

will drop it.

>
>> +
>> +     hid->claimed = 0;
>
> Should it be here and not in core?

This is a line that was copied/pasted from usbhid. I'll check how can
I do that without interfering with core.

>
>> +
>> +     i2chid_free_buffers(ihid);
>> +}
>> +
>> +static int i2chid_open(struct hid_device *hid)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     int ret;
>> +
>> +     if (!hid->open++) {
>> +             ret = i2chid_set_power(client, I2C_HID_PWR_ON);
>> +             if (ret) {
>> +                     hid->open--;
>> +                     return -EIO;
>> +             }
>> +             spin_lock_irq(&ihid->flock);
>> +             set_bit(I2C_HID_STARTED, &ihid->flags);
>> +             spin_unlock_irq(&ihid->flock);
>> +     }
>> +     return 0;
>> +}
>> +
>> +static void i2chid_close(struct hid_device *hid)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +
>> +     /* protecting hid->open to make sure we don't restart
>> +      * data acquistion due to a resumption we no longer
>> +      * care about
>> +      */
>> +     if (!--hid->open) {
>> +             spin_lock_irq(&ihid->flock);
>> +             clear_bit(I2C_HID_STARTED, &ihid->flags);
>> +             spin_unlock_irq(&ihid->flock);
>> +
>> +             /* Save some power */
>> +             i2chid_set_power(client, I2C_HID_PWR_SLEEP);
>> +     }
>> +}
>> +
>> +static int i2chid_power(struct hid_device *hid, int lvl)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     int r = 0;
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "%s lvl:%d\n", __func__, lvl);
>> +
>> +     switch (lvl) {
>> +     case PM_HINT_FULLON:
>> +             r = i2chid_set_power(client, I2C_HID_PWR_ON);
>> +             break;
>> +     case PM_HINT_NORMAL:
>> +             r = i2chid_set_power(client, I2C_HID_PWR_SLEEP);
>> +             break;
>> +     }
>> +     return r;
>> +}
>> +
>> +static int i2chid_hidinput_input_event(struct input_dev *dev, unsigned int type,
>> +             unsigned int code, int value)
>> +{
>> +     struct hid_device *hid = input_get_drvdata(dev);
>> +     struct hid_field *field;
>> +     int offset;
>> +
>> +     if (type == EV_FF)
>> +             return input_ff_event(dev, type, code, value);
>> +
>> +     if (type != EV_LED)
>> +             return -1;
>> +
>> +     offset = hidinput_find_field(hid, type, code, &field);
>> +
>> +     if (offset == -1) {
>> +             hid_warn(dev, "event field not found\n");
>> +             return -1;
>> +     }
>> +
>> +     hid_set_field(field, offset, value);
>> +
>> +     return 0;
>> +}
>> +
>> +static struct hid_ll_driver i2c_hid_driver = {
>> +     .parse = i2chid_parse,
>> +     .start = i2chid_start,
>> +     .stop = i2chid_stop,
>> +     .open = i2chid_open,
>> +     .close = i2chid_close,
>> +     .power = i2chid_power,
>> +     .hidinput_input_event = i2chid_hidinput_input_event,
>> +};
>> +
>> +static int i2chid_init_irq(struct i2c_client *client)
>> +{
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     int rc;
>> +
>> +     dev_dbg(&client->dev, "Requesting IRQ: %d\n",
>> +             client->irq);
>> +
>> +     rc = request_threaded_irq(client->irq, NULL, i2chid_irq,
>> +                     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
>> +                     DRIVER_NAME, ihid);
>> +     if (rc < 0) {
>> +             dev_dbg(&client->dev,
>> +                     "Could not register for %s interrupt, irq = %d,"
>> +                     " rc = %d\n",
>> +             DRIVER_NAME, client->irq, rc);
>> +
>> +             return rc;
>> +     }
>> +
>> +     return client->irq;
>> +}
>> +
>> +static int __devinit i2chid_probe(struct i2c_client *client,
>> +             const struct i2c_device_id *dev_id)
>> +{
>> +     int ret;
>> +     struct i2chid *ihid;
>> +     struct hid_device *hid;
>> +     __u16 hidRegister;
>> +     unsigned char tmpstr[11];
>> +     struct i2chid_platform_data *platform_data = client->dev.platform_data;
>> +
>> +     dbg_hid("HID probe called for i2c %d\n", client->addr);
>> +
>> +     if (!platform_data) {
>> +             dev_err(&client->dev, "HID register address not provided\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     if (client->irq < 1) {o
>
> Maybe !client->irq?

I thought that irqs should be positive or null numbers. This can be
easily changed.

>
>> +             dev_err(&client->dev,
>> +                     "HID over i2c has not been provided an Int IRQ\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     ihid = kzalloc(sizeof(struct i2chid), GFP_KERNEL);
>> +     if (!ihid)
>> +             return -ENOMEM;
>> +
>> +     i2c_set_clientdata(client, ihid);
>> +
>> +     ihid->client = client;
>> +     ihid->flags = 0;
>> +
>> +     hidRegister = platform_data->hid_descriptor_address;
>> +     ihid->wHIDDescRegister = cpu_to_le16(hidRegister);
>> +
>> +     init_waitqueue_head(&ihid->wait);
>> +     spin_lock_init(&ihid->flock);
>> +
>> +     ret = i2chid_fetch_hid_descriptor(ihid);
>> +     if (ret < 0)
>> +             goto err;
>> +
>> +     ihid->irq = i2chid_init_irq(client);
>> +     if (ihid->irq < 0)
>> +             goto err;
>> +
>> +     hid = hid_allocate_device();
>> +     if (IS_ERR(hid)) {
>> +             ret = -ENOMEM;
>> +             goto err;
>> +     }
>> +
>> +     ihid->hid = hid;
>> +
>> +     hid->driver_data = client;
>> +     hid->ll_driver = &i2c_hid_driver;
>> +     hid->hid_get_raw_report = i2chid_get_raw_report;
>> +     hid->hid_output_raw_report = i2chid_output_raw_report;
>> +     hid->ff_init = hid_pidff_init;
>> +#ifdef CONFIG_USB_HIDDEV
>> +     hid->hiddev_connect = hiddev_connect;
>> +     hid->hiddev_disconnect = hiddev_disconnect;
>> +     hid->hiddev_hid_event = hiddev_hid_event;
>> +     hid->hiddev_report_event = hiddev_report_event;
>> +#endif
>> +     hid->dev.parent = &client->dev;
>> +     hid->bus = BUS_I2C;
>> +     hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
>> +     hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
>> +     hid->product = le16_to_cpu(ihid->hdesc.wProductID);
>> +
>> +     snprintf(tmpstr, sizeof(tmpstr), " %04X:%04X",
>> +              hid->vendor, hid->product);
>> +     strlcpy(hid->name, client->name, sizeof(hid->name));
>> +     strlcat(hid->name, tmpstr, sizeof(hid->name));
>> +
>> +     ret = hid_add_device(hid);
>> +     if (ret) {
>> +             if (ret != -ENODEV)
>> +                     hid_err(client, "can't add hid device: %d\n", ret);
>> +             goto err_mem_free;
>> +     }
>> +
>> +     return 0;
>> +
>> +err_mem_free:
>> +     hid_destroy_device(hid);
>> +
>> +err:
>> +     if (ihid->irq >= 0)
>> +             free_irq(ihid->irq, ihid);
>> +
>> +     kfree(ihid);
>> +     return ret;
>> +}
>> +
>> +static int __devexit i2chid_remove(struct i2c_client *client)
>> +{
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     struct hid_device *hid;
>> +
>> +     if (WARN_ON(!ihid))
>> +             return -1;
>> +
>> +     hid = ihid->hid;
>> +     hid_destroy_device(hid);
>> +
>> +     free_irq(client->irq, ihid);
>> +
>> +     kfree(ihid);
>> +
>> +     return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int i2chid_suspend(struct device *dev)
>> +{
>> +     struct i2c_client *client = to_i2c_client(dev);
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +
>> +     if (device_may_wakeup(&client->dev))
>> +             enable_irq_wake(ihid->irq);
>> +
>> +     /* Save some power */
>> +     i2chid_set_power(client, I2C_HID_PWR_SLEEP);
>> +
>> +     return 0;
>> +}
>> +
>> +static int i2chid_resume(struct device *dev)
>> +{
>> +     int ret;
>> +     struct i2c_client *client = to_i2c_client(dev);
>> +
>> +     ret = i2chid_hwreset(client);
>> +     if (ret)
>> +             return ret;
>> +
>> +     if (device_may_wakeup(&client->dev))
>> +             enable_irq_wake(client->irq);
>> +
>> +     return 0;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(i2chid_pm, i2chid_suspend, i2chid_resume);
>> +
>> +static const struct i2c_device_id i2chid_id_table[] = {
>> +     { "i2chid", 0 },
>> +     { },
>> +};
>> +MODULE_DEVICE_TABLE(i2c, i2chid_id_table);
>> +
>> +
>> +static struct i2c_driver i2chid_driver = {
>> +     .driver = {
>> +             .name   = DRIVER_NAME,
>> +             .owner  = THIS_MODULE,
>> +             .pm     = &i2chid_pm,
>> +     },
>> +
>> +     .probe          = i2chid_probe,
>> +     .remove         = __devexit_p(i2chid_remove),
>> +
>> +     .id_table       = i2chid_id_table,
>> +};
>> +
>> +module_i2c_driver(i2chid_driver);
>> +
>> +MODULE_DESCRIPTION(DRIVER_DESC);
>> +MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
>> +MODULE_LICENSE("GPL");
>> diff --git a/include/linux/i2c/i2c-hid.h b/include/linux/i2c/i2c-hid.h
>> new file mode 100644
>> index 0000000..6685605
>> --- /dev/null
>> +++ b/include/linux/i2c/i2c-hid.h
>> @@ -0,0 +1,35 @@
>> +/*
>> + * HID over I2C protocol implementation
>> + *
>> + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
>> + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
>> + *
>> + * This file is subject to the terms and conditions of the GNU General Public
>> + * License.  See the file COPYING in the main directory of this archive for
>> + * more details.
>> + */
>> +
>> +#ifndef __LINUX_I2C_HID_H
>> +#define __LINUX_I2C_HID_H
>> +
>> +#include <linux/types.h>
>> +
>> +/**
>> + * struct i2chid_platform_data - used by hid over i2c implementation.
>> + * @hid_descriptor_address: i2c register where the HID descriptor is stored.
>> + *
>> + * Note that it is the responsibility for the platform driver (or the acpi 5.0
>> + * driver) to setup the irq related to the gpio in the struct i2c_board_info.
>> + * The platform driver should also setup the gpio according to the device:
>> + *
>> + * A typical example is the following:
>> + *   irq = gpio_to_irq(intr_gpio);
>> + *   hkdk4412_i2c_devs5[0].irq = irq; // store the irq in i2c_board_info
>> + *   gpio_request(intr_gpio, "elan-irq");
>> + *   s3c_gpio_setpull(intr_gpio, S3C_GPIO_PULL_UP);
>> + */
>> +struct i2chid_platform_data {
>> +     u16 hid_descriptor_address;
>> +};
>> +
>> +#endif /* __LINUX_I2C_HID_H */
>> --
>> 1.7.11.4
>>
>
> Thanks.

Thanks to you Dmitry.
I will try to post the v2 soon.

Cheers,
Benjamin

>
> --
> Dmitry
--
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
Jiri Kosina Oct. 4, 2012, 9:48 a.m. UTC | #8
On Thu, 4 Oct 2012, Benjamin Tissoires wrote:

> >> +
> >> +     hid->claimed = 0;
> >
> > Should it be here and not in core?
> 
> This is a line that was copied/pasted from usbhid. I'll check how can
> I do that without interfering with core.


Well, we are calling ll_driver->stop at multiple places, so having this 
reset in the actual ll driver callback seems to be cleaner.

(if I understand the concern here correctly).
Jean Delvare Oct. 6, 2012, 7:54 p.m. UTC | #9
On Wed, 3 Oct 2012 09:43:12 -0700, Dmitry Torokhov wrote:
> On Fri, Sep 14, 2012 at 03:41:43PM +0200, benjamin.tissoires wrote:
> > +	}
> > +
> > +	do {
> > +		ret = i2c_transfer(client->adapter, msg, msg_num);
> > +		if (ret > 0)
> > +			break;
> > +		tries--;
> > +		dev_dbg(&client->dev, "retrying i2chid_command:%d (%d)\n",
> > +			command, tries);
> > +	} while (tries > 0);
> > +
> > +	if (wait && ret > 0) {
> > +		if (debug)
> > +			dev_err(&client->dev, "%s: waiting....\n", __func__);
> > +		if (!wait_event_timeout(ihid->wait,
> > +				!test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
> > +				msecs_to_jiffies(5000)))
> > +			ret = -ENODATA;
> > +		if (debug)
> > +			dev_err(&client->dev, "%s: finished.\n", __func__);
> 
> Why do we have error level messages with debug? I know dev_dbg is
> compiled out if !DEBUG, but there must be a better way. Maybe define
> i2c_hid_dbg() via dev_printk() and also check debug condition there?

dev_dbg() is compiled out if !DEBUG only if CONFIG_DYNAMIC_DEBUG isn't
set. These days I think every developer want to enable this option.
Jean Delvare Oct. 6, 2012, 8:04 p.m. UTC | #10
Hi Benjamin,

On Fri, 14 Sep 2012 15:41:43 +0200, benjamin.tissoires wrote:
> From: Benjamin Tissoires <benjamin.tissoires@enac.fr>
> 
> Microsoft published the protocol specification of HID over i2c:
> http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
> 
> This patch introduces an implementation of this protocol.
> 
> This implementation does not includes the ACPI part of the specification.
> This will come when ACPI 5.0 devices will be available.
> 
> Once the ACPI part will be done, OEM will not have to declare HID over I2C
> devices in their platform specific driver.
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
> ---
> 
> Hi,
> 
> this is finally my first implementation of HID over I2C.
> 
> This has been tested on an Elan Microelectronics HID over I2C device, with
> a Samsung Exynos 4412 board.
> 
> Any comments are welcome.
> 
> Cheers,
> Benjamin
> 
>  drivers/i2c/Kconfig         |    8 +
>  drivers/i2c/Makefile        |    1 +
>  drivers/i2c/i2c-hid.c       | 1027 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/i2c/i2c-hid.h |   35 ++
>  4 files changed, 1071 insertions(+)
>  create mode 100644 drivers/i2c/i2c-hid.c
>  create mode 100644 include/linux/i2c/i2c-hid.h

Looks like the wrong place for this driver. HID-over-USB support lives
under drivers/hid, so your driver should go there as well. Not only
this will be more consistent, but it also makes more sense: your driver
is a user, not an implementer, of the I2C layer, so it doesn't belong
to drivers/i2c.

Also, you need to sort out dependencies. Your causes a link failure here:

ERROR: "hiddev_report_event" [drivers/i2c/i2c-hid.ko] undefined!
ERROR: "hiddev_disconnect" [drivers/i2c/i2c-hid.ko] undefined!
ERROR: "hiddev_connect" [drivers/i2c/i2c-hid.ko] undefined!
ERROR: "hid_pidff_init" [drivers/i2c/i2c-hid.ko] undefined!
make[1]: *** [__modpost] Erreur 1
make: *** [modules] Erreur 2

This is because these functions aren't exported and I tried to build
i2c-hid as a module.BTW I see that these functions are part of the
usbhid driver, which looks seriously wrong. If these functions are
transport layer-independent, they should be moved to the hid-code or
some sub-module. One should be able to enable HID-over-I2C without
HID-over-USB.
Stéphane Chatty Oct. 6, 2012, 8:30 p.m. UTC | #11
Hi Jean

(I cc Marcel Holtmann because BT is involved too)

Le 6 oct. 2012 à 22:04, Jean Delvare a écrit :

> Hi Benjamin,
> 
> On Fri, 14 Sep 2012 15:41:43 +0200, benjamin.tissoires wrote:
>> From: Benjamin Tissoires <benjamin.tissoires@enac.fr>
>> 
>> Microsoft published the protocol specification of HID over i2c:
>> http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
>> 
>> This patch introduces an implementation of this protocol.
>> 
>> This implementation does not includes the ACPI part of the specification.
>> This will come when ACPI 5.0 devices will be available.
>> 
>> Once the ACPI part will be done, OEM will not have to declare HID over I2C
>> devices in their platform specific driver.
>> 
>> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
>> ---
>> 
>> Hi,
>> 
>> this is finally my first implementation of HID over I2C.
>> 
>> This has been tested on an Elan Microelectronics HID over I2C device, with
>> a Samsung Exynos 4412 board.
>> 
>> Any comments are welcome.
>> 
>> Cheers,
>> Benjamin
>> 
>> drivers/i2c/Kconfig         |    8 +
>> drivers/i2c/Makefile        |    1 +
>> drivers/i2c/i2c-hid.c       | 1027 +++++++++++++++++++++++++++++++++++++++++++
>> include/linux/i2c/i2c-hid.h |   35 ++
>> 4 files changed, 1071 insertions(+)
>> create mode 100644 drivers/i2c/i2c-hid.c
>> create mode 100644 include/linux/i2c/i2c-hid.h
> 
> Looks like the wrong place for this driver. HID-over-USB support lives
> under drivers/hid, so your driver should go there as well. Not only
> this will be more consistent, but it also makes more sense: your driver
> is a user, not an implementer, of the I2C layer, so it doesn't belong
> to drivers/i2c.

This is a question I asked a few months back, but apparently not all points of views had been expressed at the time. Currently, HID-over-USB lives in drivers/hid, but HID-over-BT lives in drivers/bluetooth. When I asked, Jiri explained that he maintained HID-over-USB and Marcel maintained HID-over-BT, which explained the choices made. Let's try to summarize what we know now:

The question is what drives the choice of where to put HID-over-XXX, among the following
 1- who the maintainer is. Here, Benjamin will probably maintain this so it does not help.
 2- dependencies. HID-over-XXX depends on HID as much as it depends on XXX, so it does not help.
 3- data flow. Indeed, HID is a client of HID-over-XXX which is a client of XXX. Are there other parts of the kernel where this drives the choice of where YYY-over-XXX lives?

Jiri, Marcel, Greg, others, any opinions?

> 
> Also, you need to sort out dependencies. Your causes a link failure here:
> 
> ERROR: "hiddev_report_event" [drivers/i2c/i2c-hid.ko] undefined!
> ERROR: "hiddev_disconnect" [drivers/i2c/i2c-hid.ko] undefined!
> ERROR: "hiddev_connect" [drivers/i2c/i2c-hid.ko] undefined!
> ERROR: "hid_pidff_init" [drivers/i2c/i2c-hid.ko] undefined!
> make[1]: *** [__modpost] Erreur 1
> make: *** [modules] Erreur 2
> 
> This is because these functions aren't exported and I tried to build
> i2c-hid as a module.BTW I see that these functions are part of the
> usbhid driver, which looks seriously wrong. If these functions are
> transport layer-independent, they should be moved to the hid-code or
> some sub-module. One should be able to enable HID-over-I2C without
> HID-over-USB.
> 
> -- 
> Jean Delvare

Cheers,

St.

PS: Benjamin is leaving ENAC. He'll probably keep maintaining HID-over-I2C, and we'll probably share the maintenance of hid-multitouch as well as other input-related projects we have not published yet.

--
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
Jean Delvare Oct. 6, 2012, 9:11 p.m. UTC | #12
On Sat, 6 Oct 2012 22:30:00 +0200, Stéphane Chatty wrote:
> Le 6 oct. 2012 à 22:04, Jean Delvare a écrit :
> > Looks like the wrong place for this driver. HID-over-USB support lives
> > under drivers/hid, so your driver should go there as well. Not only
> > this will be more consistent, but it also makes more sense: your driver
> > is a user, not an implementer, of the I2C layer, so it doesn't belong
> > to drivers/i2c.
> 
> This is a question I asked a few months back, but apparently not all points of views had been expressed at the time. Currently, HID-over-USB lives in drivers/hid, but HID-over-BT lives in drivers/bluetooth. When I asked, Jiri explained that he maintained HID-over-USB and Marcel maintained HID-over-BT, which explained the choices made. Let's try to summarize what we know now:
> 
> The question is what drives the choice of where to put HID-over-XXX, among the following
>  1- who the maintainer is. Here, Benjamin will probably maintain this so it does not help.
>  2- dependencies. HID-over-XXX depends on HID as much as it depends on XXX, so it does not help.
>  3- data flow. Indeed, HID is a client of HID-over-XXX which is a client of XXX. Are there other parts of the kernel where this drives the choice of where YYY-over-XXX lives?
> 
> Jiri, Marcel, Greg, others, any opinions?

My vote is a clear 3. It took me a few years to kick all users (as
opposed to implementers) of i2c from drivers/i2c and finding them a
proper home, I'm not going to accept new intruders. Grouping drivers
according to what they implement makes it a lot easier to share code
and ideas between related drivers. If you want to convince yourself,
just imagine the mess it would be if all drivers for PCI devices lived
under drivers/pci.
Jiri Kosina Oct. 6, 2012, 9:18 p.m. UTC | #13
On Sat, 6 Oct 2012, Jean Delvare wrote:

> > The question is what drives the choice of where to put HID-over-XXX, among the following
> >  1- who the maintainer is. Here, Benjamin will probably maintain this 
> > so it does not help.
> >  2- dependencies. HID-over-XXX depends on HID as much as it depends on 
> > XXX, so it does not help.
> >  3- data flow. Indeed, HID is a client of HID-over-XXX which is a 
> > client of XXX. Are there other parts of the kernel where this drives 
> > the choice of where YYY-over-XXX lives?
> > 
> > Jiri, Marcel, Greg, others, any opinions?
> 
> My vote is a clear 3. It took me a few years to kick all users (as
> opposed to implementers) of i2c from drivers/i2c and finding them a
> proper home, I'm not going to accept new intruders. Grouping drivers
> according to what they implement makes it a lot easier to share code
> and ideas between related drivers. If you want to convince yourself,
> just imagine the mess it would be if all drivers for PCI devices lived
> under drivers/pci.

This is more or less consistent with my original opinion when I was 
refactoring the HID layer out of the individual drivers a few years ago.

But Marcel objected that he wants to keep all the bluetooth-related 
drivers under net/bluetooth, and I didn't really want to push hard against 
this, because I don't have really super-strong personal preference either 
way.

But we definitely can use this oportunity to bring this up for discussion 
again.
Stéphane Chatty Oct. 6, 2012, 9:27 p.m. UTC | #14
Le 6 oct. 2012 à 23:11, Jean Delvare a écrit :

> On Sat, 6 Oct 2012 22:30:00 +0200, Stéphane Chatty wrote:
>> Le 6 oct. 2012 à 22:04, Jean Delvare a écrit :
>>> Looks like the wrong place for this driver. HID-over-USB support lives
>>> under drivers/hid, so your driver should go there as well. Not only
>>> this will be more consistent, but it also makes more sense: your driver
>>> is a user, not an implementer, of the I2C layer, so it doesn't belong
>>> to drivers/i2c.
>> 
>> This is a question I asked a few months back, but apparently not all points of views had been expressed at the time. Currently, HID-over-USB lives in drivers/hid, but HID-over-BT lives in drivers/bluetooth. When I asked, Jiri explained that he maintained HID-over-USB and Marcel maintained HID-over-BT, which explained the choices made. Let's try to summarize what we know now:
>> 
>> The question is what drives the choice of where to put HID-over-XXX, among the following
>> 1- who the maintainer is. Here, Benjamin will probably maintain this so it does not help.
>> 2- dependencies. HID-over-XXX depends on HID as much as it depends on XXX, so it does not help.
>> 3- data flow. Indeed, HID is a client of HID-over-XXX which is a client of XXX. Are there other parts of the kernel where this drives the choice of where YYY-over-XXX lives?
>> 
>> Jiri, Marcel, Greg, others, any opinions?
> 
> My vote is a clear 3. It took me a few years to kick all users (as
> opposed to implementers) of i2c from drivers/i2c and finding them a
> proper home, I'm not going to accept new intruders. Grouping drivers
> according to what they implement makes it a lot easier to share code
> and ideas between related drivers. If you want to convince yourself,
> just imagine the mess it would be if all drivers for PCI devices lived
> under drivers/pci.


Having no strong opinion myself, I'm trying to get to the bottom of this :-) Here, I see two points that need clarification:

- I'm under the impression that the situation is exactly opposite between i2c and USB: drivers/usb contains lots of drivers for specific devices, but HID-over-USB is in drivers/hid. I actually found this disturbing when reading the HID code for the first time. Mmmm.
- given your explanation, I'd say that you would agree to 2 as well, if it meant for instance that HID-over-I2C is neither in drivers/hid nor drivers/i2c. Actually, you don't care whether it is 1, 2, or 3 that drives the choice as long as HID-over-I2C is not in drivers/i2c, do you? :-)

Cheers,

St.--
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
Jiri Kosina Oct. 6, 2012, 9:28 p.m. UTC | #15
On Sat, 6 Oct 2012, Jiri Kosina wrote:

> > My vote is a clear 3. It took me a few years to kick all users (as
> > opposed to implementers) of i2c from drivers/i2c and finding them a
> > proper home, I'm not going to accept new intruders. Grouping drivers
> > according to what they implement makes it a lot easier to share code
> > and ideas between related drivers. If you want to convince yourself,
> > just imagine the mess it would be if all drivers for PCI devices lived
> > under drivers/pci.
> 
> This is more or less consistent with my original opinion when I was 
> refactoring the HID layer out of the individual drivers a few years ago.
> 
> But Marcel objected that he wants to keep all the bluetooth-related 
> drivers under net/bluetooth, and I didn't really want to push hard against 
> this, because I don't have really super-strong personal preference either 
> way.
> 
> But we definitely can use this oportunity to bring this up for discussion 
> again.

Basically, to me this all boils down to the question -- what is more 
important: low-level transport being used, or the general function of the 
device?

To me, it's the latter, and as such, everything would belong under 
drivers/hid.

On the other hand, I believe the Marcel will be arguing the bluetooth 
devices are actually network devices, and he has got a point as well (even 
though I personally consider bluetooth keyboard to be much more HID device 
than network device).
Stéphane Chatty Oct. 6, 2012, 9:39 p.m. UTC | #16
Le 6 oct. 2012 à 23:28, Jiri Kosina a écrit :

> On Sat, 6 Oct 2012, Jiri Kosina wrote:
> 
>>> My vote is a clear 3. It took me a few years to kick all users (as
>>> opposed to implementers) of i2c from drivers/i2c and finding them a
>>> proper home, I'm not going to accept new intruders. Grouping drivers
>>> according to what they implement makes it a lot easier to share code
>>> and ideas between related drivers. If you want to convince yourself,
>>> just imagine the mess it would be if all drivers for PCI devices lived
>>> under drivers/pci.
>> 
>> This is more or less consistent with my original opinion when I was 
>> refactoring the HID layer out of the individual drivers a few years ago.
>> 
>> But Marcel objected that he wants to keep all the bluetooth-related 
>> drivers under net/bluetooth, and I didn't really want to push hard against 
>> this, because I don't have really super-strong personal preference either 
>> way.
>> 
>> But we definitely can use this oportunity to bring this up for discussion 
>> again.
> 
> Basically, to me this all boils down to the question -- what is more 
> important: low-level transport being used, or the general function of the 
> device?
> 
> To me, it's the latter, and as such, everything would belong under 
> drivers/hid.

Then shouldn't is be drivers/input, rather?

St.


--
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
Jean Delvare Oct. 7, 2012, 7:16 a.m. UTC | #17
On Sat, 6 Oct 2012 23:27:47 +0200, Stéphane Chatty wrote:
> Le 6 oct. 2012 à 23:11, Jean Delvare a écrit :
> > On Sat, 6 Oct 2012 22:30:00 +0200, Stéphane Chatty wrote:
> >> This is a question I asked a few months back, but apparently not all points of views had been expressed at the time. Currently, HID-over-USB lives in drivers/hid, but HID-over-BT lives in drivers/bluetooth. When I asked, Jiri explained that he maintained HID-over-USB and Marcel maintained HID-over-BT, which explained the choices made. Let's try to summarize what we know now:
> >> 
> >> The question is what drives the choice of where to put HID-over-XXX, among the following
> >> 1- who the maintainer is. Here, Benjamin will probably maintain this so it does not help.
> >> 2- dependencies. HID-over-XXX depends on HID as much as it depends on XXX, so it does not help.
> >> 3- data flow. Indeed, HID is a client of HID-over-XXX which is a client of XXX. Are there other parts of the kernel where this drives the choice of where YYY-over-XXX lives?
> >> 
> >> Jiri, Marcel, Greg, others, any opinions?
> > 
> > My vote is a clear 3. It took me a few years to kick all users (as
> > opposed to implementers) of i2c from drivers/i2c and finding them a
> > proper home, I'm not going to accept new intruders. Grouping drivers
> > according to what they implement makes it a lot easier to share code
> > and ideas between related drivers. If you want to convince yourself,
> > just imagine the mess it would be if all drivers for PCI devices lived
> > under drivers/pci.
> 
> 
> Having no strong opinion myself, I'm trying to get to the bottom of this :-) Here, I see two points that need clarification:
> 
> - I'm under the impression that the situation is exactly opposite between i2c and USB: drivers/usb contains lots of drivers for specific devices, but HID-over-USB is in drivers/hid. I actually found this disturbing when reading the HID code for the first time. Mmmm.

Indeed I see a lot of drivers under drivers/usb. I'm glad I am not
responsible for this subsystem ;) I think drivers/pci is a much cleaner
example to follow. If nothing else, grouping drivers by functionality
solves the problem of devices which can be accessed through multiple
transport layers. For devices with multiple functions, we have
drivers/mfd, specifically designed to make it possible to put support
for each function into its dedicated location.

> - given your explanation, I'd say that you would agree to 2 as well, if it meant for instance that HID-over-I2C is neither in drivers/hid nor drivers/i2c. Actually, you don't care whether it is 1, 2, or 3 that drives the choice as long as HID-over-I2C is not in drivers/i2c, do you? :-)

I do care that things are as consistent and logical as possible. I know
sometimes there are borderline cases, or things done a certain way for
historical reasons, but grouping by functionality seems the more
logical and efficient as a rule of thumb.
Jean Delvare Oct. 7, 2012, 2:28 p.m. UTC | #18
Salut Benjamin,

On Fri, 14 Sep 2012 15:41:43 +0200, benjamin.tissoires wrote:
> From: Benjamin Tissoires <benjamin.tissoires@enac.fr>
> 
> Microsoft published the protocol specification of HID over i2c:
> http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
> 
> This patch introduces an implementation of this protocol.
> 
> This implementation does not includes the ACPI part of the specification.
> This will come when ACPI 5.0 devices will be available.
> 
> Once the ACPI part will be done, OEM will not have to declare HID over I2C
> devices in their platform specific driver.
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
> ---
> 
> Hi,
> 
> this is finally my first implementation of HID over I2C.
> 
> This has been tested on an Elan Microelectronics HID over I2C device, with
> a Samsung Exynos 4412 board.
> 
> Any comments are welcome.

Code review follows. It is by no way meant to be complete, as I don't
know a thing about HID. I hope you'll find it useful nevertheless.

> 
> Cheers,
> Benjamin
> 
>  drivers/i2c/Kconfig         |    8 +
>  drivers/i2c/Makefile        |    1 +
>  drivers/i2c/i2c-hid.c       | 1027 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/i2c/i2c-hid.h |   35 ++
>  4 files changed, 1071 insertions(+)
>  create mode 100644 drivers/i2c/i2c-hid.c
>  create mode 100644 include/linux/i2c/i2c-hid.h
> 
> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> index 5a3bb3d..5adf65a 100644
> --- a/drivers/i2c/Kconfig
> +++ b/drivers/i2c/Kconfig
> @@ -47,6 +47,14 @@ config I2C_CHARDEV
>  	  This support is also available as a module.  If so, the module 
>  	  will be called i2c-dev.
>  
> +config I2C_HID
> +	tristate "HID over I2C bus"

You are definitely missing dependencies here, CONFIG_HID at least.

> +	help
> +	  Say Y here to use the HID over i2c protocol implementation.
> +
> +	  This support is also available as a module.  If so, the module
> +	  will be called i2c-hid.
> +
>  config I2C_MUX
>  	tristate "I2C bus multiplexing support"
>  	help
> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> index beee6b2..8f38116 100644
> --- a/drivers/i2c/Makefile
> +++ b/drivers/i2c/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_BOARDINFO)	+= i2c-boardinfo.o
>  obj-$(CONFIG_I2C)		+= i2c-core.o
>  obj-$(CONFIG_I2C_SMBUS)		+= i2c-smbus.o
>  obj-$(CONFIG_I2C_CHARDEV)	+= i2c-dev.o
> +obj-$(CONFIG_I2C_HID)		+= i2c-hid.o
>  obj-$(CONFIG_I2C_MUX)		+= i2c-mux.o
>  obj-y				+= algos/ busses/ muxes/
>  
> diff --git a/drivers/i2c/i2c-hid.c b/drivers/i2c/i2c-hid.c
> new file mode 100644
> index 0000000..eb17d8c
> --- /dev/null
> +++ b/drivers/i2c/i2c-hid.c
> @@ -0,0 +1,1027 @@
> +/*
> + * HID over I2C protocol implementation
> + *
> + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
> + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
> + *
> + * This code is partly based on "USB HID support for Linux":
> + *
> + *  Copyright (c) 1999 Andreas Gal
> + *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
> + *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
> + *  Copyright (c) 2007-2008 Oliver Neukum
> + *  Copyright (c) 2006-2010 Jiri Kosina
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>

I don't think you need to include that one, everything irq-related you
need comes with <linux/interrupt.h> below. The header comment in that
file actually says: "Please do not include this file in generic code."

> +#include <linux/gpio.h>

Your driver makes no use of GPIO.

> +#include <linux/interrupt.h>
> +#include <linux/input.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/pm.h>

You are missing <linux/spinlock.h> for
spin_lock_irq()/spin_unlock_irq(), <linux/device.h> for
device_may_wakeup(), <linux/wait.h> for wait_event_timeout(),
<linux/err.h> for IS_ERR(), <linux/string.h> for strcat()/memcpy(),
<linux/list.h> for list_for_each_entry() and <linux/jiffies.h> for
msecs_to_jiffies(). I'd suggest including <linux/kernel.h> as well, for
sprintf() if nothing else, and <linux/bug.h> for WARN_ON().

> +
> +#include <linux/i2c/i2c-hid.h>
> +
> +#include <linux/hid.h>
> +#include <linux/hiddev.h>
> +#include <linux/hidraw.h>

I don't think you using anything from <linux/hidraw.h>, do you?

> +
> +#define DRIVER_NAME		"i2chid"
> +#define DRIVER_DESC		"HID over I2C core driver"

I see little interest in this define, as you're only using it once.
Even DRIVER_NAME isn't so useful, 2 of the 3 occurrences would be
replaced with client->name.&

> +
> +#define I2C_HID_COMMAND_TRIES	3
> +
> +/* flags */
> +#define I2C_HID_STARTED		(1 << 0)
> +#define I2C_HID_OUT_RUNNING	(1 << 1)
> +#define I2C_HID_IN_RUNNING	(1 << 2)
> +#define I2C_HID_RESET_PENDING	(1 << 3)
> +#define I2C_HID_SUSPENDED	(1 << 4)

3 of these are never used, so why define them?

> +
> +#define I2C_HID_PWR_ON		0x00
> +#define I2C_HID_PWR_SLEEP	0x01
> +
> +/* debug option */
> +static bool debug = false;

checkpatch.pl says:

ERROR: do not initialise statics to 0 or NULL
#206: FILE: drivers/i2c/i2c-hid.c:52:
+static bool debug = false;


> +module_param(debug, bool, 0444);
> +MODULE_PARM_DESC(debug, "print a lot of debug informations");

Spelling: information (in English, "information" is already a plural.)

> +
> +struct i2chid_desc {
> +	__le16 wHIDDescLength;
> +	__le16 bcdVersion;
> +	__le16 wReportDescLength;
> +	__le16 wReportDescRegister;
> +	__le16 wInputRegister;
> +	__le16 wMaxInputLength;
> +	__le16 wOutputRegister;
> +	__le16 wMaxOutputLength;
> +	__le16 wCommandRegister;
> +	__le16 wDataRegister;
> +	__le16 wVendorID;
> +	__le16 wProductID;
> +	__le16 wVersionID;
> +} __packed;
> +
> +struct i2chid_cmd {
> +	enum {
> +		/* fecth HID descriptor */

Typo: fetch

> +		HID_DESCR_CMD,
> +
> +		/* fetch report descriptors */
> +		HID_REPORT_DESCR_CMD,
> +
> +		/* commands */
> +		HID_RESET_CMD,
> +		HID_GET_REPORT_CMD,
> +		HID_SET_REPORT_CMD,
> +		HID_GET_IDLE_CMD,
> +		HID_SET_IDLE_CMD,
> +		HID_GET_PROTOCOL_CMD,
> +		HID_SET_PROTOCOL_CMD,
> +		HID_SET_POWER_CMD,
> +
> +		/* read/write data register */
> +		HID_DATA_CMD,
> +	} name;
> +	unsigned int registerIndex;
> +	__u8 opcode;
> +	unsigned int length;
> +	bool wait;
> +};
> +
> +union command {
> +	u8 data[0];
> +	struct cmd {
> +		__le16 reg;
> +		__u8 reportTypeID;
> +		__u8 opcode;
> +	} __packed c;
> +};
> +
> +#define I2C_HID_CMD(name_, opcode_) \
> +	.name = name_, .opcode = opcode_, .length = 4, \
> +	.registerIndex = offsetof(struct i2chid_desc, wCommandRegister)
> +
> +static const struct i2chid_cmd cmds[] = {
> +	{ I2C_HID_CMD(HID_RESET_CMD,		0x01), .wait = true },
> +	{ I2C_HID_CMD(HID_GET_REPORT_CMD,	0x02) },
> +	{ I2C_HID_CMD(HID_SET_REPORT_CMD,	0x03) },
> +	{ I2C_HID_CMD(HID_GET_IDLE_CMD,		0x04) },
> +	{ I2C_HID_CMD(HID_SET_IDLE_CMD,		0x05) },
> +	{ I2C_HID_CMD(HID_GET_PROTOCOL_CMD,	0x06) },
> +	{ I2C_HID_CMD(HID_SET_PROTOCOL_CMD,	0x07) },
> +	{ I2C_HID_CMD(HID_SET_POWER_CMD,	0x08) },
> +
> +	{.name = HID_DESCR_CMD,
> +	 .length = 2},
> +
> +	{.name = HID_REPORT_DESCR_CMD,
> +	 .registerIndex = offsetof(struct i2chid_desc, wReportDescRegister),
> +	 .opcode = 0x00,
> +	 .length = 2},
> +
> +	{.name = HID_DATA_CMD,
> +	 .registerIndex = offsetof(struct i2chid_desc, wDataRegister),
> +	 .opcode = 0x00,
> +	 .length = 2},

Please use consistent spacing.

> +
> +	{}, /* terminating */

I suspect it would be more efficient to not terminate the array and use
ARRAY_SIZE() when needed.

> +};
> +
> +/* The main device structure */
> +struct i2chid {
> +	struct i2c_client	*client;	/* i2c client */
> +	struct hid_device	*hid;	/* pointer to corresponding HID dev */
> +	union {
> +		__u8 hdesc_buffer[sizeof(struct i2chid_desc)];
> +		struct i2chid_desc hdesc;	/* the HID Descriptor */
> +	};
> +	__le16			wHIDDescRegister; /* location of the i2c
> +						   * register of the HID
> +						   * descriptor. */
> +	unsigned int		bufsize;	/* i2c buffer size */
> +	char			*inbuf;		/* Input buffer */
> +
> +	spinlock_t		flock;		/* flags spinlock */
> +	unsigned long		flags;		/* device flags */
> +
> +	int			irq;		/* the interrupt line irq */
> +
> +	wait_queue_head_t	wait;		/* For waiting the interrupt */
> +};
> +
> +void i2chid_print_buffer(struct i2chid *ihid, u8 *buf, unsigned int n)

Should be static and buf should be declared const.

> +{
> +	int i;
> +	char *string = kzalloc((n*3+1)*sizeof(char), GFP_KERNEL);

This could fail, you have to check for it.

> +	char tmpbuf[4] = "";

Useless initialization.

> +
> +	for (i = 0; i < n; ++i) {
> +		sprintf(tmpbuf, "%02x ", buf[i] & 0xFF);

Useless mask.

> +		strcat(string, tmpbuf);
> +	}
> +
> +	dev_err(&ihid->client->dev, "%s\n", string);
> +	kfree(string);
> +}

This is horribly inefficient. I don't know what usual value you expect
for "n", but the above algorithm has a O(n^2) complexity for no good
reason. As a rule of thumb, every time you use strcat() in a loop, you
can be sure you did something wrong ;)

The efficient way of doing the above is to remember where you are in
the output buffer, and sprintf at this location directly:

	int i;
	char *string = kzalloc((n*3+1)*sizeof(char), GFP_KERNEL);
	char *p = string;

	for (i = 0; i < n; ++i)
		p += sprintf(p, "%02x ", buf[i]);

I would also suggest that you put some introduction work in the log
message, otherwise it looks like mere garbage. Doing so will also give
you the opportunity to put the spaces before the numbers rather than
after them, so you don't print a useless trailing space.

> +
> +static int __i2chid_command(struct i2c_client *client, int command, u8 reportID,
> +		u8 reportType, u8 *args, int args_len,
> +		unsigned char *buf_recv, int data_len)
> +{
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	union command *cmd;
> +	unsigned char *rec_buf = buf_recv;

Useless variable, use buf_recv directly.

> +	int ret;
> +	int tries = I2C_HID_COMMAND_TRIES;
> +	int length = 0;
> +	struct i2c_msg msg[2];
> +	int msg_num = 0;
> +	int i;
> +	bool wait = false;
> +
> +	if (WARN_ON(!ihid))
> +		return -ENODEV;
> +
> +	cmd = kmalloc(args_len + sizeof(union command), GFP_KERNEL);

Your intent would be clearer with sizeof(union command) + args_len.
Also, what excuse do you have for not using kzalloc?

> +	if (!cmd)
> +		return -ENOMEM;
> +
> +	for (i = 0; cmds[i].length; i++) {
> +		unsigned int registerIndex;
> +
> +		if (cmds[i].name != command)
> +			continue;
> +
> +		registerIndex = cmds[i].registerIndex;
> +		cmd->data[0] = ihid->hdesc_buffer[registerIndex];
> +		cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1];
> +		cmd->c.opcode = cmds[i].opcode;
> +		length = cmds[i].length;
> +		wait = cmds[i].wait;
> +	}

This again is horribly inefficient. Just order the commands properly in
cmds[] and you can access them by index. This will be much faster.

> +
> +	/* special case for HID_DESCR_CMD */
> +	if (command == HID_DESCR_CMD)
> +		cmd->c.reg = ihid->wHIDDescRegister;
> +
> +	cmd->c.reportTypeID = reportID | reportType << 4;
> +
> +	memcpy(cmd->data + length, args, args_len);
> +	length += args_len;
> +
> +	if (debug) {
> +		char tmpstr[4] = "";
> +		char strbuf[50] = "";
> +		int i;
> +		for (i = 0; i < length; i++) {
> +			sprintf(tmpstr, "%02x ", cmd->data[i] & 0xFF);
> +			strcat(strbuf, tmpstr);
> +		}
> +		dev_err(&client->dev, "%s, cmd=%s\n", __func__, strbuf);
> +	}

AFAICS this is mostly duplicating i2chid_print_buffer().

> +
> +	msg[0].addr = client->addr;
> +	msg[0].flags = client->flags & I2C_M_TEN;
> +	msg[0].len = length;
> +	msg[0].buf = (char *) cmd->data;

Useless cast.

> +	msg[1].addr = client->addr;
> +	msg[1].flags = client->flags & I2C_M_TEN;
> +	msg[1].flags |= I2C_M_RD;
> +	msg[1].len = data_len;
> +	msg[1].buf = rec_buf;
> +
> +	msg_num = data_len > 0 ? 2 : 1;

If !data_len you have been initializing 4 fields for nothing right
above. Test first and you'll make your code faster for that specific
case.

> +
> +	if (wait) {
> +		spin_lock_irq(&ihid->flock);
> +		set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
> +		spin_unlock_irq(&ihid->flock);
> +	}
> +
> +	do {
> +		ret = i2c_transfer(client->adapter, msg, msg_num);
> +		if (ret > 0)

Success here is ret == msg_num, not ret > 0.

> +			break;
> +		tries--;
> +		dev_dbg(&client->dev, "retrying i2chid_command:%d (%d)\n",
> +			command, tries);
> +	} while (tries > 0);
> +
> +	if (wait && ret > 0) {
> +		if (debug)
> +			dev_err(&client->dev, "%s: waiting....\n", __func__);

Ellipsis is usually 3 dots not 4.

> +		if (!wait_event_timeout(ihid->wait,
> +				!test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
> +				msecs_to_jiffies(5000)))
> +			ret = -ENODATA;
> +		if (debug)
> +			dev_err(&client->dev, "%s: finished.\n", __func__);
> +	}
> +
> +	kfree(cmd);
> +
> +	return ret > 0 ? ret != msg_num : ret;

Makes no sense to me. You should return 0 if ret == msg_num, and a
negative error code otherwise.

> +}
> +
> +static int i2chid_command(struct i2c_client *client, int command,
> +		unsigned char *buf_recv, int data_len)
> +{
> +	return __i2chid_command(client, command, 0, 0, NULL, 0,
> +				buf_recv, data_len);
> +}
> +
> +static int i2chid_get_report(struct i2c_client *client, u8 reportType,
> +		u32 reportID, unsigned char *buf_recv, int data_len)
> +{
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	u8 args[6];
> +	int ret;
> +	int args_len = 0;
> +	u16 readRegister = ihid->hdesc.wDataRegister;

This is missing le16_to_cpu().

> +
> +	if (debug)
> +		dev_err(&client->dev, "%s\n", __func__);
> +
> +	if (reportID >= 15) {

Mixing decimal and hexadecimal is confusing.

> +		args[args_len++] = (reportID >> 0) & 0xFF;
> +		args[args_len++] = (reportID >> 8) & 0xFF;
> +		args[args_len++] = (reportID >> 16) & 0xFF;
> +		args[args_len++] = (reportID >> 24) & 0xFF;
> +		reportID = 0x0F;
> +	}
> +
> +	args[args_len++] = readRegister & 0xff;
> +	args[args_len++] = (readRegister >> 8) & 0xff;

Useless mask. Please also use consistent case for hexadecimal values.

> +
> +	ret = __i2chid_command(client, HID_GET_REPORT_CMD, reportID & 0x0F,

Useless mask.

> +		reportType, args, args_len, buf_recv, data_len);
> +	if (ret) {
> +		dev_err(&client->dev, "HID_GET_REPORT_CMD Fail\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int i2chid_set_report(struct i2c_client *client, u8 reportType,
> +		u32 reportID, unsigned char *buf, int data_len)
> +{
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	u8 *args;
> +	int ret;
> +	u16 dataRegister = ihid->hdesc.wDataRegister;

Missing conversion again.

> +	int args_len =  (reportID >= 15 ? 4 : 0) +
> +			2 /* dataRegister */ +
> +			2 /* size */ +

/* data_len */ actually.

> +			data_len;
> +	int index = 0;
> +
> +	if (debug)
> +		dev_err(&client->dev, "%s\n", __func__);
> +
> +	args = kmalloc(args_len, GFP_KERNEL);

This should be kzalloc() and you should check for failure.

> +
> +	if (reportID >= 15) {
> +		args[index++] = (reportID >> 0) & 0xFF;
> +		args[index++] = (reportID >> 8) & 0xFF;
> +		args[index++] = (reportID >> 16) & 0xFF;
> +		args[index++] = (reportID >> 24) & 0xFF;
> +		reportID = 0x0F;
> +	}
> +
> +	args[index++] = dataRegister & 0xff;
> +	args[index++] = (dataRegister >> 8) & 0xff;
> +
> +	args[index++] = data_len & 0xff;
> +	args[index++] = (data_len >> 8) & 0xff;

data_len is an int, there's no guarantee it fits on 2 bytes.

> +
> +	memcpy(&args[index], buf, data_len);
> +
> +	ret = __i2chid_command(client, HID_SET_REPORT_CMD, reportID & 0x0F,
> +		reportType, args, args_len, NULL, 0);
> +	if (ret) {
> +		dev_err(&client->dev, "HID_SET_REPORT_CMD Fail\n");

Memory leak!

> +		return -EINVAL;
> +	}
> +
> +	kfree(args);
> +	return data_len;
> +}
> +
> +static int i2chid_set_power(struct i2c_client *client, int power_state)
> +{
> +	int ret;
> +
> +	if (debug)
> +		dev_err(&client->dev, "%s\n", __func__);
> +
> +	ret = __i2chid_command(client, HID_SET_POWER_CMD, power_state & 0x01,

Useless mask.

> +		0, NULL, 0, NULL, 0);
> +	if (ret) {
> +		dev_err(&client->dev, "HID_SET_POWER_CMD Fail\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int i2chid_hwreset(struct i2c_client *client)
> +{
> +	uint8_t buf_recv[79];

Why uint8_t here when you used unsigned char everywhere else? Why 79
when you only need 2 bytes?

> +	int ret;
> +
> +	if (debug)
> +		dev_err(&client->dev, "%s\n", __func__);
> +
> +	ret = i2chid_set_power(client, I2C_HID_PWR_ON);
> +	if (ret)
> +		return -EINVAL;
> +
> +	if (debug)
> +		dev_err(&client->dev, "resetting...\n");
> +
> +	ret = i2chid_command(client, HID_RESET_CMD, buf_recv, 0);

I don't see the point of passing buf_recv here.

> +	if (ret) {
> +		dev_err(&client->dev, "HID_RESET_CMD Fail\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = i2c_master_recv(client, buf_recv, 2);
> +	if (ret != 2 || (buf_recv[0] != 0 && buf_recv[1] != 0)) {

If I read the datasheet correctly, the device must reply with two 0
bytes after reset, so the above test is wrong, it should be
buf_recv[0] != 0 || buf_recv[1] != 0 (||, not &&.)

> +		dev_err(&client->dev,
> +			"HID_RESET_CMD Failed: got %02x %02x, size=%d\n",
> +			buf_recv[0], buf_recv[1], ret);

If size < 2, you are printing uninitialized bytes from the stack, which
is bad.

> +
> +		i2chid_set_power(client, I2C_HID_PWR_SLEEP);
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static bool i2chid_get_input(struct i2chid *ihid)

Very confusing return convention, given that every other function in
this driver returns 0 on success.

> +{
> +	int ret;
> +	int size = ihid->hdesc.wMaxInputLength;

Missing conversion.

> +
> +	ret = i2c_master_recv(ihid->client, ihid->inbuf, size);
> +	if (ret != size) {
> +		dev_err(&ihid->client->dev, "%s: got %d data instead of %d\n",
> +			__func__, ret, size);
> +		return false;
> +	}
> +
> +	if (debug)
> +		i2chid_print_buffer(ihid, ihid->inbuf, size);
> +
> +	ret = hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2,
> +			size - 2, 1);
> +	if (ret)
> +		return false;
> +
> +	return true;
> +}
> +
> +static irqreturn_t i2chid_irq(int irq, void *dev_id)
> +{
> +	struct i2chid *ihid = dev_id;
> +	int ready;
> +
> +	if (!ihid)
> +		return IRQ_HANDLED;

You'll have to explain that one to me. This just can't happen, can it?

> +
> +	spin_lock_irq(&ihid->flock);
> +	if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags)) {
> +		spin_unlock_irq(&ihid->flock);
> +
> +		wake_up(&ihid->wait);
> +		return IRQ_HANDLED;
> +	}
> +
> +	ready = test_bit(I2C_HID_STARTED, &ihid->flags);
> +	spin_unlock_irq(&ihid->flock);
> +
> +	if (ready)
> +		i2chid_get_input(ihid);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int i2chid_get_report_length(struct hid_report *report)
> +{
> +	return ((report->size - 1) >> 3) + 1 +
> +		report->device->report_enum[report->type].numbered + 2;
> +}
> +
> +void i2chid_init_report(struct hid_report *report)

Should be static.

> +{
> +	struct hid_device *hid = report->device;
> +	struct i2c_client *client = hid->driver_data;
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	unsigned int size, ret_size;
> +
> +	size = i2chid_get_report_length(report);
> +	i2chid_get_report(client,
> +			report->type == HID_FEATURE_REPORT ? 0x03 : 0x01,
> +			report->id, ihid->inbuf, size);
> +
> +	if (debug)
> +		i2chid_print_buffer(ihid, ihid->inbuf, size);
> +
> +	ret_size = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
> +
> +	if (ret_size != size) {
> +		dev_err(&client->dev, "error in %s size:%d / ret_size:%d\n",
> +			__func__, size, ret_size);
> +		return;
> +	}
> +
> +	/* hid->driver_lock is held as we are in probe function,
> +	 * we just need to setup the input fields, so using
> +	 * hid_report_raw_event is safe. */
> +	hid_report_raw_event(hid, report->type, ihid->inbuf + 2, size - 2, 1);
> +}
> +
> +/*
> + * Initialize all reports
> + */
> +void i2chid_init_reports(struct hid_device *hid)

Should be static.

> +{
> +	struct hid_report *report;
> +
> +	list_for_each_entry(report,
> +		&hid->report_enum[HID_INPUT_REPORT].report_list, list)
> +		i2chid_init_report(report);
> +
> +	list_for_each_entry(report,
> +		&hid->report_enum[HID_FEATURE_REPORT].report_list, list)
> +		i2chid_init_report(report);
> +}
> +
> +/*
> + * Traverse the supplied list of reports and find the longest
> + */
> +static void i2chid_find_max_report(struct hid_device *hid, unsigned int type,
> +		unsigned int *max)
> +{
> +	struct hid_report *report;
> +	unsigned int size;
> +
> +	/* We should not rely on wMaxInputLength, as some devices may set it to
> +	 * a wrong length. */
> +	list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
> +		size = i2chid_get_report_length(report);
> +		if (*max < size)
> +			*max = size;
> +	}
> +}
> +
> +static int i2chid_alloc_buffers(struct i2chid *ihid)
> +{
> +	ihid->inbuf = kzalloc(ihid->bufsize, GFP_KERNEL);
> +
> +	if (!ihid->inbuf)
> +		return -1;

-ENOMEM would be much cleaner.

> +
> +	return 0;
> +}
> +
> +static void i2chid_free_buffers(struct i2chid *ihid)
> +{
> +	kfree(ihid->inbuf);
> +}
> +
> +static int i2chid_get_raw_report(struct hid_device *hid,
> +		unsigned char report_number, __u8 *buf, size_t count,
> +		unsigned char report_type)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	int ret;
> +
> +	if (count > ihid->bufsize)
> +		count = ihid->bufsize;
> +
> +	ret = i2chid_get_report(client,
> +			report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
> +			report_number, ihid->inbuf, count);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	count = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
> +
> +	memcpy(buf, ihid->inbuf + 2, count);
> +
> +	return count;
> +}
> +
> +static int i2chid_output_raw_report(struct hid_device *hid, __u8 *buf,
> +		size_t count, unsigned char report_type)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	int ret;
> +	int report_id = buf[0];
> +
> +	ret = i2chid_set_report(client,
> +				report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
> +				report_id, buf, count);
> +
> +	return ret;
> +
> +}
> +
> +static int i2chid_fetch_hid_descriptor(struct i2chid *ihid)

Only called in probe function, so can be __devinit. It would be better
to place both functions close to each other BTW.

> +{
> +	struct i2c_client *client = ihid->client;
> +	struct i2chid_desc *hdesc = &ihid->hdesc;
> +	unsigned int dsize = 0;

Useless initialization.

> +	int ret;
> +
> +	/* Fetch the length of HID description, retrieve the 4 first bytes:
> +	 * bytes 0-1 -> length
> +	 * bytes 2-3 -> bcdVersion (has to be 1.00) */
> +	ret = i2chid_command(client, HID_DESCR_CMD, ihid->hdesc_buffer, 4);
> +
> +	if (debug)
> +		dev_err(&client->dev,
> +			"%s, ihid->hdesc_buffer: %02x %02x %02x %02x\n",
> +			__func__,
> +			ihid->hdesc_buffer[0],
> +			ihid->hdesc_buffer[1],
> +			ihid->hdesc_buffer[2],
> +			ihid->hdesc_buffer[3]);
> +
> +	if (ret) {
> +		dev_err(&client->dev, "HID_DESCR_LENGTH_CMD Fail (ret=%d)\n",
> +			ret);
> +		return -EINVAL;

These should all be -ENODEV in this function: the device isn't what you
expected. EINVAL is for invalid argument.

> +	}
> +
> +	dsize = le16_to_cpu(hdesc->wHIDDescLength);
> +	if (!dsize || dsize > HID_MAX_DESCRIPTOR_SIZE) {
> +		dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
> +			dsize);
> +		return -EINVAL;
> +	}
> +
> +	/* check bcdVersion == 1.0 */
> +	if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
> +		dev_err(&client->dev,
> +			"unexpected HID descriptor bcdVersion (0x%04x)\n",
> +			le16_to_cpu(hdesc->bcdVersion));

So, the day revision 1.1 comes out and a device implements it, it won't
be supported, even if revision 1.1 only made minor changes we can live
without. Is it smart?

> +		return -EINVAL;
> +	}
> +
> +	if (debug)
> +		dev_err(&client->dev, "Fetching the HID descriptor\n");
> +
> +	ret = i2chid_command(client, HID_DESCR_CMD, ihid->hdesc_buffer, dsize);
> +	if (ret) {
> +		dev_err(&client->dev, "HID_DESCR_CMD Fail\n");
> +		return -EINVAL;
> +	}
> +
> +	if (debug)
> +		i2chid_print_buffer(ihid, ihid->hdesc_buffer, dsize);
> +
> +	return 0;
> +}
> +
> +static int i2chid_parse(struct hid_device *hid)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	struct i2chid_desc *hdesc = &ihid->hdesc;
> +	unsigned int rsize = 0;

Useless initialization.

> +	char *rdesc;
> +	int ret;
> +	int tries = 3;
> +
> +	if (debug)
> +		dev_err(&client->dev, "entering %s\n", __func__);
> +
> +	rsize = le16_to_cpu(hdesc->wReportDescLength);
> +	if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
> +		dbg_hid("weird size of report descriptor (%u)\n", rsize);
> +		return -EINVAL;
> +	}
> +
> +	do {
> +		ret = i2chid_hwreset(client);
> +		if (ret)
> +			msleep(1000);
> +	} while (tries-- > 0 && ret);
> +
> +	if (ret)
> +		return ret;
> +
> +	rdesc = kmalloc(rsize, GFP_KERNEL);
> +
> +	if (!rdesc) {
> +		dbg_hid("couldn't allocate rdesc memory\n");
> +		return -ENOMEM;
> +	}
> +
> +	if (debug)
> +		dev_err(&client->dev, "asking HID report descriptor\n");
> +
> +	ret = i2chid_command(client, HID_REPORT_DESCR_CMD, rdesc, rsize);
> +	if (ret) {
> +		hid_err(hid, "reading report descriptor failed\n");
> +		kfree(rdesc);
> +		ret = -ENOMEM;

No.

> +		goto err;
> +	}
> +
> +	if (debug)
> +		i2chid_print_buffer(ihid, rdesc, rsize);
> +
> +	ret = hid_parse_report(hid, rdesc, rsize);
> +	kfree(rdesc);
> +	if (ret) {
> +		dbg_hid("parsing report descriptor failed\n");
> +		goto err;
> +	}
> +
> +	return 0;
> +err:
> +	return ret;

This label makes no sense, just replace gotos by returns.

> +}
> +
> +static int i2chid_start(struct hid_device *hid)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	int ret;
> +
> +	ihid->bufsize = HID_MIN_BUFFER_SIZE;
> +	i2chid_find_max_report(hid, HID_INPUT_REPORT, &ihid->bufsize);
> +	i2chid_find_max_report(hid, HID_OUTPUT_REPORT, &ihid->bufsize);
> +	i2chid_find_max_report(hid, HID_FEATURE_REPORT, &ihid->bufsize);
> +
> +	if (ihid->bufsize > HID_MAX_BUFFER_SIZE)
> +		ihid->bufsize = HID_MAX_BUFFER_SIZE;

Won't this result in issues later?

> +
> +	if (i2chid_alloc_buffers(ihid)) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
> +		i2chid_init_reports(hid);
> +
> +	return 0;
> +
> +fail:
> +	i2chid_free_buffers(ihid);

This is wrong, you're freeing buffers you failed to allocate.

> +	return ret;
> +}
> +
> +static void i2chid_stop(struct hid_device *hid)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +
> +	if (WARN_ON(!ihid))
> +		return;
> +
> +	hid->claimed = 0;
> +
> +	i2chid_free_buffers(ihid);
> +}
> +
> +static int i2chid_open(struct hid_device *hid)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	int ret;
> +
> +	if (!hid->open++) {
> +		ret = i2chid_set_power(client, I2C_HID_PWR_ON);
> +		if (ret) {
> +			hid->open--;
> +			return -EIO;
> +		}
> +		spin_lock_irq(&ihid->flock);
> +		set_bit(I2C_HID_STARTED, &ihid->flags);
> +		spin_unlock_irq(&ihid->flock);
> +	}
> +	return 0;
> +}
> +
> +static void i2chid_close(struct hid_device *hid)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +
> +	/* protecting hid->open to make sure we don't restart
> +	 * data acquistion due to a resumption we no longer
> +	 * care about
> +	 */
> +	if (!--hid->open) {
> +		spin_lock_irq(&ihid->flock);
> +		clear_bit(I2C_HID_STARTED, &ihid->flags);
> +		spin_unlock_irq(&ihid->flock);
> +
> +		/* Save some power */
> +		i2chid_set_power(client, I2C_HID_PWR_SLEEP);
> +	}
> +}
> +
> +static int i2chid_power(struct hid_device *hid, int lvl)
> +{
> +	struct i2c_client *client = hid->driver_data;
> +	int r = 0;

Why not "ret" like everywhere else?

> +
> +	if (debug)
> +		dev_err(&client->dev, "%s lvl:%d\n", __func__, lvl);
> +
> +	switch (lvl) {
> +	case PM_HINT_FULLON:
> +		r = i2chid_set_power(client, I2C_HID_PWR_ON);
> +		break;
> +	case PM_HINT_NORMAL:
> +		r = i2chid_set_power(client, I2C_HID_PWR_SLEEP);
> +		break;
> +	}
> +	return r;
> +}
> +
> +static int i2chid_hidinput_input_event(struct input_dev *dev, unsigned int type,
> +		unsigned int code, int value)
> +{
> +	struct hid_device *hid = input_get_drvdata(dev);
> +	struct hid_field *field;
> +	int offset;
> +
> +	if (type == EV_FF)
> +		return input_ff_event(dev, type, code, value);
> +
> +	if (type != EV_LED)
> +		return -1;
> +
> +	offset = hidinput_find_field(hid, type, code, &field);
> +
> +	if (offset == -1) {
> +		hid_warn(dev, "event field not found\n");
> +		return -1;
> +	}
> +
> +	hid_set_field(field, offset, value);
> +
> +	return 0;
> +}
> +
> +static struct hid_ll_driver i2c_hid_driver = {
> +	.parse = i2chid_parse,
> +	.start = i2chid_start,
> +	.stop = i2chid_stop,
> +	.open = i2chid_open,
> +	.close = i2chid_close,
> +	.power = i2chid_power,
> +	.hidinput_input_event = i2chid_hidinput_input_event,
> +};
> +
> +static int i2chid_init_irq(struct i2c_client *client)

Can be __devinit.

> +{
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	int rc;

Why not "ret" like everywhere else?

> +
> +	dev_dbg(&client->dev, "Requesting IRQ: %d\n",
> +		client->irq);
> +
> +	rc = request_threaded_irq(client->irq, NULL, i2chid_irq,
> +			IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> +			DRIVER_NAME, ihid);
> +	if (rc < 0) {
> +		dev_dbg(&client->dev,
> +			"Could not register for %s interrupt, irq = %d,"
> +			" rc = %d\n",
> +		DRIVER_NAME, client->irq, rc);
> +
> +		return rc;
> +	}
> +
> +	return client->irq;
> +}
> +
> +static int __devinit i2chid_probe(struct i2c_client *client,
> +		const struct i2c_device_id *dev_id)
> +{
> +	int ret;
> +	struct i2chid *ihid;
> +	struct hid_device *hid;
> +	__u16 hidRegister;

You don't need this local variable.

> +	unsigned char tmpstr[11];
> +	struct i2chid_platform_data *platform_data = client->dev.platform_data;
> +
> +	dbg_hid("HID probe called for i2c %d\n", client->addr);

I2C addresses are traditionally printed as hexadecimal values.

> +
> +	if (!platform_data) {
> +		dev_err(&client->dev, "HID register address not provided\n");
> +		return -EINVAL;
> +	}
> +
> +	if (client->irq < 1) {
> +		dev_err(&client->dev,
> +			"HID over i2c has not been provided an Int IRQ\n");
> +		return -EINVAL;
> +	}
> +
> +	ihid = kzalloc(sizeof(struct i2chid), GFP_KERNEL);
> +	if (!ihid)
> +		return -ENOMEM;
> +
> +	i2c_set_clientdata(client, ihid);
> +
> +	ihid->client = client;
> +	ihid->flags = 0;

flags are already 0 thanks to kzalloc.

> +
> +	hidRegister = platform_data->hid_descriptor_address;
> +	ihid->wHIDDescRegister = cpu_to_le16(hidRegister);
> +
> +	init_waitqueue_head(&ihid->wait);
> +	spin_lock_init(&ihid->flock);
> +
> +	ret = i2chid_fetch_hid_descriptor(ihid);
> +	if (ret < 0)
> +		goto err;
> +
> +	ihid->irq = i2chid_init_irq(client);
> +	if (ihid->irq < 0)

"ret" is set to 0 at this point, so you return 0 on error. Not good.

> +		goto err;
> +
> +	hid = hid_allocate_device();
> +	if (IS_ERR(hid)) {
> +		ret = -ENOMEM;

Use PTR_ERR().

> +		goto err;
> +	}
> +
> +	ihid->hid = hid;
> +
> +	hid->driver_data = client;
> +	hid->ll_driver = &i2c_hid_driver;
> +	hid->hid_get_raw_report = i2chid_get_raw_report;
> +	hid->hid_output_raw_report = i2chid_output_raw_report;
> +	hid->ff_init = hid_pidff_init;
> +#ifdef CONFIG_USB_HIDDEV
> +	hid->hiddev_connect = hiddev_connect;
> +	hid->hiddev_disconnect = hiddev_disconnect;
> +	hid->hiddev_hid_event = hiddev_hid_event;
> +	hid->hiddev_report_event = hiddev_report_event;
> +#endif
> +	hid->dev.parent = &client->dev;
> +	hid->bus = BUS_I2C;
> +	hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
> +	hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
> +	hid->product = le16_to_cpu(ihid->hdesc.wProductID);
> +
> +	snprintf(tmpstr, sizeof(tmpstr), " %04X:%04X",
> +		 hid->vendor, hid->product);
> +	strlcpy(hid->name, client->name, sizeof(hid->name));
> +	strlcat(hid->name, tmpstr, sizeof(hid->name));

Please use snprintf on hid->name directly, it's more efficient.

> +
> +	ret = hid_add_device(hid);
> +	if (ret) {
> +		if (ret != -ENODEV)
> +			hid_err(client, "can't add hid device: %d\n", ret);
> +		goto err_mem_free;
> +	}
> +
> +	return 0;
> +
> +err_mem_free:
> +	hid_destroy_device(hid);
> +
> +err:
> +	if (ihid->irq >= 0)
> +		free_irq(ihid->irq, ihid);
> +
> +	kfree(ihid);
> +	return ret;
> +}
> +
> +static int __devexit i2chid_remove(struct i2c_client *client)
> +{
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +	struct hid_device *hid;
> +
> +	if (WARN_ON(!ihid))
> +		return -1;
> +
> +	hid = ihid->hid;
> +	hid_destroy_device(hid);
> +
> +	free_irq(client->irq, ihid);
> +
> +	kfree(ihid);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int i2chid_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct i2chid *ihid = i2c_get_clientdata(client);
> +
> +	if (device_may_wakeup(&client->dev))
> +		enable_irq_wake(ihid->irq);
> +
> +	/* Save some power */
> +	i2chid_set_power(client, I2C_HID_PWR_SLEEP);
> +
> +	return 0;
> +}
> +
> +static int i2chid_resume(struct device *dev)
> +{
> +	int ret;
> +	struct i2c_client *client = to_i2c_client(dev);
> +
> +	ret = i2chid_hwreset(client);
> +	if (ret)
> +		return ret;
> +
> +	if (device_may_wakeup(&client->dev))
> +		enable_irq_wake(client->irq);

All other drivers I checked call disable_irq_wake() here.

> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(i2chid_pm, i2chid_suspend, i2chid_resume);
> +
> +static const struct i2c_device_id i2chid_id_table[] = {
> +	{ "i2chid", 0 },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(i2c, i2chid_id_table);
> +
> +
> +static struct i2c_driver i2chid_driver = {
> +	.driver = {
> +		.name	= DRIVER_NAME,
> +		.owner	= THIS_MODULE,
> +		.pm	= &i2chid_pm,
> +	},
> +
> +	.probe		= i2chid_probe,
> +	.remove		= __devexit_p(i2chid_remove),
> +
> +	.id_table	= i2chid_id_table,
> +};
> +
> +module_i2c_driver(i2chid_driver);
> +
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/i2c/i2c-hid.h b/include/linux/i2c/i2c-hid.h
> new file mode 100644
> index 0000000..6685605
> --- /dev/null
> +++ b/include/linux/i2c/i2c-hid.h
> @@ -0,0 +1,35 @@
> +/*
> + * HID over I2C protocol implementation
> + *
> + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
> + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#ifndef __LINUX_I2C_HID_H
> +#define __LINUX_I2C_HID_H
> +
> +#include <linux/types.h>
> +
> +/**
> + * struct i2chid_platform_data - used by hid over i2c implementation.
> + * @hid_descriptor_address: i2c register where the HID descriptor is stored.
> + *
> + * Note that it is the responsibility for the platform driver (or the acpi 5.0

responsibility of

> + * driver) to setup the irq related to the gpio in the struct i2c_board_info.
> + * The platform driver should also setup the gpio according to the device:
> + *
> + * A typical example is the following:
> + *	irq = gpio_to_irq(intr_gpio);
> + *	hkdk4412_i2c_devs5[0].irq = irq; // store the irq in i2c_board_info
> + *	gpio_request(intr_gpio, "elan-irq");
> + *	s3c_gpio_setpull(intr_gpio, S3C_GPIO_PULL_UP);
> + */
> +struct i2chid_platform_data {
> +	u16 hid_descriptor_address;
> +};
> +
> +#endif /* __LINUX_I2C_HID_H */
Benjamin Tissoires Oct. 7, 2012, 4 p.m. UTC | #19
Hi Jean,

Thanks for the comments, the tests and the review. I'm going to try to
answer most of the remarks, so here is the first:

On Sat, Oct 6, 2012 at 10:04 PM, Jean Delvare <khali@linux-fr.org> wrote:
> Hi Benjamin,
>
[...]
>a
> ERROR: "hiddev_report_event" [drivers/i2c/i2c-hid.ko] undefined!
> ERROR: "hiddev_disconnect" [drivers/i2c/i2c-hid.ko] undefined!
> ERROR: "hiddev_connect" [drivers/i2c/i2c-hid.ko] undefined!
> ERROR: "hid_pidff_init" [drivers/i2c/i2c-hid.ko] undefined!
> make[1]: *** [__modpost] Erreur 1
> make: *** [modules] Erreur 2
>
> This is because these functions aren't exported and I tried to build
> i2c-hid as a module.BTW I see that these functions are part of the
> usbhid driver, which looks seriously wrong. If these functions are
> transport layer-independent, they should be moved to the hid-code or
> some sub-module. One should be able to enable HID-over-I2C without
> HID-over-USB.

It looks like I've been mislead by the generic names of these functions.
By looking at the code, it appears that I can not use them in their
current state as they are all usb related.
I think that it will need some work to refactor the general part of
hiddev so that I2C and bluetooth can use them. For the next version,
I'll just drop hiddev and pidff support.
I'm not sure we won't ever find a ff device connected through i2c, but
the hiddev support will surely be needed.

Cheers,
Benjamin

>
> --
> Jean Delvare
--
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
Benjamin Tissoires Oct. 7, 2012, 4:07 p.m. UTC | #20
On Sat, Oct 6, 2012 at 11:39 PM, Stéphane Chatty <chatty@enac.fr> wrote:
>
> Le 6 oct. 2012 à 23:28, Jiri Kosina a écrit :
>
>> On Sat, 6 Oct 2012, Jiri Kosina wrote:
>>
>>>> My vote is a clear 3. It took me a few years to kick all users (as
>>>> opposed to implementers) of i2c from drivers/i2c and finding them a
>>>> proper home, I'm not going to accept new intruders. Grouping drivers
>>>> according to what they implement makes it a lot easier to share code
>>>> and ideas between related drivers. If you want to convince yourself,
>>>> just imagine the mess it would be if all drivers for PCI devices lived
>>>> under drivers/pci.
>>>
>>> This is more or less consistent with my original opinion when I was
>>> refactoring the HID layer out of the individual drivers a few years ago.
>>>
>>> But Marcel objected that he wants to keep all the bluetooth-related
>>> drivers under net/bluetooth, and I didn't really want to push hard against
>>> this, because I don't have really super-strong personal preference either
>>> way.
>>>
>>> But we definitely can use this oportunity to bring this up for discussion
>>> again.
>>
>> Basically, to me this all boils down to the question -- what is more
>> important: low-level transport being used, or the general function of the
>> device?
>>
>> To me, it's the latter, and as such, everything would belong under
>> drivers/hid.
>
> Then shouldn't is be drivers/input, rather?

Ouch, it will introduce more and more complexity.

It seems that hid transport layers should go in drivers/hid.
However, I don't like mixing the transport layer and the final
drivers. Maybe this is the time to rework a little bit the tree.
To minimize the moves, we could introduce:
drivers/hid/busses/usb
drivers/hid/busses/i2c
drivers/hid/busses/bluetooth

Cheers,
Benjamin

>
> St.
>
>
--
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
Stéphane Chatty Oct. 7, 2012, 4:20 p.m. UTC | #21
Le 7 oct. 2012 à 18:07, Benjamin Tissoires a écrit :
>>> 
>>> Basically, to me this all boils down to the question -- what is more
>>> important: low-level transport being used, or the general function of the
>>> device?
>>> 
>>> To me, it's the latter, and as such, everything would belong under
>>> drivers/hid.
>> 
>> Then shouldn't is be drivers/input, rather?
> 
> Ouch, it will introduce more and more complexity.

Purely rhetorical question, I agree. But still.

> 
> It seems that hid transport layers should go in drivers/hid.
> However, I don't like mixing the transport layer and the final
> drivers. Maybe this is the time to rework a little bit the tree.
> To minimize the moves, we could introduce:
> drivers/hid/busses/usb
> drivers/hid/busses/i2c
> drivers/hid/busses/bluetooth

What about creating drivers/hid/core and move all generic stuff there? That is:
drivers/hid/core
drivers/hid/usb
drivers/hid/i2c
drivers/hid/bluetooth

Cheers,

St.


--
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
Benjamin Tissoires Oct. 7, 2012, 4:57 p.m. UTC | #22
Hello Jean,

many thanks for this detailed review. I agree to almost all of your
comments, so I didn't add an 'ok' after each remark.
This review will give me some work, but the driver will be much better.

On Sun, Oct 7, 2012 at 4:28 PM, Jean Delvare <khali@linux-fr.org> wrote:
> Salut Benjamin,
>
> On Fri, 14 Sep 2012 15:41:43 +0200, benjamin.tissoires wrote:
>> From: Benjamin Tissoires <benjamin.tissoires@enac.fr>
>>
>> Microsoft published the protocol specification of HID over i2c:
>> http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
>>
>> This patch introduces an implementation of this protocol.
>>
>> This implementation does not includes the ACPI part of the specification.
>> This will come when ACPI 5.0 devices will be available.
>>
>> Once the ACPI part will be done, OEM will not have to declare HID over I2C
>> devices in their platform specific driver.
>>
>> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
>> ---
>>
>> Hi,
>>
>> this is finally my first implementation of HID over I2C.
>>
>> This has been tested on an Elan Microelectronics HID over I2C device, with
>> a Samsung Exynos 4412 board.
>>
>> Any comments are welcome.
>
> Code review follows. It is by no way meant to be complete, as I don't
> know a thing about HID. I hope you'll find it useful nevertheless.
>
>>
>> Cheers,
>> Benjamin
>>
>>  drivers/i2c/Kconfig         |    8 +
>>  drivers/i2c/Makefile        |    1 +
>>  drivers/i2c/i2c-hid.c       | 1027 +++++++++++++++++++++++++++++++++++++++++++
>>  include/linux/i2c/i2c-hid.h |   35 ++
>>  4 files changed, 1071 insertions(+)
>>  create mode 100644 drivers/i2c/i2c-hid.c
>>  create mode 100644 include/linux/i2c/i2c-hid.h
>>
>> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
>> index 5a3bb3d..5adf65a 100644
>> --- a/drivers/i2c/Kconfig
>> +++ b/drivers/i2c/Kconfig
>> @@ -47,6 +47,14 @@ config I2C_CHARDEV
>>         This support is also available as a module.  If so, the module
>>         will be called i2c-dev.
>>
>> +config I2C_HID
>> +     tristate "HID over I2C bus"
>
> You are definitely missing dependencies here, CONFIG_HID at least.
>
>> +     help
>> +       Say Y here to use the HID over i2c protocol implementation.
>> +
>> +       This support is also available as a module.  If so, the module
>> +       will be called i2c-hid.
>> +
>>  config I2C_MUX
>>       tristate "I2C bus multiplexing support"
>>       help
>> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
>> index beee6b2..8f38116 100644
>> --- a/drivers/i2c/Makefile
>> +++ b/drivers/i2c/Makefile
>> @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_BOARDINFO)   += i2c-boardinfo.o
>>  obj-$(CONFIG_I2C)            += i2c-core.o
>>  obj-$(CONFIG_I2C_SMBUS)              += i2c-smbus.o
>>  obj-$(CONFIG_I2C_CHARDEV)    += i2c-dev.o
>> +obj-$(CONFIG_I2C_HID)                += i2c-hid.o
>>  obj-$(CONFIG_I2C_MUX)                += i2c-mux.o
>>  obj-y                                += algos/ busses/ muxes/
>>
>> diff --git a/drivers/i2c/i2c-hid.c b/drivers/i2c/i2c-hid.c
>> new file mode 100644
>> index 0000000..eb17d8c
>> --- /dev/null
>> +++ b/drivers/i2c/i2c-hid.c
>> @@ -0,0 +1,1027 @@
>> +/*
>> + * HID over I2C protocol implementation
>> + *
>> + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
>> + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
>> + *
>> + * This code is partly based on "USB HID support for Linux":
>> + *
>> + *  Copyright (c) 1999 Andreas Gal
>> + *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
>> + *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
>> + *  Copyright (c) 2007-2008 Oliver Neukum
>> + *  Copyright (c) 2006-2010 Jiri Kosina
>> + *
>> + * This file is subject to the terms and conditions of the GNU General Public
>> + * License.  See the file COPYING in the main directory of this archive for
>> + * more details.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/i2c.h>
>> +#include <linux/irq.h>
>
> I don't think you need to include that one, everything irq-related you
> need comes with <linux/interrupt.h> below. The header comment in that
> file actually says: "Please do not include this file in generic code."
>
>> +#include <linux/gpio.h>
>
> Your driver makes no use of GPIO.
>
>> +#include <linux/interrupt.h>
>> +#include <linux/input.h>
>> +#include <linux/delay.h>
>> +#include <linux/slab.h>
>> +#include <linux/pm.h>
>
> You are missing <linux/spinlock.h> for
> spin_lock_irq()/spin_unlock_irq(), <linux/device.h> for
> device_may_wakeup(), <linux/wait.h> for wait_event_timeout(),
> <linux/err.h> for IS_ERR(), <linux/string.h> for strcat()/memcpy(),
> <linux/list.h> for list_for_each_entry() and <linux/jiffies.h> for
> msecs_to_jiffies(). I'd suggest including <linux/kernel.h> as well, for
> sprintf() if nothing else, and <linux/bug.h> for WARN_ON().

It seems like I've been to lazy with the includes... Thanks!

>
>> +
>> +#include <linux/i2c/i2c-hid.h>
>> +
>> +#include <linux/hid.h>
>> +#include <linux/hiddev.h>
>> +#include <linux/hidraw.h>
>
> I don't think you using anything from <linux/hidraw.h>, do you?

oops...

>
>> +
>> +#define DRIVER_NAME          "i2chid"
>> +#define DRIVER_DESC          "HID over I2C core driver"
>
> I see little interest in this define, as you're only using it once.
> Even DRIVER_NAME isn't so useful, 2 of the 3 occurrences would be
> replaced with client->name.&
>
>> +
>> +#define I2C_HID_COMMAND_TRIES        3
>> +
>> +/* flags */
>> +#define I2C_HID_STARTED              (1 << 0)
>> +#define I2C_HID_OUT_RUNNING  (1 << 1)
>> +#define I2C_HID_IN_RUNNING   (1 << 2)
>> +#define I2C_HID_RESET_PENDING        (1 << 3)
>> +#define I2C_HID_SUSPENDED    (1 << 4)
>
> 3 of these are never used, so why define them?

This seems to came out from the different pre-releases of the patch.
I'll have a look at them and keep the right ones.

>
>> +
>> +#define I2C_HID_PWR_ON               0x00
>> +#define I2C_HID_PWR_SLEEP    0x01
>> +
>> +/* debug option */
>> +static bool debug = false;
>
> checkpatch.pl says:
>
> ERROR: do not initialise statics to 0 or NULL
> #206: FILE: drivers/i2c/i2c-hid.c:52:
> +static bool debug = false;

Yep, I saw that, but I was not confident in putting this option
uninitialized. I think it's better to keep it initialized.
However, I'll see if I keep it at the end with the refactoring of the
debug system.

>
>
>> +module_param(debug, bool, 0444);
>> +MODULE_PARM_DESC(debug, "print a lot of debug informations");
>
> Spelling: information (in English, "information" is already a plural.)
>
>> +
>> +struct i2chid_desc {
>> +     __le16 wHIDDescLength;
>> +     __le16 bcdVersion;
>> +     __le16 wReportDescLength;
>> +     __le16 wReportDescRegister;
>> +     __le16 wInputRegister;
>> +     __le16 wMaxInputLength;
>> +     __le16 wOutputRegister;
>> +     __le16 wMaxOutputLength;
>> +     __le16 wCommandRegister;
>> +     __le16 wDataRegister;
>> +     __le16 wVendorID;
>> +     __le16 wProductID;
>> +     __le16 wVersionID;
>> +} __packed;
>> +
>> +struct i2chid_cmd {
>> +     enum {
>> +             /* fecth HID descriptor */
>
> Typo: fetch
>
>> +             HID_DESCR_CMD,
>> +
>> +             /* fetch report descriptors */
>> +             HID_REPORT_DESCR_CMD,
>> +
>> +             /* commands */
>> +             HID_RESET_CMD,
>> +             HID_GET_REPORT_CMD,
>> +             HID_SET_REPORT_CMD,
>> +             HID_GET_IDLE_CMD,
>> +             HID_SET_IDLE_CMD,
>> +             HID_GET_PROTOCOL_CMD,
>> +             HID_SET_PROTOCOL_CMD,
>> +             HID_SET_POWER_CMD,
>> +
>> +             /* read/write data register */
>> +             HID_DATA_CMD,
>> +     } name;
>> +     unsigned int registerIndex;
>> +     __u8 opcode;
>> +     unsigned int length;
>> +     bool wait;
>> +};
>> +
>> +union command {
>> +     u8 data[0];
>> +     struct cmd {
>> +             __le16 reg;
>> +             __u8 reportTypeID;
>> +             __u8 opcode;
>> +     } __packed c;
>> +};
>> +
>> +#define I2C_HID_CMD(name_, opcode_) \
>> +     .name = name_, .opcode = opcode_, .length = 4, \
>> +     .registerIndex = offsetof(struct i2chid_desc, wCommandRegister)
>> +
>> +static const struct i2chid_cmd cmds[] = {
>> +     { I2C_HID_CMD(HID_RESET_CMD,            0x01), .wait = true },
>> +     { I2C_HID_CMD(HID_GET_REPORT_CMD,       0x02) },
>> +     { I2C_HID_CMD(HID_SET_REPORT_CMD,       0x03) },
>> +     { I2C_HID_CMD(HID_GET_IDLE_CMD,         0x04) },
>> +     { I2C_HID_CMD(HID_SET_IDLE_CMD,         0x05) },
>> +     { I2C_HID_CMD(HID_GET_PROTOCOL_CMD,     0x06) },
>> +     { I2C_HID_CMD(HID_SET_PROTOCOL_CMD,     0x07) },
>> +     { I2C_HID_CMD(HID_SET_POWER_CMD,        0x08) },
>> +
>> +     {.name = HID_DESCR_CMD,
>> +      .length = 2},
>> +
>> +     {.name = HID_REPORT_DESCR_CMD,
>> +      .registerIndex = offsetof(struct i2chid_desc, wReportDescRegister),
>> +      .opcode = 0x00,
>> +      .length = 2},
>> +
>> +     {.name = HID_DATA_CMD,
>> +      .registerIndex = offsetof(struct i2chid_desc, wDataRegister),
>> +      .opcode = 0x00,
>> +      .length = 2},
>
> Please use consistent spacing.
>
>> +
>> +     {}, /* terminating */
>
> I suspect it would be more efficient to not terminate the array and use
> ARRAY_SIZE() when needed.
>
>> +};
>> +
>> +/* The main device structure */
>> +struct i2chid {
>> +     struct i2c_client       *client;        /* i2c client */
>> +     struct hid_device       *hid;   /* pointer to corresponding HID dev */
>> +     union {
>> +             __u8 hdesc_buffer[sizeof(struct i2chid_desc)];
>> +             struct i2chid_desc hdesc;       /* the HID Descriptor */
>> +     };
>> +     __le16                  wHIDDescRegister; /* location of the i2c
>> +                                                * register of the HID
>> +                                                * descriptor. */
>> +     unsigned int            bufsize;        /* i2c buffer size */
>> +     char                    *inbuf;         /* Input buffer */
>> +
>> +     spinlock_t              flock;          /* flags spinlock */
>> +     unsigned long           flags;          /* device flags */
>> +
>> +     int                     irq;            /* the interrupt line irq */
>> +
>> +     wait_queue_head_t       wait;           /* For waiting the interrupt */
>> +};
>> +
>> +void i2chid_print_buffer(struct i2chid *ihid, u8 *buf, unsigned int n)
>
> Should be static and buf should be declared const.
>
>> +{
>> +     int i;
>> +     char *string = kzalloc((n*3+1)*sizeof(char), GFP_KERNEL);
>
> This could fail, you have to check for it.
>
>> +     char tmpbuf[4] = "";
>
> Useless initialization.
>
>> +
>> +     for (i = 0; i < n; ++i) {
>> +             sprintf(tmpbuf, "%02x ", buf[i] & 0xFF);
>
> Useless mask.
>
>> +             strcat(string, tmpbuf);
>> +     }
>> +
>> +     dev_err(&ihid->client->dev, "%s\n", string);
>> +     kfree(string);
>> +}
>
> This is horribly inefficient. I don't know what usual value you expect
> for "n", but the above algorithm has a O(n^2) complexity for no good
> reason. As a rule of thumb, every time you use strcat() in a loop, you
> can be sure you did something wrong ;)
>
> The efficient way of doing the above is to remember where you are in
> the output buffer, and sprintf at this location directly:
>
>         int i;
>         char *string = kzalloc((n*3+1)*sizeof(char), GFP_KERNEL);
>         char *p = string;
>
>         for (i = 0; i < n; ++i)
>                 p += sprintf(p, "%02x ", buf[i]);
>
> I would also suggest that you put some introduction work in the log
> message, otherwise it looks like mere garbage. Doing so will also give
> you the opportunity to put the spaces before the numbers rather than
> after them, so you don't print a useless trailing space.

This is all right and I appreciate the explanations.
Nevertheless, Dmitry proposed a much better way to write my debugging
outputs with dev_XXX(&ihid->client->dev, "%*ph\n", n, buf); No sprintf
required anymore.

>
>> +
>> +static int __i2chid_command(struct i2c_client *client, int command, u8 reportID,
>> +             u8 reportType, u8 *args, int args_len,
>> +             unsigned char *buf_recv, int data_len)
>> +{
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     union command *cmd;
>> +     unsigned char *rec_buf = buf_recv;
>
> Useless variable, use buf_recv directly.
>
>> +     int ret;
>> +     int tries = I2C_HID_COMMAND_TRIES;
>> +     int length = 0;
>> +     struct i2c_msg msg[2];
>> +     int msg_num = 0;
>> +     int i;
>> +     bool wait = false;
>> +
>> +     if (WARN_ON(!ihid))
>> +             return -ENODEV;
>> +
>> +     cmd = kmalloc(args_len + sizeof(union command), GFP_KERNEL);
>
> Your intent would be clearer with sizeof(union command) + args_len.
> Also, what excuse do you have for not using kzalloc?

None.... :-(
And above all, I think I have already been confronted to the problem
of a non-filled buffer with almost good values.

>
>> +     if (!cmd)
>> +             return -ENOMEM;
>> +
>> +     for (i = 0; cmds[i].length; i++) {
>> +             unsigned int registerIndex;
>> +
>> +             if (cmds[i].name != command)
>> +                     continue;
>> +
>> +             registerIndex = cmds[i].registerIndex;
>> +             cmd->data[0] = ihid->hdesc_buffer[registerIndex];
>> +             cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1];
>> +             cmd->c.opcode = cmds[i].opcode;
>> +             length = cmds[i].length;
>> +             wait = cmds[i].wait;
>> +     }
>
> This again is horribly inefficient. Just order the commands properly in
> cmds[] and you can access them by index. This will be much faster.
>
>> +
>> +     /* special case for HID_DESCR_CMD */
>> +     if (command == HID_DESCR_CMD)
>> +             cmd->c.reg = ihid->wHIDDescRegister;
>> +
>> +     cmd->c.reportTypeID = reportID | reportType << 4;
>> +
>> +     memcpy(cmd->data + length, args, args_len);
>> +     length += args_len;
>> +
>> +     if (debug) {
>> +             char tmpstr[4] = "";
>> +             char strbuf[50] = "";
>> +             int i;
>> +             for (i = 0; i < length; i++) {
>> +                     sprintf(tmpstr, "%02x ", cmd->data[i] & 0xFF);
>> +                     strcat(strbuf, tmpstr);
>> +             }
>> +             dev_err(&client->dev, "%s, cmd=%s\n", __func__, strbuf);
>> +     }
>
> AFAICS this is mostly duplicating i2chid_print_buffer().
>
>> +
>> +     msg[0].addr = client->addr;
>> +     msg[0].flags = client->flags & I2C_M_TEN;
>> +     msg[0].len = length;
>> +     msg[0].buf = (char *) cmd->data;
>
> Useless cast.
>
>> +     msg[1].addr = client->addr;
>> +     msg[1].flags = client->flags & I2C_M_TEN;
>> +     msg[1].flags |= I2C_M_RD;
>> +     msg[1].len = data_len;
>> +     msg[1].buf = rec_buf;
>> +
>> +     msg_num = data_len > 0 ? 2 : 1;
>
> If !data_len you have been initializing 4 fields for nothing right
> above. Test first and you'll make your code faster for that specific
> case.
>
>> +
>> +     if (wait) {
>> +             spin_lock_irq(&ihid->flock);
>> +             set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
>> +             spin_unlock_irq(&ihid->flock);
>> +     }
>> +
>> +     do {
>> +             ret = i2c_transfer(client->adapter, msg, msg_num);
>> +             if (ret > 0)
>
> Success here is ret == msg_num, not ret > 0.
>
>> +                     break;
>> +             tries--;
>> +             dev_dbg(&client->dev, "retrying i2chid_command:%d (%d)\n",
>> +                     command, tries);
>> +     } while (tries > 0);
>> +
>> +     if (wait && ret > 0) {
>> +             if (debug)
>> +                     dev_err(&client->dev, "%s: waiting....\n", __func__);
>
> Ellipsis is usually 3 dots not 4.
>
>> +             if (!wait_event_timeout(ihid->wait,
>> +                             !test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
>> +                             msecs_to_jiffies(5000)))
>> +                     ret = -ENODATA;
>> +             if (debug)
>> +                     dev_err(&client->dev, "%s: finished.\n", __func__);
>> +     }
>> +
>> +     kfree(cmd);
>> +
>> +     return ret > 0 ? ret != msg_num : ret;
>
> Makes no sense to me. You should return 0 if ret == msg_num, and a
> negative error code otherwise.
>
>> +}
>> +
>> +static int i2chid_command(struct i2c_client *client, int command,
>> +             unsigned char *buf_recv, int data_len)
>> +{
>> +     return __i2chid_command(client, command, 0, 0, NULL, 0,
>> +                             buf_recv, data_len);
>> +}
>> +
>> +static int i2chid_get_report(struct i2c_client *client, u8 reportType,
>> +             u32 reportID, unsigned char *buf_recv, int data_len)
>> +{
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     u8 args[6];
>> +     int ret;
>> +     int args_len = 0;
>> +     u16 readRegister = ihid->hdesc.wDataRegister;
>
> This is missing le16_to_cpu().

I agree this is awful, but not putting it allows me to not have to
check the endianness when I'm using it.
But I may be totally wrong on this.

>
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "%s\n", __func__);
>> +
>> +     if (reportID >= 15) {
>
> Mixing decimal and hexadecimal is confusing.
>
>> +             args[args_len++] = (reportID >> 0) & 0xFF;
>> +             args[args_len++] = (reportID >> 8) & 0xFF;
>> +             args[args_len++] = (reportID >> 16) & 0xFF;
>> +             args[args_len++] = (reportID >> 24) & 0xFF;
>> +             reportID = 0x0F;
>> +     }
>> +
>> +     args[args_len++] = readRegister & 0xff;
>> +     args[args_len++] = (readRegister >> 8) & 0xff;
>
> Useless mask. Please also use consistent case for hexadecimal values.
>
>> +
>> +     ret = __i2chid_command(client, HID_GET_REPORT_CMD, reportID & 0x0F,
>
> Useless mask.
>
>> +             reportType, args, args_len, buf_recv, data_len);
>> +     if (ret) {
>> +             dev_err(&client->dev, "HID_GET_REPORT_CMD Fail\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int i2chid_set_report(struct i2c_client *client, u8 reportType,
>> +             u32 reportID, unsigned char *buf, int data_len)
>> +{
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     u8 *args;
>> +     int ret;
>> +     u16 dataRegister = ihid->hdesc.wDataRegister;
>
> Missing conversion again.
>
>> +     int args_len =  (reportID >= 15 ? 4 : 0) +
>> +                     2 /* dataRegister */ +
>> +                     2 /* size */ +
>
> /* data_len */ actually.
>
>> +                     data_len;
>> +     int index = 0;
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "%s\n", __func__);
>> +
>> +     args = kmalloc(args_len, GFP_KERNEL);
>
> This should be kzalloc() and you should check for failure.
>
>> +
>> +     if (reportID >= 15) {
>> +             args[index++] = (reportID >> 0) & 0xFF;
>> +             args[index++] = (reportID >> 8) & 0xFF;
>> +             args[index++] = (reportID >> 16) & 0xFF;
>> +             args[index++] = (reportID >> 24) & 0xFF;
>> +             reportID = 0x0F;
>> +     }
>> +
>> +     args[index++] = dataRegister & 0xff;
>> +     args[index++] = (dataRegister >> 8) & 0xff;
>> +
>> +     args[index++] = data_len & 0xff;
>> +     args[index++] = (data_len >> 8) & 0xff;
>
> data_len is an int, there's no guarantee it fits on 2 bytes.
>
>> +
>> +     memcpy(&args[index], buf, data_len);
>> +
>> +     ret = __i2chid_command(client, HID_SET_REPORT_CMD, reportID & 0x0F,
>> +             reportType, args, args_len, NULL, 0);
>> +     if (ret) {
>> +             dev_err(&client->dev, "HID_SET_REPORT_CMD Fail\n");
>
> Memory leak!

ouch

>
>> +             return -EINVAL;
>> +     }
>> +
>> +     kfree(args);
>> +     return data_len;
>> +}
>> +
>> +static int i2chid_set_power(struct i2c_client *client, int power_state)
>> +{
>> +     int ret;
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "%s\n", __func__);
>> +
>> +     ret = __i2chid_command(client, HID_SET_POWER_CMD, power_state & 0x01,
>
> Useless mask.
>
>> +             0, NULL, 0, NULL, 0);
>> +     if (ret) {
>> +             dev_err(&client->dev, "HID_SET_POWER_CMD Fail\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int i2chid_hwreset(struct i2c_client *client)
>> +{
>> +     uint8_t buf_recv[79];
>
> Why uint8_t here when you used unsigned char everywhere else? Why 79
> when you only need 2 bytes?

This may comes from the device I have which sends input reports of 78
or 79 length... Useless.

>
>> +     int ret;
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "%s\n", __func__);
>> +
>> +     ret = i2chid_set_power(client, I2C_HID_PWR_ON);
>> +     if (ret)
>> +             return -EINVAL;
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "resetting...\n");
>> +
>> +     ret = i2chid_command(client, HID_RESET_CMD, buf_recv, 0);
>
> I don't see the point of passing buf_recv here.
>
>> +     if (ret) {
>> +             dev_err(&client->dev, "HID_RESET_CMD Fail\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     ret = i2c_master_recv(client, buf_recv, 2);
>> +     if (ret != 2 || (buf_recv[0] != 0 && buf_recv[1] != 0)) {
>
> If I read the datasheet correctly, the device must reply with two 0
> bytes after reset, so the above test is wrong, it should be
> buf_recv[0] != 0 || buf_recv[1] != 0 (||, not &&.)
>
>> +             dev_err(&client->dev,
>> +                     "HID_RESET_CMD Failed: got %02x %02x, size=%d\n",
>> +                     buf_recv[0], buf_recv[1], ret);
>
> If size < 2, you are printing uninitialized bytes from the stack, which
> is bad.
>
>> +
>> +             i2chid_set_power(client, I2C_HID_PWR_SLEEP);
>> +             return -EINVAL;
>> +     }
>> +     return 0;
>> +}
>> +
>> +static bool i2chid_get_input(struct i2chid *ihid)
>
> Very confusing return convention, given that every other function in
> this driver returns 0 on success.
>
>> +{
>> +     int ret;
>> +     int size = ihid->hdesc.wMaxInputLength;
>
> Missing conversion.
>
>> +
>> +     ret = i2c_master_recv(ihid->client, ihid->inbuf, size);
>> +     if (ret != size) {
>> +             dev_err(&ihid->client->dev, "%s: got %d data instead of %d\n",
>> +                     __func__, ret, size);
>> +             return false;
>> +     }
>> +
>> +     if (debug)
>> +             i2chid_print_buffer(ihid, ihid->inbuf, size);
>> +
>> +     ret = hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2,
>> +                     size - 2, 1);
>> +     if (ret)
>> +             return false;
>> +
>> +     return true;
>> +}
>> +
>> +static irqreturn_t i2chid_irq(int irq, void *dev_id)
>> +{
>> +     struct i2chid *ihid = dev_id;
>> +     int ready;
>> +
>> +     if (!ihid)
>> +             return IRQ_HANDLED;
>
> You'll have to explain that one to me. This just can't happen, can it?

Yep, this can't happen. Dmitry already spot it.

>
>> +
>> +     spin_lock_irq(&ihid->flock);
>> +     if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags)) {
>> +             spin_unlock_irq(&ihid->flock);
>> +
>> +             wake_up(&ihid->wait);
>> +             return IRQ_HANDLED;
>> +     }
>> +
>> +     ready = test_bit(I2C_HID_STARTED, &ihid->flags);
>> +     spin_unlock_irq(&ihid->flock);
>> +
>> +     if (ready)
>> +             i2chid_get_input(ihid);
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static int i2chid_get_report_length(struct hid_report *report)
>> +{
>> +     return ((report->size - 1) >> 3) + 1 +
>> +             report->device->report_enum[report->type].numbered + 2;
>> +}
>> +
>> +void i2chid_init_report(struct hid_report *report)
>
> Should be static.
>
>> +{
>> +     struct hid_device *hid = report->device;
>> +     struct i2c_client *client = hid->driver_data;
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     unsigned int size, ret_size;
>> +
>> +     size = i2chid_get_report_length(report);
>> +     i2chid_get_report(client,
>> +                     report->type == HID_FEATURE_REPORT ? 0x03 : 0x01,
>> +                     report->id, ihid->inbuf, size);
>> +
>> +     if (debug)
>> +             i2chid_print_buffer(ihid, ihid->inbuf, size);
>> +
>> +     ret_size = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
>> +
>> +     if (ret_size != size) {
>> +             dev_err(&client->dev, "error in %s size:%d / ret_size:%d\n",
>> +                     __func__, size, ret_size);
>> +             return;
>> +     }
>> +
>> +     /* hid->driver_lock is held as we are in probe function,
>> +      * we just need to setup the input fields, so using
>> +      * hid_report_raw_event is safe. */
>> +     hid_report_raw_event(hid, report->type, ihid->inbuf + 2, size - 2, 1);
>> +}
>> +
>> +/*
>> + * Initialize all reports
>> + */
>> +void i2chid_init_reports(struct hid_device *hid)
>
> Should be static.
>
>> +{
>> +     struct hid_report *report;
>> +
>> +     list_for_each_entry(report,
>> +             &hid->report_enum[HID_INPUT_REPORT].report_list, list)
>> +             i2chid_init_report(report);
>> +
>> +     list_for_each_entry(report,
>> +             &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
>> +             i2chid_init_report(report);
>> +}
>> +
>> +/*
>> + * Traverse the supplied list of reports and find the longest
>> + */
>> +static void i2chid_find_max_report(struct hid_device *hid, unsigned int type,
>> +             unsigned int *max)
>> +{
>> +     struct hid_report *report;
>> +     unsigned int size;
>> +
>> +     /* We should not rely on wMaxInputLength, as some devices may set it to
>> +      * a wrong length. */
>> +     list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
>> +             size = i2chid_get_report_length(report);
>> +             if (*max < size)
>> +                     *max = size;
>> +     }
>> +}
>> +
>> +static int i2chid_alloc_buffers(struct i2chid *ihid)
>> +{
>> +     ihid->inbuf = kzalloc(ihid->bufsize, GFP_KERNEL);
>> +
>> +     if (!ihid->inbuf)
>> +             return -1;
>
> -ENOMEM would be much cleaner.
>
>> +
>> +     return 0;
>> +}
>> +
>> +static void i2chid_free_buffers(struct i2chid *ihid)
>> +{
>> +     kfree(ihid->inbuf);
>> +}
>> +
>> +static int i2chid_get_raw_report(struct hid_device *hid,
>> +             unsigned char report_number, __u8 *buf, size_t count,
>> +             unsigned char report_type)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     int ret;
>> +
>> +     if (count > ihid->bufsize)
>> +             count = ihid->bufsize;
>> +
>> +     ret = i2chid_get_report(client,
>> +                     report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
>> +                     report_number, ihid->inbuf, count);
>> +
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     count = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
>> +
>> +     memcpy(buf, ihid->inbuf + 2, count);
>> +
>> +     return count;
>> +}
>> +
>> +static int i2chid_output_raw_report(struct hid_device *hid, __u8 *buf,
>> +             size_t count, unsigned char report_type)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     int ret;
>> +     int report_id = buf[0];
>> +
>> +     ret = i2chid_set_report(client,
>> +                             report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
>> +                             report_id, buf, count);
>> +
>> +     return ret;
>> +
>> +}
>> +
>> +static int i2chid_fetch_hid_descriptor(struct i2chid *ihid)
>
> Only called in probe function, so can be __devinit. It would be better
> to place both functions close to each other BTW.
>
>> +{
>> +     struct i2c_client *client = ihid->client;
>> +     struct i2chid_desc *hdesc = &ihid->hdesc;
>> +     unsigned int dsize = 0;
>
> Useless initialization.
>
>> +     int ret;
>> +
>> +     /* Fetch the length of HID description, retrieve the 4 first bytes:
>> +      * bytes 0-1 -> length
>> +      * bytes 2-3 -> bcdVersion (has to be 1.00) */
>> +     ret = i2chid_command(client, HID_DESCR_CMD, ihid->hdesc_buffer, 4);
>> +
>> +     if (debug)
>> +             dev_err(&client->dev,
>> +                     "%s, ihid->hdesc_buffer: %02x %02x %02x %02x\n",
>> +                     __func__,
>> +                     ihid->hdesc_buffer[0],
>> +                     ihid->hdesc_buffer[1],
>> +                     ihid->hdesc_buffer[2],
>> +                     ihid->hdesc_buffer[3]);
>> +
>> +     if (ret) {
>> +             dev_err(&client->dev, "HID_DESCR_LENGTH_CMD Fail (ret=%d)\n",
>> +                     ret);
>> +             return -EINVAL;
>
> These should all be -ENODEV in this function: the device isn't what you
> expected. EINVAL is for invalid argument.
>
>> +     }
>> +
>> +     dsize = le16_to_cpu(hdesc->wHIDDescLength);
>> +     if (!dsize || dsize > HID_MAX_DESCRIPTOR_SIZE) {
>> +             dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
>> +                     dsize);
>> +             return -EINVAL;
>> +     }
>> +
>> +     /* check bcdVersion == 1.0 */
>> +     if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
>> +             dev_err(&client->dev,
>> +                     "unexpected HID descriptor bcdVersion (0x%04x)\n",
>> +                     le16_to_cpu(hdesc->bcdVersion));
>
> So, the day revision 1.1 comes out and a device implements it, it won't
> be supported, even if revision 1.1 only made minor changes we can live
> without. Is it smart?

Smart, maybe not. But if the 1.1 made minor changes in the HID
descriptor by moving fields, we may end with many surprises.
As we have no guarantees on what will be that norm, I'd rather wait
for it to come out and make a small patch that eventually allows it.

I agree that if we have much guarantees from Microsoft that they won't
break backward compatibility, we could just raise a  warning and
continue.
But as long as the standard is published by Microsoft, lead by
Microsoft and that the only 'valid' implementation is maintained by
Microsoft, I'd rather not bet on the future.

BTW, this is my only opinion, and ifpeople think we can continue if
the bcd version is not 1.00, I'm fine with it.

>
>> +             return -EINVAL;
>> +     }
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "Fetching the HID descriptor\n");
>> +
>> +     ret = i2chid_command(client, HID_DESCR_CMD, ihid->hdesc_buffer, dsize);
>> +     if (ret) {
>> +             dev_err(&client->dev, "HID_DESCR_CMD Fail\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     if (debug)
>> +             i2chid_print_buffer(ihid, ihid->hdesc_buffer, dsize);
>> +
>> +     return 0;
>> +}
>> +
>> +static int i2chid_parse(struct hid_device *hid)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     struct i2chid_desc *hdesc = &ihid->hdesc;
>> +     unsigned int rsize = 0;
>
> Useless initialization.
>
>> +     char *rdesc;
>> +     int ret;
>> +     int tries = 3;
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "entering %s\n", __func__);
>> +
>> +     rsize = le16_to_cpu(hdesc->wReportDescLength);
>> +     if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
>> +             dbg_hid("weird size of report descriptor (%u)\n", rsize);
>> +             return -EINVAL;
>> +     }
>> +
>> +     do {
>> +             ret = i2chid_hwreset(client);
>> +             if (ret)
>> +                     msleep(1000);
>> +     } while (tries-- > 0 && ret);
>> +
>> +     if (ret)
>> +             return ret;
>> +
>> +     rdesc = kmalloc(rsize, GFP_KERNEL);
>> +
>> +     if (!rdesc) {
>> +             dbg_hid("couldn't allocate rdesc memory\n");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "asking HID report descriptor\n");
>> +
>> +     ret = i2chid_command(client, HID_REPORT_DESCR_CMD, rdesc, rsize);
>> +     if (ret) {
>> +             hid_err(hid, "reading report descriptor failed\n");
>> +             kfree(rdesc);
>> +             ret = -ENOMEM;
>
> No.
>
>> +             goto err;
>> +     }
>> +
>> +     if (debug)
>> +             i2chid_print_buffer(ihid, rdesc, rsize);
>> +
>> +     ret = hid_parse_report(hid, rdesc, rsize);
>> +     kfree(rdesc);
>> +     if (ret) {
>> +             dbg_hid("parsing report descriptor failed\n");
>> +             goto err;
>> +     }
>> +
>> +     return 0;
>> +err:
>> +     return ret;
>
> This label makes no sense, just replace gotos by returns.
>
>> +}
>> +
>> +static int i2chid_start(struct hid_device *hid)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     int ret;
>> +
>> +     ihid->bufsize = HID_MIN_BUFFER_SIZE;
>> +     i2chid_find_max_report(hid, HID_INPUT_REPORT, &ihid->bufsize);
>> +     i2chid_find_max_report(hid, HID_OUTPUT_REPORT, &ihid->bufsize);
>> +     i2chid_find_max_report(hid, HID_FEATURE_REPORT, &ihid->bufsize);
>> +
>> +     if (ihid->bufsize > HID_MAX_BUFFER_SIZE)
>> +             ihid->bufsize = HID_MAX_BUFFER_SIZE;
>
> Won't this result in issues later?

By re-reading the code, yes. I will change it.

>
>> +
>> +     if (i2chid_alloc_buffers(ihid)) {
>> +             ret = -ENOMEM;
>> +             goto fail;
>> +     }
>> +
>> +     if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
>> +             i2chid_init_reports(hid);
>> +
>> +     return 0;
>> +
>> +fail:
>> +     i2chid_free_buffers(ihid);
>
> This is wrong, you're freeing buffers you failed to allocate.

ouch

>
>> +     return ret;
>> +}
>> +
>> +static void i2chid_stop(struct hid_device *hid)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +
>> +     if (WARN_ON(!ihid))
>> +             return;
>> +
>> +     hid->claimed = 0;
>> +
>> +     i2chid_free_buffers(ihid);
>> +}
>> +
>> +static int i2chid_open(struct hid_device *hid)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     int ret;
>> +
>> +     if (!hid->open++) {
>> +             ret = i2chid_set_power(client, I2C_HID_PWR_ON);
>> +             if (ret) {
>> +                     hid->open--;
>> +                     return -EIO;
>> +             }
>> +             spin_lock_irq(&ihid->flock);
>> +             set_bit(I2C_HID_STARTED, &ihid->flags);
>> +             spin_unlock_irq(&ihid->flock);
>> +     }
>> +     return 0;
>> +}
>> +
>> +static void i2chid_close(struct hid_device *hid)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +
>> +     /* protecting hid->open to make sure we don't restart
>> +      * data acquistion due to a resumption we no longer
>> +      * care about
>> +      */
>> +     if (!--hid->open) {
>> +             spin_lock_irq(&ihid->flock);
>> +             clear_bit(I2C_HID_STARTED, &ihid->flags);
>> +             spin_unlock_irq(&ihid->flock);
>> +
>> +             /* Save some power */
>> +             i2chid_set_power(client, I2C_HID_PWR_SLEEP);
>> +     }
>> +}
>> +
>> +static int i2chid_power(struct hid_device *hid, int lvl)
>> +{
>> +     struct i2c_client *client = hid->driver_data;
>> +     int r = 0;
>
> Why not "ret" like everywhere else?
>
>> +
>> +     if (debug)
>> +             dev_err(&client->dev, "%s lvl:%d\n", __func__, lvl);
>> +
>> +     switch (lvl) {
>> +     case PM_HINT_FULLON:
>> +             r = i2chid_set_power(client, I2C_HID_PWR_ON);
>> +             break;
>> +     case PM_HINT_NORMAL:
>> +             r = i2chid_set_power(client, I2C_HID_PWR_SLEEP);
>> +             break;
>> +     }
>> +     return r;
>> +}
>> +
>> +static int i2chid_hidinput_input_event(struct input_dev *dev, unsigned int type,
>> +             unsigned int code, int value)
>> +{
>> +     struct hid_device *hid = input_get_drvdata(dev);
>> +     struct hid_field *field;
>> +     int offset;
>> +
>> +     if (type == EV_FF)
>> +             return input_ff_event(dev, type, code, value);
>> +
>> +     if (type != EV_LED)
>> +             return -1;
>> +
>> +     offset = hidinput_find_field(hid, type, code, &field);
>> +
>> +     if (offset == -1) {
>> +             hid_warn(dev, "event field not found\n");
>> +             return -1;
>> +     }
>> +
>> +     hid_set_field(field, offset, value);
>> +
>> +     return 0;
>> +}
>> +
>> +static struct hid_ll_driver i2c_hid_driver = {
>> +     .parse = i2chid_parse,
>> +     .start = i2chid_start,
>> +     .stop = i2chid_stop,
>> +     .open = i2chid_open,
>> +     .close = i2chid_close,
>> +     .power = i2chid_power,
>> +     .hidinput_input_event = i2chid_hidinput_input_event,
>> +};
>> +
>> +static int i2chid_init_irq(struct i2c_client *client)
>
> Can be __devinit.
>
>> +{
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     int rc;
>
> Why not "ret" like everywhere else?
>
>> +
>> +     dev_dbg(&client->dev, "Requesting IRQ: %d\n",
>> +             client->irq);
>> +
>> +     rc = request_threaded_irq(client->irq, NULL, i2chid_irq,
>> +                     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
>> +                     DRIVER_NAME, ihid);
>> +     if (rc < 0) {
>> +             dev_dbg(&client->dev,
>> +                     "Could not register for %s interrupt, irq = %d,"
>> +                     " rc = %d\n",
>> +             DRIVER_NAME, client->irq, rc);
>> +
>> +             return rc;
>> +     }
>> +
>> +     return client->irq;
>> +}
>> +
>> +static int __devinit i2chid_probe(struct i2c_client *client,
>> +             const struct i2c_device_id *dev_id)
>> +{
>> +     int ret;
>> +     struct i2chid *ihid;
>> +     struct hid_device *hid;
>> +     __u16 hidRegister;
>
> You don't need this local variable.
>
>> +     unsigned char tmpstr[11];
>> +     struct i2chid_platform_data *platform_data = client->dev.platform_data;
>> +
>> +     dbg_hid("HID probe called for i2c %d\n", client->addr);
>
> I2C addresses are traditionally printed as hexadecimal values.
>
>> +
>> +     if (!platform_data) {
>> +             dev_err(&client->dev, "HID register address not provided\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     if (client->irq < 1) {
>> +             dev_err(&client->dev,
>> +                     "HID over i2c has not been provided an Int IRQ\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     ihid = kzalloc(sizeof(struct i2chid), GFP_KERNEL);
>> +     if (!ihid)
>> +             return -ENOMEM;
>> +
>> +     i2c_set_clientdata(client, ihid);
>> +
>> +     ihid->client = client;
>> +     ihid->flags = 0;
>
> flags are already 0 thanks to kzalloc.
>
>> +
>> +     hidRegister = platform_data->hid_descriptor_address;
>> +     ihid->wHIDDescRegister = cpu_to_le16(hidRegister);
>> +
>> +     init_waitqueue_head(&ihid->wait);
>> +     spin_lock_init(&ihid->flock);
>> +
>> +     ret = i2chid_fetch_hid_descriptor(ihid);
>> +     if (ret < 0)
>> +             goto err;
>> +
>> +     ihid->irq = i2chid_init_irq(client);
>> +     if (ihid->irq < 0)
>
> "ret" is set to 0 at this point, so you return 0 on error. Not good.

ouch

>
>> +             goto err;
>> +
>> +     hid = hid_allocate_device();
>> +     if (IS_ERR(hid)) {
>> +             ret = -ENOMEM;
>
> Use PTR_ERR().
>
>> +             goto err;
>> +     }
>> +
>> +     ihid->hid = hid;
>> +
>> +     hid->driver_data = client;
>> +     hid->ll_driver = &i2c_hid_driver;
>> +     hid->hid_get_raw_report = i2chid_get_raw_report;
>> +     hid->hid_output_raw_report = i2chid_output_raw_report;
>> +     hid->ff_init = hid_pidff_init;
>> +#ifdef CONFIG_USB_HIDDEV
>> +     hid->hiddev_connect = hiddev_connect;
>> +     hid->hiddev_disconnect = hiddev_disconnect;
>> +     hid->hiddev_hid_event = hiddev_hid_event;
>> +     hid->hiddev_report_event = hiddev_report_event;
>> +#endif
>> +     hid->dev.parent = &client->dev;
>> +     hid->bus = BUS_I2C;
>> +     hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
>> +     hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
>> +     hid->product = le16_to_cpu(ihid->hdesc.wProductID);
>> +
>> +     snprintf(tmpstr, sizeof(tmpstr), " %04X:%04X",
>> +              hid->vendor, hid->product);
>> +     strlcpy(hid->name, client->name, sizeof(hid->name));
>> +     strlcat(hid->name, tmpstr, sizeof(hid->name));
>
> Please use snprintf on hid->name directly, it's more efficient.
>
>> +
>> +     ret = hid_add_device(hid);
>> +     if (ret) {
>> +             if (ret != -ENODEV)
>> +                     hid_err(client, "can't add hid device: %d\n", ret);
>> +             goto err_mem_free;
>> +     }
>> +
>> +     return 0;
>> +
>> +err_mem_free:
>> +     hid_destroy_device(hid);
>> +
>> +err:
>> +     if (ihid->irq >= 0)
>> +             free_irq(ihid->irq, ihid);
>> +
>> +     kfree(ihid);
>> +     return ret;
>> +}
>> +
>> +static int __devexit i2chid_remove(struct i2c_client *client)
>> +{
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +     struct hid_device *hid;
>> +
>> +     if (WARN_ON(!ihid))
>> +             return -1;
>> +
>> +     hid = ihid->hid;
>> +     hid_destroy_device(hid);
>> +
>> +     free_irq(client->irq, ihid);
>> +
>> +     kfree(ihid);
>> +
>> +     return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int i2chid_suspend(struct device *dev)
>> +{
>> +     struct i2c_client *client = to_i2c_client(dev);
>> +     struct i2chid *ihid = i2c_get_clientdata(client);
>> +
>> +     if (device_may_wakeup(&client->dev))
>> +             enable_irq_wake(ihid->irq);
>> +
>> +     /* Save some power */
>> +     i2chid_set_power(client, I2C_HID_PWR_SLEEP);
>> +
>> +     return 0;
>> +}
>> +
>> +static int i2chid_resume(struct device *dev)
>> +{
>> +     int ret;
>> +     struct i2c_client *client = to_i2c_client(dev);
>> +
>> +     ret = i2chid_hwreset(client);
>> +     if (ret)
>> +             return ret;
>> +
>> +     if (device_may_wakeup(&client->dev))
>> +             enable_irq_wake(client->irq);
>
> All other drivers I checked call disable_irq_wake() here.

Yep. Seems like a bad copy/paste. I call the very same function in
i2chid_suspend, so definitely a mistake.

>
>> +
>> +     return 0;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(i2chid_pm, i2chid_suspend, i2chid_resume);
>> +
>> +static const struct i2c_device_id i2chid_id_table[] = {
>> +     { "i2chid", 0 },
>> +     { },
>> +};
>> +MODULE_DEVICE_TABLE(i2c, i2chid_id_table);
>> +
>> +
>> +static struct i2c_driver i2chid_driver = {
>> +     .driver = {
>> +             .name   = DRIVER_NAME,
>> +             .owner  = THIS_MODULE,
>> +             .pm     = &i2chid_pm,
>> +     },
>> +
>> +     .probe          = i2chid_probe,
>> +     .remove         = __devexit_p(i2chid_remove),
>> +
>> +     .id_table       = i2chid_id_table,
>> +};
>> +
>> +module_i2c_driver(i2chid_driver);
>> +
>> +MODULE_DESCRIPTION(DRIVER_DESC);
>> +MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
>> +MODULE_LICENSE("GPL");
>> diff --git a/include/linux/i2c/i2c-hid.h b/include/linux/i2c/i2c-hid.h
>> new file mode 100644
>> index 0000000..6685605
>> --- /dev/null
>> +++ b/include/linux/i2c/i2c-hid.h
>> @@ -0,0 +1,35 @@
>> +/*
>> + * HID over I2C protocol implementation
>> + *
>> + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
>> + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
>> + *
>> + * This file is subject to the terms and conditions of the GNU General Public
>> + * License.  See the file COPYING in the main directory of this archive for
>> + * more details.
>> + */
>> +
>> +#ifndef __LINUX_I2C_HID_H
>> +#define __LINUX_I2C_HID_H
>> +
>> +#include <linux/types.h>
>> +
>> +/**
>> + * struct i2chid_platform_data - used by hid over i2c implementation.
>> + * @hid_descriptor_address: i2c register where the HID descriptor is stored.
>> + *
>> + * Note that it is the responsibility for the platform driver (or the acpi 5.0
>
> responsibility of
>
>> + * driver) to setup the irq related to the gpio in the struct i2c_board_info.
>> + * The platform driver should also setup the gpio according to the device:
>> + *
>> + * A typical example is the following:
>> + *   irq = gpio_to_irq(intr_gpio);
>> + *   hkdk4412_i2c_devs5[0].irq = irq; // store the irq in i2c_board_info
>> + *   gpio_request(intr_gpio, "elan-irq");
>> + *   s3c_gpio_setpull(intr_gpio, S3C_GPIO_PULL_UP);
>> + */
>> +struct i2chid_platform_data {
>> +     u16 hid_descriptor_address;
>> +};
>> +
>> +#endif /* __LINUX_I2C_HID_H */
>
>
> --
> Jean Delvare

Again, thanks for the review.
I'll come back with the v2 soon (I hope).

Cheers,
Benjamin
--
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
Jean Delvare Oct. 7, 2012, 7:03 p.m. UTC | #23
On Sun, 7 Oct 2012 18:57:36 +0200, Benjamin Tissoires wrote:
> On Sun, Oct 7, 2012 at 4:28 PM, Jean Delvare <khali@linux-fr.org> wrote:
> > On Fri, 14 Sep 2012 15:41:43 +0200, benjamin.tissoires wrote:
> >> +     u16 readRegister = ihid->hdesc.wDataRegister;
> >
> > This is missing le16_to_cpu().
> 
> I agree this is awful, but not putting it allows me to not have to
> check the endianness when I'm using it.
> But I may be totally wrong on this.

I'm afraid I don't follow you. I want to see:

	u16 readRegister = le16_to_cpu(ihid->hdesc.wDataRegister);

If you don't do that, your driver is broken on bigendian systems. And
there's no need to "check the endianness when you're using it", the
above should be enough for things to work just fine.
Jiri Kosina Oct. 8, 2012, 2:42 p.m. UTC | #24
On Sun, 7 Oct 2012, Benjamin Tissoires wrote:

> It seems that hid transport layers should go in drivers/hid.
> However, I don't like mixing the transport layer and the final
> drivers. Maybe this is the time to rework a little bit the tree.
> To minimize the moves, we could introduce:
> drivers/hid/busses/usb
> drivers/hid/busses/i2c
> drivers/hid/busses/bluetooth

Something like that, I would personally like. But I am not going to 
forcefully take it over without bluetooth guys agreeing on that as well.
Benjamin Tissoires Oct. 15, 2012, 8:42 p.m. UTC | #25
On Sun, Oct 7, 2012 at 9:03 PM, Jean Delvare <khali@linux-fr.org> wrote:
> On Sun, 7 Oct 2012 18:57:36 +0200, Benjamin Tissoires wrote:
>> On Sun, Oct 7, 2012 at 4:28 PM, Jean Delvare <khali@linux-fr.org> wrote:
>> > On Fri, 14 Sep 2012 15:41:43 +0200, benjamin.tissoires wrote:
>> >> +     u16 readRegister = ihid->hdesc.wDataRegister;
>> >
>> > This is missing le16_to_cpu().
>>
>> I agree this is awful, but not putting it allows me to not have to
>> check the endianness when I'm using it.
>> But I may be totally wrong on this.
>
> I'm afraid I don't follow you. I want to see:
>
>         u16 readRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
>
> If you don't do that, your driver is broken on bigendian systems. And
> there's no need to "check the endianness when you're using it", the
> above should be enough for things to work just fine.
>

a little bit late, but yes, you are entirely right. I was confused by
the fact that I only wanted to use the number coming from the device
to communicate with it, but as I made bit shifting within the CPU, I
need to convert it.
So forget my previous words here, it is fixed in v2.

Thanks
Benjamin

> --
> Jean Delvare
--
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/i2c/Kconfig b/drivers/i2c/Kconfig
index 5a3bb3d..5adf65a 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -47,6 +47,14 @@  config I2C_CHARDEV
 	  This support is also available as a module.  If so, the module 
 	  will be called i2c-dev.
 
+config I2C_HID
+	tristate "HID over I2C bus"
+	help
+	  Say Y here to use the HID over i2c protocol implementation.
+
+	  This support is also available as a module.  If so, the module
+	  will be called i2c-hid.
+
 config I2C_MUX
 	tristate "I2C bus multiplexing support"
 	help
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index beee6b2..8f38116 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -6,6 +6,7 @@  obj-$(CONFIG_I2C_BOARDINFO)	+= i2c-boardinfo.o
 obj-$(CONFIG_I2C)		+= i2c-core.o
 obj-$(CONFIG_I2C_SMBUS)		+= i2c-smbus.o
 obj-$(CONFIG_I2C_CHARDEV)	+= i2c-dev.o
+obj-$(CONFIG_I2C_HID)		+= i2c-hid.o
 obj-$(CONFIG_I2C_MUX)		+= i2c-mux.o
 obj-y				+= algos/ busses/ muxes/
 
diff --git a/drivers/i2c/i2c-hid.c b/drivers/i2c/i2c-hid.c
new file mode 100644
index 0000000..eb17d8c
--- /dev/null
+++ b/drivers/i2c/i2c-hid.c
@@ -0,0 +1,1027 @@ 
+/*
+ * HID over I2C protocol implementation
+ *
+ * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+ * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
+ *
+ * This code is partly based on "USB HID support for Linux":
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2007-2008 Oliver Neukum
+ *  Copyright (c) 2006-2010 Jiri Kosina
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+
+#include <linux/i2c/i2c-hid.h>
+
+#include <linux/hid.h>
+#include <linux/hiddev.h>
+#include <linux/hidraw.h>
+
+#define DRIVER_NAME		"i2chid"
+#define DRIVER_DESC		"HID over I2C core driver"
+
+#define I2C_HID_COMMAND_TRIES	3
+
+/* flags */
+#define I2C_HID_STARTED		(1 << 0)
+#define I2C_HID_OUT_RUNNING	(1 << 1)
+#define I2C_HID_IN_RUNNING	(1 << 2)
+#define I2C_HID_RESET_PENDING	(1 << 3)
+#define I2C_HID_SUSPENDED	(1 << 4)
+
+#define I2C_HID_PWR_ON		0x00
+#define I2C_HID_PWR_SLEEP	0x01
+
+/* debug option */
+static bool debug = false;
+module_param(debug, bool, 0444);
+MODULE_PARM_DESC(debug, "print a lot of debug informations");
+
+struct i2chid_desc {
+	__le16 wHIDDescLength;
+	__le16 bcdVersion;
+	__le16 wReportDescLength;
+	__le16 wReportDescRegister;
+	__le16 wInputRegister;
+	__le16 wMaxInputLength;
+	__le16 wOutputRegister;
+	__le16 wMaxOutputLength;
+	__le16 wCommandRegister;
+	__le16 wDataRegister;
+	__le16 wVendorID;
+	__le16 wProductID;
+	__le16 wVersionID;
+} __packed;
+
+struct i2chid_cmd {
+	enum {
+		/* fecth HID descriptor */
+		HID_DESCR_CMD,
+
+		/* fetch report descriptors */
+		HID_REPORT_DESCR_CMD,
+
+		/* commands */
+		HID_RESET_CMD,
+		HID_GET_REPORT_CMD,
+		HID_SET_REPORT_CMD,
+		HID_GET_IDLE_CMD,
+		HID_SET_IDLE_CMD,
+		HID_GET_PROTOCOL_CMD,
+		HID_SET_PROTOCOL_CMD,
+		HID_SET_POWER_CMD,
+
+		/* read/write data register */
+		HID_DATA_CMD,
+	} name;
+	unsigned int registerIndex;
+	__u8 opcode;
+	unsigned int length;
+	bool wait;
+};
+
+union command {
+	u8 data[0];
+	struct cmd {
+		__le16 reg;
+		__u8 reportTypeID;
+		__u8 opcode;
+	} __packed c;
+};
+
+#define I2C_HID_CMD(name_, opcode_) \
+	.name = name_, .opcode = opcode_, .length = 4, \
+	.registerIndex = offsetof(struct i2chid_desc, wCommandRegister)
+
+static const struct i2chid_cmd cmds[] = {
+	{ I2C_HID_CMD(HID_RESET_CMD,		0x01), .wait = true },
+	{ I2C_HID_CMD(HID_GET_REPORT_CMD,	0x02) },
+	{ I2C_HID_CMD(HID_SET_REPORT_CMD,	0x03) },
+	{ I2C_HID_CMD(HID_GET_IDLE_CMD,		0x04) },
+	{ I2C_HID_CMD(HID_SET_IDLE_CMD,		0x05) },
+	{ I2C_HID_CMD(HID_GET_PROTOCOL_CMD,	0x06) },
+	{ I2C_HID_CMD(HID_SET_PROTOCOL_CMD,	0x07) },
+	{ I2C_HID_CMD(HID_SET_POWER_CMD,	0x08) },
+
+	{.name = HID_DESCR_CMD,
+	 .length = 2},
+
+	{.name = HID_REPORT_DESCR_CMD,
+	 .registerIndex = offsetof(struct i2chid_desc, wReportDescRegister),
+	 .opcode = 0x00,
+	 .length = 2},
+
+	{.name = HID_DATA_CMD,
+	 .registerIndex = offsetof(struct i2chid_desc, wDataRegister),
+	 .opcode = 0x00,
+	 .length = 2},
+
+	{}, /* terminating */
+};
+
+/* The main device structure */
+struct i2chid {
+	struct i2c_client	*client;	/* i2c client */
+	struct hid_device	*hid;	/* pointer to corresponding HID dev */
+	union {
+		__u8 hdesc_buffer[sizeof(struct i2chid_desc)];
+		struct i2chid_desc hdesc;	/* the HID Descriptor */
+	};
+	__le16			wHIDDescRegister; /* location of the i2c
+						   * register of the HID
+						   * descriptor. */
+	unsigned int		bufsize;	/* i2c buffer size */
+	char			*inbuf;		/* Input buffer */
+
+	spinlock_t		flock;		/* flags spinlock */
+	unsigned long		flags;		/* device flags */
+
+	int			irq;		/* the interrupt line irq */
+
+	wait_queue_head_t	wait;		/* For waiting the interrupt */
+};
+
+void i2chid_print_buffer(struct i2chid *ihid, u8 *buf, unsigned int n)
+{
+	int i;
+	char *string = kzalloc((n*3+1)*sizeof(char), GFP_KERNEL);
+	char tmpbuf[4] = "";
+
+	for (i = 0; i < n; ++i) {
+		sprintf(tmpbuf, "%02x ", buf[i] & 0xFF);
+		strcat(string, tmpbuf);
+	}
+
+	dev_err(&ihid->client->dev, "%s\n", string);
+	kfree(string);
+}
+
+static int __i2chid_command(struct i2c_client *client, int command, u8 reportID,
+		u8 reportType, u8 *args, int args_len,
+		unsigned char *buf_recv, int data_len)
+{
+	struct i2chid *ihid = i2c_get_clientdata(client);
+	union command *cmd;
+	unsigned char *rec_buf = buf_recv;
+	int ret;
+	int tries = I2C_HID_COMMAND_TRIES;
+	int length = 0;
+	struct i2c_msg msg[2];
+	int msg_num = 0;
+	int i;
+	bool wait = false;
+
+	if (WARN_ON(!ihid))
+		return -ENODEV;
+
+	cmd = kmalloc(args_len + sizeof(union command), GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	for (i = 0; cmds[i].length; i++) {
+		unsigned int registerIndex;
+
+		if (cmds[i].name != command)
+			continue;
+
+		registerIndex = cmds[i].registerIndex;
+		cmd->data[0] = ihid->hdesc_buffer[registerIndex];
+		cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1];
+		cmd->c.opcode = cmds[i].opcode;
+		length = cmds[i].length;
+		wait = cmds[i].wait;
+	}
+
+	/* special case for HID_DESCR_CMD */
+	if (command == HID_DESCR_CMD)
+		cmd->c.reg = ihid->wHIDDescRegister;
+
+	cmd->c.reportTypeID = reportID | reportType << 4;
+
+	memcpy(cmd->data + length, args, args_len);
+	length += args_len;
+
+	if (debug) {
+		char tmpstr[4] = "";
+		char strbuf[50] = "";
+		int i;
+		for (i = 0; i < length; i++) {
+			sprintf(tmpstr, "%02x ", cmd->data[i] & 0xFF);
+			strcat(strbuf, tmpstr);
+		}
+		dev_err(&client->dev, "%s, cmd=%s\n", __func__, strbuf);
+	}
+
+	msg[0].addr = client->addr;
+	msg[0].flags = client->flags & I2C_M_TEN;
+	msg[0].len = length;
+	msg[0].buf = (char *) cmd->data;
+	msg[1].addr = client->addr;
+	msg[1].flags = client->flags & I2C_M_TEN;
+	msg[1].flags |= I2C_M_RD;
+	msg[1].len = data_len;
+	msg[1].buf = rec_buf;
+
+	msg_num = data_len > 0 ? 2 : 1;
+
+	if (wait) {
+		spin_lock_irq(&ihid->flock);
+		set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
+		spin_unlock_irq(&ihid->flock);
+	}
+
+	do {
+		ret = i2c_transfer(client->adapter, msg, msg_num);
+		if (ret > 0)
+			break;
+		tries--;
+		dev_dbg(&client->dev, "retrying i2chid_command:%d (%d)\n",
+			command, tries);
+	} while (tries > 0);
+
+	if (wait && ret > 0) {
+		if (debug)
+			dev_err(&client->dev, "%s: waiting....\n", __func__);
+		if (!wait_event_timeout(ihid->wait,
+				!test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
+				msecs_to_jiffies(5000)))
+			ret = -ENODATA;
+		if (debug)
+			dev_err(&client->dev, "%s: finished.\n", __func__);
+	}
+
+	kfree(cmd);
+
+	return ret > 0 ? ret != msg_num : ret;
+}
+
+static int i2chid_command(struct i2c_client *client, int command,
+		unsigned char *buf_recv, int data_len)
+{
+	return __i2chid_command(client, command, 0, 0, NULL, 0,
+				buf_recv, data_len);
+}
+
+static int i2chid_get_report(struct i2c_client *client, u8 reportType,
+		u32 reportID, unsigned char *buf_recv, int data_len)
+{
+	struct i2chid *ihid = i2c_get_clientdata(client);
+	u8 args[6];
+	int ret;
+	int args_len = 0;
+	u16 readRegister = ihid->hdesc.wDataRegister;
+
+	if (debug)
+		dev_err(&client->dev, "%s\n", __func__);
+
+	if (reportID >= 15) {
+		args[args_len++] = (reportID >> 0) & 0xFF;
+		args[args_len++] = (reportID >> 8) & 0xFF;
+		args[args_len++] = (reportID >> 16) & 0xFF;
+		args[args_len++] = (reportID >> 24) & 0xFF;
+		reportID = 0x0F;
+	}
+
+	args[args_len++] = readRegister & 0xff;
+	args[args_len++] = (readRegister >> 8) & 0xff;
+
+	ret = __i2chid_command(client, HID_GET_REPORT_CMD, reportID & 0x0F,
+		reportType, args, args_len, buf_recv, data_len);
+	if (ret) {
+		dev_err(&client->dev, "HID_GET_REPORT_CMD Fail\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int i2chid_set_report(struct i2c_client *client, u8 reportType,
+		u32 reportID, unsigned char *buf, int data_len)
+{
+	struct i2chid *ihid = i2c_get_clientdata(client);
+	u8 *args;
+	int ret;
+	u16 dataRegister = ihid->hdesc.wDataRegister;
+	int args_len =  (reportID >= 15 ? 4 : 0) +
+			2 /* dataRegister */ +
+			2 /* size */ +
+			data_len;
+	int index = 0;
+
+	if (debug)
+		dev_err(&client->dev, "%s\n", __func__);
+
+	args = kmalloc(args_len, GFP_KERNEL);
+
+	if (reportID >= 15) {
+		args[index++] = (reportID >> 0) & 0xFF;
+		args[index++] = (reportID >> 8) & 0xFF;
+		args[index++] = (reportID >> 16) & 0xFF;
+		args[index++] = (reportID >> 24) & 0xFF;
+		reportID = 0x0F;
+	}
+
+	args[index++] = dataRegister & 0xff;
+	args[index++] = (dataRegister >> 8) & 0xff;
+
+	args[index++] = data_len & 0xff;
+	args[index++] = (data_len >> 8) & 0xff;
+
+	memcpy(&args[index], buf, data_len);
+
+	ret = __i2chid_command(client, HID_SET_REPORT_CMD, reportID & 0x0F,
+		reportType, args, args_len, NULL, 0);
+	if (ret) {
+		dev_err(&client->dev, "HID_SET_REPORT_CMD Fail\n");
+		return -EINVAL;
+	}
+
+	kfree(args);
+	return data_len;
+}
+
+static int i2chid_set_power(struct i2c_client *client, int power_state)
+{
+	int ret;
+
+	if (debug)
+		dev_err(&client->dev, "%s\n", __func__);
+
+	ret = __i2chid_command(client, HID_SET_POWER_CMD, power_state & 0x01,
+		0, NULL, 0, NULL, 0);
+	if (ret) {
+		dev_err(&client->dev, "HID_SET_POWER_CMD Fail\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int i2chid_hwreset(struct i2c_client *client)
+{
+	uint8_t buf_recv[79];
+	int ret;
+
+	if (debug)
+		dev_err(&client->dev, "%s\n", __func__);
+
+	ret = i2chid_set_power(client, I2C_HID_PWR_ON);
+	if (ret)
+		return -EINVAL;
+
+	if (debug)
+		dev_err(&client->dev, "resetting...\n");
+
+	ret = i2chid_command(client, HID_RESET_CMD, buf_recv, 0);
+	if (ret) {
+		dev_err(&client->dev, "HID_RESET_CMD Fail\n");
+		return -EINVAL;
+	}
+
+	ret = i2c_master_recv(client, buf_recv, 2);
+	if (ret != 2 || (buf_recv[0] != 0 && buf_recv[1] != 0)) {
+		dev_err(&client->dev,
+			"HID_RESET_CMD Failed: got %02x %02x, size=%d\n",
+			buf_recv[0], buf_recv[1], ret);
+
+		i2chid_set_power(client, I2C_HID_PWR_SLEEP);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static bool i2chid_get_input(struct i2chid *ihid)
+{
+	int ret;
+	int size = ihid->hdesc.wMaxInputLength;
+
+	ret = i2c_master_recv(ihid->client, ihid->inbuf, size);
+	if (ret != size) {
+		dev_err(&ihid->client->dev, "%s: got %d data instead of %d\n",
+			__func__, ret, size);
+		return false;
+	}
+
+	if (debug)
+		i2chid_print_buffer(ihid, ihid->inbuf, size);
+
+	ret = hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2,
+			size - 2, 1);
+	if (ret)
+		return false;
+
+	return true;
+}
+
+static irqreturn_t i2chid_irq(int irq, void *dev_id)
+{
+	struct i2chid *ihid = dev_id;
+	int ready;
+
+	if (!ihid)
+		return IRQ_HANDLED;
+
+	spin_lock_irq(&ihid->flock);
+	if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags)) {
+		spin_unlock_irq(&ihid->flock);
+
+		wake_up(&ihid->wait);
+		return IRQ_HANDLED;
+	}
+
+	ready = test_bit(I2C_HID_STARTED, &ihid->flags);
+	spin_unlock_irq(&ihid->flock);
+
+	if (ready)
+		i2chid_get_input(ihid);
+
+	return IRQ_HANDLED;
+}
+
+static int i2chid_get_report_length(struct hid_report *report)
+{
+	return ((report->size - 1) >> 3) + 1 +
+		report->device->report_enum[report->type].numbered + 2;
+}
+
+void i2chid_init_report(struct hid_report *report)
+{
+	struct hid_device *hid = report->device;
+	struct i2c_client *client = hid->driver_data;
+	struct i2chid *ihid = i2c_get_clientdata(client);
+	unsigned int size, ret_size;
+
+	size = i2chid_get_report_length(report);
+	i2chid_get_report(client,
+			report->type == HID_FEATURE_REPORT ? 0x03 : 0x01,
+			report->id, ihid->inbuf, size);
+
+	if (debug)
+		i2chid_print_buffer(ihid, ihid->inbuf, size);
+
+	ret_size = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
+
+	if (ret_size != size) {
+		dev_err(&client->dev, "error in %s size:%d / ret_size:%d\n",
+			__func__, size, ret_size);
+		return;
+	}
+
+	/* hid->driver_lock is held as we are in probe function,
+	 * we just need to setup the input fields, so using
+	 * hid_report_raw_event is safe. */
+	hid_report_raw_event(hid, report->type, ihid->inbuf + 2, size - 2, 1);
+}
+
+/*
+ * Initialize all reports
+ */
+void i2chid_init_reports(struct hid_device *hid)
+{
+	struct hid_report *report;
+
+	list_for_each_entry(report,
+		&hid->report_enum[HID_INPUT_REPORT].report_list, list)
+		i2chid_init_report(report);
+
+	list_for_each_entry(report,
+		&hid->report_enum[HID_FEATURE_REPORT].report_list, list)
+		i2chid_init_report(report);
+}
+
+/*
+ * Traverse the supplied list of reports and find the longest
+ */
+static void i2chid_find_max_report(struct hid_device *hid, unsigned int type,
+		unsigned int *max)
+{
+	struct hid_report *report;
+	unsigned int size;
+
+	/* We should not rely on wMaxInputLength, as some devices may set it to
+	 * a wrong length. */
+	list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
+		size = i2chid_get_report_length(report);
+		if (*max < size)
+			*max = size;
+	}
+}
+
+static int i2chid_alloc_buffers(struct i2chid *ihid)
+{
+	ihid->inbuf = kzalloc(ihid->bufsize, GFP_KERNEL);
+
+	if (!ihid->inbuf)
+		return -1;
+
+	return 0;
+}
+
+static void i2chid_free_buffers(struct i2chid *ihid)
+{
+	kfree(ihid->inbuf);
+}
+
+static int i2chid_get_raw_report(struct hid_device *hid,
+		unsigned char report_number, __u8 *buf, size_t count,
+		unsigned char report_type)
+{
+	struct i2c_client *client = hid->driver_data;
+	struct i2chid *ihid = i2c_get_clientdata(client);
+	int ret;
+
+	if (count > ihid->bufsize)
+		count = ihid->bufsize;
+
+	ret = i2chid_get_report(client,
+			report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
+			report_number, ihid->inbuf, count);
+
+	if (ret < 0)
+		return ret;
+
+	count = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
+
+	memcpy(buf, ihid->inbuf + 2, count);
+
+	return count;
+}
+
+static int i2chid_output_raw_report(struct hid_device *hid, __u8 *buf,
+		size_t count, unsigned char report_type)
+{
+	struct i2c_client *client = hid->driver_data;
+	int ret;
+	int report_id = buf[0];
+
+	ret = i2chid_set_report(client,
+				report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
+				report_id, buf, count);
+
+	return ret;
+
+}
+
+static int i2chid_fetch_hid_descriptor(struct i2chid *ihid)
+{
+	struct i2c_client *client = ihid->client;
+	struct i2chid_desc *hdesc = &ihid->hdesc;
+	unsigned int dsize = 0;
+	int ret;
+
+	/* Fetch the length of HID description, retrieve the 4 first bytes:
+	 * bytes 0-1 -> length
+	 * bytes 2-3 -> bcdVersion (has to be 1.00) */
+	ret = i2chid_command(client, HID_DESCR_CMD, ihid->hdesc_buffer, 4);
+
+	if (debug)
+		dev_err(&client->dev,
+			"%s, ihid->hdesc_buffer: %02x %02x %02x %02x\n",
+			__func__,
+			ihid->hdesc_buffer[0],
+			ihid->hdesc_buffer[1],
+			ihid->hdesc_buffer[2],
+			ihid->hdesc_buffer[3]);
+
+	if (ret) {
+		dev_err(&client->dev, "HID_DESCR_LENGTH_CMD Fail (ret=%d)\n",
+			ret);
+		return -EINVAL;
+	}
+
+	dsize = le16_to_cpu(hdesc->wHIDDescLength);
+	if (!dsize || dsize > HID_MAX_DESCRIPTOR_SIZE) {
+		dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
+			dsize);
+		return -EINVAL;
+	}
+
+	/* check bcdVersion == 1.0 */
+	if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
+		dev_err(&client->dev,
+			"unexpected HID descriptor bcdVersion (0x%04x)\n",
+			le16_to_cpu(hdesc->bcdVersion));
+		return -EINVAL;
+	}
+
+	if (debug)
+		dev_err(&client->dev, "Fetching the HID descriptor\n");
+
+	ret = i2chid_command(client, HID_DESCR_CMD, ihid->hdesc_buffer, dsize);
+	if (ret) {
+		dev_err(&client->dev, "HID_DESCR_CMD Fail\n");
+		return -EINVAL;
+	}
+
+	if (debug)
+		i2chid_print_buffer(ihid, ihid->hdesc_buffer, dsize);
+
+	return 0;
+}
+
+static int i2chid_parse(struct hid_device *hid)
+{
+	struct i2c_client *client = hid->driver_data;
+	struct i2chid *ihid = i2c_get_clientdata(client);
+	struct i2chid_desc *hdesc = &ihid->hdesc;
+	unsigned int rsize = 0;
+	char *rdesc;
+	int ret;
+	int tries = 3;
+
+	if (debug)
+		dev_err(&client->dev, "entering %s\n", __func__);
+
+	rsize = le16_to_cpu(hdesc->wReportDescLength);
+	if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
+		dbg_hid("weird size of report descriptor (%u)\n", rsize);
+		return -EINVAL;
+	}
+
+	do {
+		ret = i2chid_hwreset(client);
+		if (ret)
+			msleep(1000);
+	} while (tries-- > 0 && ret);
+
+	if (ret)
+		return ret;
+
+	rdesc = kmalloc(rsize, GFP_KERNEL);
+
+	if (!rdesc) {
+		dbg_hid("couldn't allocate rdesc memory\n");
+		return -ENOMEM;
+	}
+
+	if (debug)
+		dev_err(&client->dev, "asking HID report descriptor\n");
+
+	ret = i2chid_command(client, HID_REPORT_DESCR_CMD, rdesc, rsize);
+	if (ret) {
+		hid_err(hid, "reading report descriptor failed\n");
+		kfree(rdesc);
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	if (debug)
+		i2chid_print_buffer(ihid, rdesc, rsize);
+
+	ret = hid_parse_report(hid, rdesc, rsize);
+	kfree(rdesc);
+	if (ret) {
+		dbg_hid("parsing report descriptor failed\n");
+		goto err;
+	}
+
+	return 0;
+err:
+	return ret;
+}
+
+static int i2chid_start(struct hid_device *hid)
+{
+	struct i2c_client *client = hid->driver_data;
+	struct i2chid *ihid = i2c_get_clientdata(client);
+	int ret;
+
+	ihid->bufsize = HID_MIN_BUFFER_SIZE;
+	i2chid_find_max_report(hid, HID_INPUT_REPORT, &ihid->bufsize);
+	i2chid_find_max_report(hid, HID_OUTPUT_REPORT, &ihid->bufsize);
+	i2chid_find_max_report(hid, HID_FEATURE_REPORT, &ihid->bufsize);
+
+	if (ihid->bufsize > HID_MAX_BUFFER_SIZE)
+		ihid->bufsize = HID_MAX_BUFFER_SIZE;
+
+	if (i2chid_alloc_buffers(ihid)) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
+		i2chid_init_reports(hid);
+
+	return 0;
+
+fail:
+	i2chid_free_buffers(ihid);
+	return ret;
+}
+
+static void i2chid_stop(struct hid_device *hid)
+{
+	struct i2c_client *client = hid->driver_data;
+	struct i2chid *ihid = i2c_get_clientdata(client);
+
+	if (WARN_ON(!ihid))
+		return;
+
+	hid->claimed = 0;
+
+	i2chid_free_buffers(ihid);
+}
+
+static int i2chid_open(struct hid_device *hid)
+{
+	struct i2c_client *client = hid->driver_data;
+	struct i2chid *ihid = i2c_get_clientdata(client);
+	int ret;
+
+	if (!hid->open++) {
+		ret = i2chid_set_power(client, I2C_HID_PWR_ON);
+		if (ret) {
+			hid->open--;
+			return -EIO;
+		}
+		spin_lock_irq(&ihid->flock);
+		set_bit(I2C_HID_STARTED, &ihid->flags);
+		spin_unlock_irq(&ihid->flock);
+	}
+	return 0;
+}
+
+static void i2chid_close(struct hid_device *hid)
+{
+	struct i2c_client *client = hid->driver_data;
+	struct i2chid *ihid = i2c_get_clientdata(client);
+
+	/* protecting hid->open to make sure we don't restart
+	 * data acquistion due to a resumption we no longer
+	 * care about
+	 */
+	if (!--hid->open) {
+		spin_lock_irq(&ihid->flock);
+		clear_bit(I2C_HID_STARTED, &ihid->flags);
+		spin_unlock_irq(&ihid->flock);
+
+		/* Save some power */
+		i2chid_set_power(client, I2C_HID_PWR_SLEEP);
+	}
+}
+
+static int i2chid_power(struct hid_device *hid, int lvl)
+{
+	struct i2c_client *client = hid->driver_data;
+	int r = 0;
+
+	if (debug)
+		dev_err(&client->dev, "%s lvl:%d\n", __func__, lvl);
+
+	switch (lvl) {
+	case PM_HINT_FULLON:
+		r = i2chid_set_power(client, I2C_HID_PWR_ON);
+		break;
+	case PM_HINT_NORMAL:
+		r = i2chid_set_power(client, I2C_HID_PWR_SLEEP);
+		break;
+	}
+	return r;
+}
+
+static int i2chid_hidinput_input_event(struct input_dev *dev, unsigned int type,
+		unsigned int code, int value)
+{
+	struct hid_device *hid = input_get_drvdata(dev);
+	struct hid_field *field;
+	int offset;
+
+	if (type == EV_FF)
+		return input_ff_event(dev, type, code, value);
+
+	if (type != EV_LED)
+		return -1;
+
+	offset = hidinput_find_field(hid, type, code, &field);
+
+	if (offset == -1) {
+		hid_warn(dev, "event field not found\n");
+		return -1;
+	}
+
+	hid_set_field(field, offset, value);
+
+	return 0;
+}
+
+static struct hid_ll_driver i2c_hid_driver = {
+	.parse = i2chid_parse,
+	.start = i2chid_start,
+	.stop = i2chid_stop,
+	.open = i2chid_open,
+	.close = i2chid_close,
+	.power = i2chid_power,
+	.hidinput_input_event = i2chid_hidinput_input_event,
+};
+
+static int i2chid_init_irq(struct i2c_client *client)
+{
+	struct i2chid *ihid = i2c_get_clientdata(client);
+	int rc;
+
+	dev_dbg(&client->dev, "Requesting IRQ: %d\n",
+		client->irq);
+
+	rc = request_threaded_irq(client->irq, NULL, i2chid_irq,
+			IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+			DRIVER_NAME, ihid);
+	if (rc < 0) {
+		dev_dbg(&client->dev,
+			"Could not register for %s interrupt, irq = %d,"
+			" rc = %d\n",
+		DRIVER_NAME, client->irq, rc);
+
+		return rc;
+	}
+
+	return client->irq;
+}
+
+static int __devinit i2chid_probe(struct i2c_client *client,
+		const struct i2c_device_id *dev_id)
+{
+	int ret;
+	struct i2chid *ihid;
+	struct hid_device *hid;
+	__u16 hidRegister;
+	unsigned char tmpstr[11];
+	struct i2chid_platform_data *platform_data = client->dev.platform_data;
+
+	dbg_hid("HID probe called for i2c %d\n", client->addr);
+
+	if (!platform_data) {
+		dev_err(&client->dev, "HID register address not provided\n");
+		return -EINVAL;
+	}
+
+	if (client->irq < 1) {
+		dev_err(&client->dev,
+			"HID over i2c has not been provided an Int IRQ\n");
+		return -EINVAL;
+	}
+
+	ihid = kzalloc(sizeof(struct i2chid), GFP_KERNEL);
+	if (!ihid)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, ihid);
+
+	ihid->client = client;
+	ihid->flags = 0;
+
+	hidRegister = platform_data->hid_descriptor_address;
+	ihid->wHIDDescRegister = cpu_to_le16(hidRegister);
+
+	init_waitqueue_head(&ihid->wait);
+	spin_lock_init(&ihid->flock);
+
+	ret = i2chid_fetch_hid_descriptor(ihid);
+	if (ret < 0)
+		goto err;
+
+	ihid->irq = i2chid_init_irq(client);
+	if (ihid->irq < 0)
+		goto err;
+
+	hid = hid_allocate_device();
+	if (IS_ERR(hid)) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	ihid->hid = hid;
+
+	hid->driver_data = client;
+	hid->ll_driver = &i2c_hid_driver;
+	hid->hid_get_raw_report = i2chid_get_raw_report;
+	hid->hid_output_raw_report = i2chid_output_raw_report;
+	hid->ff_init = hid_pidff_init;
+#ifdef CONFIG_USB_HIDDEV
+	hid->hiddev_connect = hiddev_connect;
+	hid->hiddev_disconnect = hiddev_disconnect;
+	hid->hiddev_hid_event = hiddev_hid_event;
+	hid->hiddev_report_event = hiddev_report_event;
+#endif
+	hid->dev.parent = &client->dev;
+	hid->bus = BUS_I2C;
+	hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
+	hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
+	hid->product = le16_to_cpu(ihid->hdesc.wProductID);
+
+	snprintf(tmpstr, sizeof(tmpstr), " %04X:%04X",
+		 hid->vendor, hid->product);
+	strlcpy(hid->name, client->name, sizeof(hid->name));
+	strlcat(hid->name, tmpstr, sizeof(hid->name));
+
+	ret = hid_add_device(hid);
+	if (ret) {
+		if (ret != -ENODEV)
+			hid_err(client, "can't add hid device: %d\n", ret);
+		goto err_mem_free;
+	}
+
+	return 0;
+
+err_mem_free:
+	hid_destroy_device(hid);
+
+err:
+	if (ihid->irq >= 0)
+		free_irq(ihid->irq, ihid);
+
+	kfree(ihid);
+	return ret;
+}
+
+static int __devexit i2chid_remove(struct i2c_client *client)
+{
+	struct i2chid *ihid = i2c_get_clientdata(client);
+	struct hid_device *hid;
+
+	if (WARN_ON(!ihid))
+		return -1;
+
+	hid = ihid->hid;
+	hid_destroy_device(hid);
+
+	free_irq(client->irq, ihid);
+
+	kfree(ihid);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int i2chid_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct i2chid *ihid = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(&client->dev))
+		enable_irq_wake(ihid->irq);
+
+	/* Save some power */
+	i2chid_set_power(client, I2C_HID_PWR_SLEEP);
+
+	return 0;
+}
+
+static int i2chid_resume(struct device *dev)
+{
+	int ret;
+	struct i2c_client *client = to_i2c_client(dev);
+
+	ret = i2chid_hwreset(client);
+	if (ret)
+		return ret;
+
+	if (device_may_wakeup(&client->dev))
+		enable_irq_wake(client->irq);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(i2chid_pm, i2chid_suspend, i2chid_resume);
+
+static const struct i2c_device_id i2chid_id_table[] = {
+	{ "i2chid", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, i2chid_id_table);
+
+
+static struct i2c_driver i2chid_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+		.pm	= &i2chid_pm,
+	},
+
+	.probe		= i2chid_probe,
+	.remove		= __devexit_p(i2chid_remove),
+
+	.id_table	= i2chid_id_table,
+};
+
+module_i2c_driver(i2chid_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/i2c/i2c-hid.h b/include/linux/i2c/i2c-hid.h
new file mode 100644
index 0000000..6685605
--- /dev/null
+++ b/include/linux/i2c/i2c-hid.h
@@ -0,0 +1,35 @@ 
+/*
+ * HID over I2C protocol implementation
+ *
+ * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+ * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#ifndef __LINUX_I2C_HID_H
+#define __LINUX_I2C_HID_H
+
+#include <linux/types.h>
+
+/**
+ * struct i2chid_platform_data - used by hid over i2c implementation.
+ * @hid_descriptor_address: i2c register where the HID descriptor is stored.
+ *
+ * Note that it is the responsibility for the platform driver (or the acpi 5.0
+ * driver) to setup the irq related to the gpio in the struct i2c_board_info.
+ * The platform driver should also setup the gpio according to the device:
+ *
+ * A typical example is the following:
+ *	irq = gpio_to_irq(intr_gpio);
+ *	hkdk4412_i2c_devs5[0].irq = irq; // store the irq in i2c_board_info
+ *	gpio_request(intr_gpio, "elan-irq");
+ *	s3c_gpio_setpull(intr_gpio, S3C_GPIO_PULL_UP);
+ */
+struct i2chid_platform_data {
+	u16 hid_descriptor_address;
+};
+
+#endif /* __LINUX_I2C_HID_H */