Skip to content

Commit dbe0dd5

Browse files
dtorJiri Kosina
authored andcommitted
HID: i2c-hid: explicitly code setting and sending reports
Instead of relying on __i2c_hid_command() that tries to handle all commands and because of that is very complicated, let's define a new dumb helper i2c_hid_xfer() that actually transfers (write and read) data, and use it when sending and setting reports. By doing that we can save on number of copy operations we have to execute, and make logic of sending reports much clearer. Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> Tested-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
1 parent b26fc31 commit dbe0dd5

1 file changed

Lines changed: 151 additions & 118 deletions

File tree

drivers/hid/i2c-hid/i2c-hid-core.c

Lines changed: 151 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <linux/kernel.h>
3636
#include <linux/hid.h>
3737
#include <linux/mutex.h>
38+
#include <asm/unaligned.h>
3839

3940
#include "../hid-ids.h"
4041
#include "i2c-hid.h"
@@ -47,6 +48,15 @@
4748
#define I2C_HID_QUIRK_BAD_INPUT_SIZE BIT(6)
4849
#define I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET BIT(7)
4950

51+
/* Command opcodes */
52+
#define I2C_HID_OPCODE_RESET 0x01
53+
#define I2C_HID_OPCODE_GET_REPORT 0x02
54+
#define I2C_HID_OPCODE_SET_REPORT 0x03
55+
#define I2C_HID_OPCODE_GET_IDLE 0x04
56+
#define I2C_HID_OPCODE_SET_IDLE 0x05
57+
#define I2C_HID_OPCODE_GET_PROTOCOL 0x06
58+
#define I2C_HID_OPCODE_SET_PROTOCOL 0x07
59+
#define I2C_HID_OPCODE_SET_POWER 0x08
5060

5161
/* flags */
5262
#define I2C_HID_STARTED 0
@@ -90,16 +100,6 @@ struct i2c_hid_cmd {
90100
unsigned int length;
91101
};
92102

93-
union command {
94-
u8 data[0];
95-
struct cmd {
96-
__le16 reg;
97-
__u8 reportTypeID;
98-
__u8 opcode;
99-
__u8 reportID;
100-
} __packed c;
101-
};
102-
103103
#define I2C_HID_CMD(opcode_) \
104104
.opcode = opcode_, .length = 4, \
105105
.registerIndex = offsetof(struct i2c_hid_desc, wCommandRegister)
@@ -115,9 +115,7 @@ static const struct i2c_hid_cmd hid_report_descr_cmd = {
115115
/* commands */
116116
static const struct i2c_hid_cmd hid_reset_cmd = { I2C_HID_CMD(0x01) };
117117
static const struct i2c_hid_cmd hid_get_report_cmd = { I2C_HID_CMD(0x02) };
118-
static const struct i2c_hid_cmd hid_set_report_cmd = { I2C_HID_CMD(0x03) };
119118
static const struct i2c_hid_cmd hid_set_power_cmd = { I2C_HID_CMD(0x08) };
120-
static const struct i2c_hid_cmd hid_no_cmd = { .length = 0 };
121119

122120
/*
123121
* These definitions are not used here, but are defined by the spec.
@@ -144,7 +142,6 @@ struct i2c_hid {
144142
u8 *inbuf; /* Input buffer */
145143
u8 *rawbuf; /* Raw Input buffer */
146144
u8 *cmdbuf; /* Command buffer */
147-
u8 *argsbuf; /* Command arguments buffer */
148145

149146
unsigned long flags; /* device flags */
150147
unsigned long quirks; /* Various quirks */
@@ -206,67 +203,90 @@ static u32 i2c_hid_lookup_quirk(const u16 idVendor, const u16 idProduct)
206203
return quirks;
207204
}
208205

206+
static int i2c_hid_xfer(struct i2c_hid *ihid,
207+
u8 *send_buf, int send_len, u8 *recv_buf, int recv_len)
208+
{
209+
struct i2c_client *client = ihid->client;
210+
struct i2c_msg msgs[2] = { 0 };
211+
int n = 0;
212+
int ret;
213+
214+
if (send_len) {
215+
i2c_hid_dbg(ihid, "%s: cmd=%*ph\n",
216+
__func__, send_len, send_buf);
217+
218+
msgs[n].addr = client->addr;
219+
msgs[n].flags = client->flags & I2C_M_TEN;
220+
msgs[n].len = send_len;
221+
msgs[n].buf = send_buf;
222+
n++;
223+
}
224+
225+
if (recv_len) {
226+
msgs[n].addr = client->addr;
227+
msgs[n].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
228+
msgs[n].len = recv_len;
229+
msgs[n].buf = recv_buf;
230+
n++;
231+
232+
set_bit(I2C_HID_READ_PENDING, &ihid->flags);
233+
}
234+
235+
ret = i2c_transfer(client->adapter, msgs, n);
236+
237+
if (recv_len)
238+
clear_bit(I2C_HID_READ_PENDING, &ihid->flags);
239+
240+
if (ret != n)
241+
return ret < 0 ? ret : -EIO;
242+
243+
return 0;
244+
}
245+
246+
static size_t i2c_hid_encode_command(u8 *buf, u8 opcode,
247+
int report_type, int report_id)
248+
{
249+
size_t length = 0;
250+
251+
if (report_id < 0x0F) {
252+
buf[length++] = report_type << 4 | report_id;
253+
buf[length++] = opcode;
254+
} else {
255+
buf[length++] = report_type << 4 | 0x0F;
256+
buf[length++] = opcode;
257+
buf[length++] = report_id;
258+
}
259+
260+
return length;
261+
}
262+
209263
static int __i2c_hid_command(struct i2c_hid *ihid,
210264
const struct i2c_hid_cmd *command, u8 reportID,
211265
u8 reportType, u8 *args, int args_len,
212266
unsigned char *buf_recv, int data_len)
213267
{
214-
struct i2c_client *client = ihid->client;
215-
union command *cmd = (union command *)ihid->cmdbuf;
216-
int ret;
217-
struct i2c_msg msg[2];
218-
int msg_num = 1;
219-
220268
int length = command->length;
221269
unsigned int registerIndex = command->registerIndex;
222270

223271
/* special case for hid_descr_cmd */
224272
if (command == &hid_descr_cmd) {
225-
cmd->c.reg = ihid->wHIDDescRegister;
273+
*(__le16 *)ihid->cmdbuf = ihid->wHIDDescRegister;
226274
} else {
227-
cmd->data[0] = ihid->hdesc_buffer[registerIndex];
228-
cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1];
275+
ihid->cmdbuf[0] = ihid->hdesc_buffer[registerIndex];
276+
ihid->cmdbuf[1] = ihid->hdesc_buffer[registerIndex + 1];
229277
}
230278

231279
if (length > 2) {
232-
cmd->c.opcode = command->opcode;
233-
if (reportID < 0x0F) {
234-
cmd->c.reportTypeID = reportType << 4 | reportID;
235-
} else {
236-
cmd->c.reportTypeID = reportType << 4 | 0x0F;
237-
cmd->c.reportID = reportID;
238-
length++;
239-
}
280+
length = sizeof(__le16) + /* register */
281+
i2c_hid_encode_command(ihid->cmdbuf + sizeof(__le16),
282+
command->opcode,
283+
reportType, reportID);
240284
}
241285

242-
memcpy(cmd->data + length, args, args_len);
286+
memcpy(ihid->cmdbuf + length, args, args_len);
243287
length += args_len;
244288

245-
i2c_hid_dbg(ihid, "%s: cmd=%*ph\n", __func__, length, cmd->data);
246-
247-
msg[0].addr = client->addr;
248-
msg[0].flags = client->flags & I2C_M_TEN;
249-
msg[0].len = length;
250-
msg[0].buf = cmd->data;
251-
if (data_len > 0) {
252-
msg[1].addr = client->addr;
253-
msg[1].flags = client->flags & I2C_M_TEN;
254-
msg[1].flags |= I2C_M_RD;
255-
msg[1].len = data_len;
256-
msg[1].buf = buf_recv;
257-
msg_num = 2;
258-
set_bit(I2C_HID_READ_PENDING, &ihid->flags);
259-
}
260-
261-
ret = i2c_transfer(client->adapter, msg, msg_num);
262-
263-
if (data_len > 0)
264-
clear_bit(I2C_HID_READ_PENDING, &ihid->flags);
265-
266-
if (ret != msg_num)
267-
return ret < 0 ? ret : -EIO;
268-
269-
return 0;
289+
return i2c_hid_xfer(ihid, ihid->cmdbuf, length, buf_recv, data_len);
270290
}
271291

272292
static int i2c_hid_command(struct i2c_hid *ihid,
@@ -301,70 +321,81 @@ static int i2c_hid_get_report(struct i2c_hid *ihid, u8 reportType,
301321
return 0;
302322
}
303323

324+
static size_t i2c_hid_format_report(u8 *buf, int report_id,
325+
const u8 *data, size_t size)
326+
{
327+
size_t length = sizeof(__le16); /* reserve space to store size */
328+
329+
if (report_id)
330+
buf[length++] = report_id;
331+
332+
memcpy(buf + length, data, size);
333+
length += size;
334+
335+
/* Store overall size in the beginning of the buffer */
336+
put_unaligned_le16(length, buf);
337+
338+
return length;
339+
}
340+
304341
/**
305342
* i2c_hid_set_or_send_report: forward an incoming report to the device
306343
* @ihid: the i2c hid device
307-
* @reportType: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT
308-
* @reportID: the report ID
344+
* @report_type: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT
345+
* @report_id: the report ID
309346
* @buf: the actual data to transfer, without the report ID
310347
* @data_len: size of buf
311-
* @use_data: true: use SET_REPORT HID command, false: send plain OUTPUT report
348+
* @do_set: true: use SET_REPORT HID command, false: send plain OUTPUT report
312349
*/
313-
static int i2c_hid_set_or_send_report(struct i2c_hid *ihid, u8 reportType,
314-
u8 reportID, unsigned char *buf, size_t data_len, bool use_data)
350+
static int i2c_hid_set_or_send_report(struct i2c_hid *ihid,
351+
u8 report_type, u8 report_id,
352+
const u8 *buf, size_t data_len,
353+
bool do_set)
315354
{
316-
u8 *args = ihid->argsbuf;
317-
const struct i2c_hid_cmd *hidcmd;
318-
int ret;
319-
u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
320-
u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);
321-
u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength);
322-
u16 size;
323-
int args_len;
324-
int index = 0;
355+
size_t length = 0;
356+
int error;
325357

326358
i2c_hid_dbg(ihid, "%s\n", __func__);
327359

328360
if (data_len > ihid->bufsize)
329361
return -EINVAL;
330362

331-
size = 2 /* size */ +
332-
(reportID ? 1 : 0) /* reportID */ +
333-
data_len /* buf */;
334-
args_len = 2 /* dataRegister */ +
335-
size /* args */;
336-
337-
if (!use_data && maxOutputLength == 0)
363+
if (!do_set && le16_to_cpu(ihid->hdesc.wMaxOutputLength) == 0)
338364
return -ENOSYS;
339365

340-
/*
341-
* use the data register for feature reports or if the device does not
342-
* support the output register
343-
*/
344-
if (use_data) {
345-
args[index++] = dataRegister & 0xFF;
346-
args[index++] = dataRegister >> 8;
347-
hidcmd = &hid_set_report_cmd;
366+
if (do_set) {
367+
/* Command register goes first */
368+
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
369+
length += sizeof(__le16);
370+
/* Next is SET_REPORT command */
371+
length += i2c_hid_encode_command(ihid->cmdbuf + length,
372+
I2C_HID_OPCODE_SET_REPORT,
373+
report_type, report_id);
374+
/*
375+
* Report data will go into the data register. Because
376+
* command can be either 2 or 3 bytes destination for
377+
* the data register may be not aligned.
378+
*/
379+
put_unaligned_le16(le16_to_cpu(ihid->hdesc.wDataRegister),
380+
ihid->cmdbuf + length);
381+
length += sizeof(__le16);
348382
} else {
349-
args[index++] = outputRegister & 0xFF;
350-
args[index++] = outputRegister >> 8;
351-
hidcmd = &hid_no_cmd;
383+
/*
384+
* With simple "send report" all data goes into the output
385+
* register.
386+
*/
387+
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wOutputRegister;;
388+
length += sizeof(__le16);
352389
}
353390

354-
args[index++] = size & 0xFF;
355-
args[index++] = size >> 8;
356-
357-
if (reportID)
358-
args[index++] = reportID;
391+
length += i2c_hid_format_report(ihid->cmdbuf + length,
392+
report_id, buf, data_len);
359393

360-
memcpy(&args[index], buf, data_len);
361-
362-
ret = __i2c_hid_command(ihid, hidcmd, reportID,
363-
reportType, args, args_len, NULL, 0);
364-
if (ret) {
394+
error = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
395+
if (error) {
365396
dev_err(&ihid->client->dev,
366-
"failed to set a report to device.\n");
367-
return ret;
397+
"failed to set a report to device: %d\n", error);
398+
return error;
368399
}
369400

370401
return data_len;
@@ -578,31 +609,33 @@ static void i2c_hid_free_buffers(struct i2c_hid *ihid)
578609
{
579610
kfree(ihid->inbuf);
580611
kfree(ihid->rawbuf);
581-
kfree(ihid->argsbuf);
582612
kfree(ihid->cmdbuf);
583613
ihid->inbuf = NULL;
584614
ihid->rawbuf = NULL;
585615
ihid->cmdbuf = NULL;
586-
ihid->argsbuf = NULL;
587616
ihid->bufsize = 0;
588617
}
589618

590619
static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size)
591620
{
592-
/* the worst case is computed from the set_report command with a
593-
* reportID > 15 and the maximum report length */
594-
int args_len = sizeof(__u8) + /* ReportID */
595-
sizeof(__u8) + /* optional ReportID byte */
596-
sizeof(__u16) + /* data register */
597-
sizeof(__u16) + /* size of the report */
598-
report_size; /* report */
621+
/*
622+
* The worst case is computed from the set_report command with a
623+
* reportID > 15 and the maximum report length.
624+
*/
625+
int cmd_len = sizeof(__le16) + /* command register */
626+
sizeof(u8) + /* encoded report type/ID */
627+
sizeof(u8) + /* opcode */
628+
sizeof(u8) + /* optional 3rd byte report ID */
629+
sizeof(__le16) + /* data register */
630+
sizeof(__le16) + /* report data size */
631+
sizeof(u8) + /* report ID if numbered report */
632+
report_size;
599633

600634
ihid->inbuf = kzalloc(report_size, GFP_KERNEL);
601635
ihid->rawbuf = kzalloc(report_size, GFP_KERNEL);
602-
ihid->argsbuf = kzalloc(args_len, GFP_KERNEL);
603-
ihid->cmdbuf = kzalloc(sizeof(union command) + args_len, GFP_KERNEL);
636+
ihid->cmdbuf = kzalloc(cmd_len, GFP_KERNEL);
604637

605-
if (!ihid->inbuf || !ihid->rawbuf || !ihid->argsbuf || !ihid->cmdbuf) {
638+
if (!ihid->inbuf || !ihid->rawbuf || !ihid->cmdbuf) {
606639
i2c_hid_free_buffers(ihid);
607640
return -ENOMEM;
608641
}
@@ -662,8 +695,9 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,
662695
return count;
663696
}
664697

665-
static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
666-
size_t count, unsigned char report_type, bool use_data)
698+
static int i2c_hid_output_raw_report(struct hid_device *hid,
699+
const u8 *buf, size_t count,
700+
u8 report_type, bool do_set)
667701
{
668702
struct i2c_client *client = hid->driver_data;
669703
struct i2c_hid *ihid = i2c_get_clientdata(client);
@@ -684,7 +718,7 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
684718
*/
685719
ret = i2c_hid_set_or_send_report(ihid,
686720
report_type == HID_FEATURE_REPORT ? 0x03 : 0x02,
687-
report_id, buf + 1, count - 1, use_data);
721+
report_id, buf + 1, count - 1, do_set);
688722

689723
if (ret >= 0)
690724
ret++; /* add report_id to the number of transferred bytes */
@@ -694,11 +728,10 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
694728
return ret;
695729
}
696730

697-
static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf,
698-
size_t count)
731+
static int i2c_hid_output_report(struct hid_device *hid, u8 *buf, size_t count)
699732
{
700733
return i2c_hid_output_raw_report(hid, buf, count, HID_OUTPUT_REPORT,
701-
false);
734+
false);
702735
}
703736

704737
static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum,

0 commit comments

Comments
 (0)