/*
* HID over I2C protocol implementation
*
* Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
* Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
* Copyright (c) 2012 Red Hat, Inc
*
* 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/interrupt.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pm.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/err.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/hid.h>
#include <linux/mutex.h>
#include <linux/acpi.h>
#include <linux/of.h>
#include <linux/i2c/i2c-hid.h>
/* flags */
#define I2C_HID_STARTED (1 << 0)
#define I2C_HID_RESET_PENDING (1 << 1)
#define I2C_HID_READ_PENDING (1 << 2)
#define I2C_HID_PWR_ON 0x00
#define I2C_HID_PWR_SLEEP 0x01
/* debug option */
static bool debug;
module_param(debug, bool, 0444);
MODULE_PARM_DESC(debug, "print a lot of debug information");
#define i2c_hid_dbg(ihid, fmt, arg...) \
do { \
if (debug) \
dev_printk(KERN_DEBUG, &(ihid)->client->dev, fmt, ##arg); \
} while (0)
struct i2c_hid_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;
__le32 reserved;
} __packed;
struct i2c_hid_cmd {
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(opcode_) \
.opcode = opcode_, .length = 4, \
.registerIndex = offsetof(struct i2c_hid_desc, wCommandRegister)
/* fetch HID descriptor */
static const struct i2c_hid_cmd hid_descr_cmd = { .length = 2 };
/* fetch report descriptors */
static const struct i2c_hid_cmd hid_report_descr_cmd = {
.registerIndex = offsetof(struct i2c_hid_desc,
wReportDescRegister),
.opcode = 0x00,
.length = 2 };
/* commands */
static const struct i2c_hid_cmd hid_reset_cmd = { I2C_HID_CMD(0x01),
.wait = true };
static const struct i2c_hid_cmd hid_get_report_cmd = { I2C_HID_CMD(0x02) };
static const struct i2c_hid_cmd hid_set_report_cmd = { I2C_HID_CMD(0x03) };
static const struct i2c_hid_cmd hid_set_power_cmd = { I2C_HID_CMD(0x08) };
static const struct i2c_hid_cmd hid_no_cmd = { .length = 0 };
/*
* These definitions are not used here, but are defined by the spec.
* Keeping them here for documentation purposes.
*
* static const struct i2c_hid_cmd hid_get_idle_cmd = { I2C_HID_CMD(0x04) };
* static const struct i2c_hid_cmd hid_set_idle_cmd = { I2C_HID_CMD(0x05) };
* static const struct i2c_hid_cmd hid_get_protocol_cmd = { I2C_HID_CMD(0x06) };
* static const struct i2c_hid_cmd hid_set_protocol_cmd = { I2C_HID_CMD(0x07) };
*/
static DEFINE_MUTEX(i2c_hid_open_mut);
/* The main device structure */
struct i2c_hid {
struct i2c_client *client; /* i2c client */
struct hid_device *hid; /* pointer to corresponding HID dev */
union {
__u8 hdesc_buffer[sizeof(struct i2c_hid_desc)];
struct i2c_hid_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 */
char *cmdbuf; /* Command buffer */
char *argsbuf; /* Command arguments buffer */
unsigned long flags; /* device flags */
wait_queue_head_t wait; /* For waiting the interrupt */
struct i2c_hid_platform_data pdata;
};
static int __i2c_hid_command(struct i2c_client *client,
const struct i2c_hid_cmd *command, u8 reportID