|
@@ -256,12 +256,21 @@ static int i2c_hid_get_report(struct i2c_client *client, u8 reportType,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
|
|
|
- u8 reportID, unsigned char *buf, size_t data_len)
|
|
|
+/**
|
|
|
+ * i2c_hid_set_or_send_report: forward an incoming report to the device
|
|
|
+ * @client: the i2c_client of the device
|
|
|
+ * @reportType: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT
|
|
|
+ * @reportID: the report ID
|
|
|
+ * @buf: the actual data to transfer, without the report ID
|
|
|
+ * @len: size of buf
|
|
|
+ * @use_data: true: use SET_REPORT HID command, false: send plain OUTPUT report
|
|
|
+ */
|
|
|
+static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType,
|
|
|
+ u8 reportID, unsigned char *buf, size_t data_len, bool use_data)
|
|
|
{
|
|
|
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
|
|
u8 *args = ihid->argsbuf;
|
|
|
- const struct i2c_hid_cmd * hidcmd = &hid_set_report_cmd;
|
|
|
+ const struct i2c_hid_cmd *hidcmd;
|
|
|
int ret;
|
|
|
u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
|
|
|
u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);
|
|
@@ -278,6 +287,9 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
|
|
|
|
|
|
i2c_hid_dbg(ihid, "%s\n", __func__);
|
|
|
|
|
|
+ if (!use_data && maxOutputLength == 0)
|
|
|
+ return -ENOSYS;
|
|
|
+
|
|
|
if (reportID >= 0x0F) {
|
|
|
args[index++] = reportID;
|
|
|
reportID = 0x0F;
|
|
@@ -287,9 +299,10 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
|
|
|
* use the data register for feature reports or if the device does not
|
|
|
* support the output register
|
|
|
*/
|
|
|
- if (reportType == 0x03 || maxOutputLength == 0) {
|
|
|
+ if (use_data) {
|
|
|
args[index++] = dataRegister & 0xFF;
|
|
|
args[index++] = dataRegister >> 8;
|
|
|
+ hidcmd = &hid_set_report_cmd;
|
|
|
} else {
|
|
|
args[index++] = outputRegister & 0xFF;
|
|
|
args[index++] = outputRegister >> 8;
|
|
@@ -550,7 +563,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,
|
|
|
}
|
|
|
|
|
|
static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
|
|
|
- size_t count, unsigned char report_type)
|
|
|
+ size_t count, unsigned char report_type, bool use_data)
|
|
|
{
|
|
|
struct i2c_client *client = hid->driver_data;
|
|
|
int report_id = buf[0];
|
|
@@ -564,9 +577,9 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
|
|
|
count--;
|
|
|
}
|
|
|
|
|
|
- ret = i2c_hid_set_report(client,
|
|
|
+ ret = i2c_hid_set_or_send_report(client,
|
|
|
report_type == HID_FEATURE_REPORT ? 0x03 : 0x02,
|
|
|
- report_id, buf, count);
|
|
|
+ report_id, buf, count, use_data);
|
|
|
|
|
|
if (report_id && ret >= 0)
|
|
|
ret++; /* add report_id to the number of transfered bytes */
|
|
@@ -574,6 +587,42 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static int __i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
|
|
|
+ size_t count, unsigned char report_type)
|
|
|
+{
|
|
|
+ struct i2c_client *client = hid->driver_data;
|
|
|
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
|
|
|
+ bool data = true; /* SET_REPORT */
|
|
|
+
|
|
|
+ if (report_type == HID_OUTPUT_REPORT)
|
|
|
+ data = le16_to_cpu(ihid->hdesc.wMaxOutputLength) == 0;
|
|
|
+
|
|
|
+ return i2c_hid_output_raw_report(hid, buf, count, report_type, data);
|
|
|
+}
|
|
|
+
|
|
|
+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,
|
|
|
+ false);
|
|
|
+}
|
|
|
+
|
|
|
+static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
|
|
|
+ __u8 *buf, size_t len, unsigned char rtype,
|
|
|
+ int reqtype)
|
|
|
+{
|
|
|
+ switch (reqtype) {
|
|
|
+ case HID_REQ_GET_REPORT:
|
|
|
+ return i2c_hid_get_raw_report(hid, reportnum, buf, len, rtype);
|
|
|
+ case HID_REQ_SET_REPORT:
|
|
|
+ if (buf[0] != reportnum)
|
|
|
+ return -EINVAL;
|
|
|
+ return i2c_hid_output_raw_report(hid, buf, len, rtype, true);
|
|
|
+ default:
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep,
|
|
|
int reqtype)
|
|
|
{
|
|
@@ -597,7 +646,7 @@ static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep,
|
|
|
break;
|
|
|
case HID_REQ_SET_REPORT:
|
|
|
hid_output_report(rep, buf);
|
|
|
- i2c_hid_output_raw_report(hid, buf, len, rep->type);
|
|
|
+ i2c_hid_output_raw_report(hid, buf, len, rep->type, true);
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -761,6 +810,8 @@ static struct hid_ll_driver i2c_hid_ll_driver = {
|
|
|
.close = i2c_hid_close,
|
|
|
.power = i2c_hid_power,
|
|
|
.request = i2c_hid_request,
|
|
|
+ .output_report = i2c_hid_output_report,
|
|
|
+ .raw_request = i2c_hid_raw_request,
|
|
|
};
|
|
|
|
|
|
static int i2c_hid_init_irq(struct i2c_client *client)
|
|
@@ -1005,7 +1056,7 @@ static int i2c_hid_probe(struct i2c_client *client,
|
|
|
|
|
|
hid->driver_data = client;
|
|
|
hid->ll_driver = &i2c_hid_ll_driver;
|
|
|
- hid->hid_output_raw_report = i2c_hid_output_raw_report;
|
|
|
+ hid->hid_output_raw_report = __i2c_hid_output_raw_report;
|
|
|
hid->dev.parent = &client->dev;
|
|
|
ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));
|
|
|
hid->bus = BUS_I2C;
|