提交 85df7133 编写于 作者: D Dmitry Torokhov 提交者: Jiri Kosina

HID: i2c-hid: rework i2c_hid_get_report() to use i2c_hid_xfer()

Explicitly prepare command for i2c_hid_get_report() which makes the logic
clearer and allows us to get rid of __i2c_hid_command() and related command
definitions.
Signed-off-by: NDmitry Torokhov <dmitry.torokhov@gmail.com>
Tested-by: NBenjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: NJiri Kosina <jkosina@suse.cz>
上级 50c5249f
...@@ -94,29 +94,6 @@ struct i2c_hid_desc { ...@@ -94,29 +94,6 @@ struct i2c_hid_desc {
__le32 reserved; __le32 reserved;
} __packed; } __packed;
struct i2c_hid_cmd {
unsigned int registerIndex;
__u8 opcode;
unsigned int length;
};
#define I2C_HID_CMD(opcode_) \
.opcode = opcode_, .length = 4, \
.registerIndex = offsetof(struct i2c_hid_desc, wCommandRegister)
/* commands */
static const struct i2c_hid_cmd hid_get_report_cmd = { I2C_HID_CMD(0x02) };
/*
* 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) };
*/
/* The main device structure */ /* The main device structure */
struct i2c_hid { struct i2c_hid {
struct i2c_client *client; /* i2c client */ struct i2c_client *client; /* i2c client */
...@@ -258,52 +235,62 @@ static size_t i2c_hid_encode_command(u8 *buf, u8 opcode, ...@@ -258,52 +235,62 @@ static size_t i2c_hid_encode_command(u8 *buf, u8 opcode,
return length; return length;
} }
static int __i2c_hid_command(struct i2c_hid *ihid, static int i2c_hid_get_report(struct i2c_hid *ihid,
const struct i2c_hid_cmd *command, u8 reportID, u8 report_type, u8 report_id,
u8 reportType, u8 *args, int args_len, u8 *recv_buf, size_t recv_len)
unsigned char *buf_recv, int data_len)
{ {
int length = command->length; size_t length = 0;
unsigned int registerIndex = command->registerIndex; size_t ret_count;
int error;
ihid->cmdbuf[0] = ihid->hdesc_buffer[registerIndex];
ihid->cmdbuf[1] = ihid->hdesc_buffer[registerIndex + 1];
if (length > 2) { i2c_hid_dbg(ihid, "%s\n", __func__);
length = sizeof(__le16) + /* register */
i2c_hid_encode_command(ihid->cmdbuf + sizeof(__le16),
command->opcode,
reportType, reportID);
}
memcpy(ihid->cmdbuf + length, args, args_len); /* Command register goes first */
length += args_len; *(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
length += sizeof(__le16);
/* Next is GET_REPORT command */
length += i2c_hid_encode_command(ihid->cmdbuf + length,
I2C_HID_OPCODE_GET_REPORT,
report_type, report_id);
/*
* Device will send report data through data register. Because
* command can be either 2 or 3 bytes destination for the data
* register may be not aligned.
*/
put_unaligned_le16(le16_to_cpu(ihid->hdesc.wDataRegister),
ihid->cmdbuf + length);
length += sizeof(__le16);
return i2c_hid_xfer(ihid, ihid->cmdbuf, length, buf_recv, data_len); /*
} * In addition to report data device will supply data length
* in the first 2 bytes of the response, so adjust .
*/
error = i2c_hid_xfer(ihid, ihid->cmdbuf, length,
ihid->rawbuf, recv_len + sizeof(__le16));
if (error) {
dev_err(&ihid->client->dev,
"failed to set a report to device: %d\n", error);
return error;
}
static int i2c_hid_get_report(struct i2c_hid *ihid, u8 reportType, /* The buffer is sufficiently aligned */
u8 reportID, unsigned char *buf_recv, int data_len) ret_count = le16_to_cpup((__le16 *)ihid->rawbuf);
{
u8 args[2];
int ret;
int args_len = 0;
u16 readRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
i2c_hid_dbg(ihid, "%s\n", __func__); /* Check for empty report response */
if (ret_count <= sizeof(__le16))
return 0;
args[args_len++] = readRegister & 0xFF; recv_len = min(recv_len, ret_count - sizeof(__le16));
args[args_len++] = readRegister >> 8; memcpy(recv_buf, ihid->rawbuf + sizeof(__le16), recv_len);
ret = __i2c_hid_command(ihid, &hid_get_report_cmd, reportID, if (report_id && recv_len != 0 && recv_buf[0] != report_id) {
reportType, args, args_len, buf_recv, data_len);
if (ret) {
dev_err(&ihid->client->dev, dev_err(&ihid->client->dev,
"failed to retrieve report from device.\n"); "device returned incorrect report (%d vs %d expected)\n",
return ret; recv_buf[0], report_id);
return -EINVAL;
} }
return 0; return recv_len;
} }
static size_t i2c_hid_format_report(u8 *buf, int report_id, static size_t i2c_hid_format_report(u8 *buf, int report_id,
...@@ -654,13 +641,12 @@ static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size) ...@@ -654,13 +641,12 @@ static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size)
} }
static int i2c_hid_get_raw_report(struct hid_device *hid, static int i2c_hid_get_raw_report(struct hid_device *hid,
unsigned char report_number, __u8 *buf, size_t count, u8 report_type, u8 report_id,
unsigned char report_type) u8 *buf, size_t count)
{ {
struct i2c_client *client = hid->driver_data; struct i2c_client *client = hid->driver_data;
struct i2c_hid *ihid = i2c_get_clientdata(client); struct i2c_hid *ihid = i2c_get_clientdata(client);
size_t ret_count, ask_count; int ret_count;
int ret;
if (report_type == HID_OUTPUT_REPORT) if (report_type == HID_OUTPUT_REPORT)
return -EINVAL; return -EINVAL;
...@@ -670,42 +656,24 @@ static int i2c_hid_get_raw_report(struct hid_device *hid, ...@@ -670,42 +656,24 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,
* not have the report ID that the upper layers expect, so we need * not have the report ID that the upper layers expect, so we need
* to stash it the buffer ourselves and adjust the data size. * to stash it the buffer ourselves and adjust the data size.
*/ */
if (!report_number) { if (!report_id) {
buf[0] = 0; buf[0] = 0;
buf++; buf++;
count--; count--;
} }
/* +2 bytes to include the size of the reply in the query buffer */ ret_count = i2c_hid_get_report(ihid,
ask_count = min(count + 2, (size_t)ihid->bufsize);
ret = i2c_hid_get_report(ihid,
report_type == HID_FEATURE_REPORT ? 0x03 : 0x01, report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
report_number, ihid->rawbuf, ask_count); report_id, buf, count);
if (ret < 0)
return ret;
ret_count = ihid->rawbuf[0] | (ihid->rawbuf[1] << 8);
if (ret_count <= 2)
return 0;
ret_count = min(ret_count, ask_count);
/* The query buffer contains the size, dropping it in the reply */
count = min(count, ret_count - 2);
memcpy(buf, ihid->rawbuf + 2, count);
if (!report_number) if (ret_count > 0 && !report_id)
count++; ret_count++;
return count; return ret_count;
} }
static int i2c_hid_output_raw_report(struct hid_device *hid, static int i2c_hid_output_raw_report(struct hid_device *hid, u8 report_type,
const u8 *buf, size_t count, const u8 *buf, size_t count, bool do_set)
u8 report_type, bool do_set)
{ {
struct i2c_client *client = hid->driver_data; struct i2c_client *client = hid->driver_data;
struct i2c_hid *ihid = i2c_get_clientdata(client); struct i2c_hid *ihid = i2c_get_clientdata(client);
...@@ -738,7 +706,7 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, ...@@ -738,7 +706,7 @@ static int i2c_hid_output_raw_report(struct hid_device *hid,
static int i2c_hid_output_report(struct hid_device *hid, u8 *buf, size_t count) static int i2c_hid_output_report(struct hid_device *hid, u8 *buf, size_t count)
{ {
return i2c_hid_output_raw_report(hid, buf, count, HID_OUTPUT_REPORT, return i2c_hid_output_raw_report(hid, HID_OUTPUT_REPORT, buf, count,
false); false);
} }
...@@ -748,11 +716,11 @@ static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum, ...@@ -748,11 +716,11 @@ static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
{ {
switch (reqtype) { switch (reqtype) {
case HID_REQ_GET_REPORT: case HID_REQ_GET_REPORT:
return i2c_hid_get_raw_report(hid, reportnum, buf, len, rtype); return i2c_hid_get_raw_report(hid, rtype, reportnum, buf, len);
case HID_REQ_SET_REPORT: case HID_REQ_SET_REPORT:
if (buf[0] != reportnum) if (buf[0] != reportnum)
return -EINVAL; return -EINVAL;
return i2c_hid_output_raw_report(hid, buf, len, rtype, true); return i2c_hid_output_raw_report(hid, rtype, buf, len, true);
default: default:
return -EIO; return -EIO;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册