Skip to contentSkip to conversation

HID: Transport Driver Cleanup

RFC In review David Herrmann 4 participants 27 messages Jul 15, 2013 – Jul 31, 2013

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

cover HID: Transport Driver Cleanup

Hi

This series provides some cleanups for HID transport drivers:
Patch #1: Cleanup which can be picked up
Patch #2-#6: Provide generic hidinput_input_event() helpers Patch #7-#8: Transport-driver Cleanup

Patch #7-#8 is an attempt to clean up the transport driver callbacks. Lets look at how the following hooks are currently implemented:
1) GET_REPORT: Issue a synchronous GET_REPORT via the ctrl-channel
2) SET_REPORT: Issue a synchronous SET_REPORT via the ctrl-channel
3) OUTPUT: Issue an asynchronous OUTPUT report on the intr-channel

USB-HID:
1) GET_REPORT can be issued via ->hid_get_raw_report() for all three report types correctly.
2) SET_REPORT can only be issued for FEATURE reports via ->hid_output_raw_report(). INPUT and OUTPUT reports on the same callback cause an asynchronous report on the intr channel.
3) OUTPUT reports can be issued via ->hid_output_raw_report() as described above in SET_REPORT.

HIDP:
1) GET_REPORT: same as for USB-HID
2) SET_REPORT can only be issued for FEATURE reports via ->hid_output_raw_report(). SET_REPORT for INPUT reports is forbidden (why?) and SET_REPORT for OUTPUT reports is actually implemented as 3)
3) OUTPUT reports can be issued via ->hid_output_raw_report().

I2C:
1) GET_REPORT implemented for INPUT and FEATURE via ->hid_get_raw_report(). Forbidden for OUTPUT reports (why?).
2) SET_REPORT can be issued for OUTPUT and FEATURE reports via

    ->hid_output_raw_report() but is forbidden for INPUT reports (why?).

3) OUTPUT reports cannot be issued at all.

UHID:
1) GET_REPORT only supported for FEATURE via ->hid_get_raw_report()
2) SET_REPORT not supported at all
3) OUTPUT supported via ->hid_output_raw_report()

As this is all very inconsistent, I added two new callbacks:
->raw_request() is the same as ->request() but with a raw buffer. It should be implemented by the transport drivers as SET/GET_REPORT calls in the ctrl-channel.

->output_report() is supposed to send an OUTPUT report on the intr-channel (same channel asynchronous input reports are received from).

Please see the hid-transport.txt for a description. The last patch tries to implement them. I have no idea how to implement it for i2c, it seems that i2c multiplexes ctrl and intr channels on a single i2c channel. I don't know how to send raw output reports at all.. Help welcome!

If there is more interest in this work, I will clean it up, try to convert the other hid_ll_driver drivers and resend as a proper patchset.

Cheers
David

David Herrmann (8):
HID: usbhid: make usbhid_set_leds() static
HID: usbhid: update LED fields unlocked
HID: input: generic hidinput_input_event handler
HID: usbhid: use generic hidinput_input_event()
HID: i2c: use generic hidinput_input_event()
HID: uhid: use generic hidinput_input_event()
HID: add transport driver documentation
HID: implement new transport-driver callbacks

 Documentation/hid/hid-transport.txt | 299 ++++++++++++++++++++++++++++++++++++
 Documentation/hid/uhid.txt          |   4 +-
 drivers/hid/hid-input.c             |  80 +++++++++-
 drivers/hid/i2c-hid/i2c-hid.c       |  24 ---
 drivers/hid/uhid.c                  |  52 ++++---
 drivers/hid/usbhid/hid-core.c       | 144 +++++++++--------
 drivers/hid/usbhid/usbhid.h         |   3 -
 include/linux/hid.h                 |  10 +-
 include/uapi/linux/uhid.h           |   4 +-
 net/bluetooth/hidp/core.c           |  90 +++++++++++
 10 files changed, 590 insertions(+), 120 deletions(-)
 create mode 100644 Documentation/hid/hid-transport.txt
Signature · 1 line
1.8.3.2
#1 lore raw

RFC 1/8 HID: usbhid: make usbhid_set_leds() static

usbhid_set_leds() is only used inside of usbhid/hid-core.c so no need to export it.

---
 drivers/hid/usbhid/hid-core.c | 3 +--
 include/linux/hid.h           | 1 -
 2 files changed, 1 insertion(+), 3 deletions(-)
drivers/hid/usbhid/hid-core.c +1 -2
index 9941828..5482bf4 100644
@@ -857,7 +857,7 @@ static int hid_find_field_early(struct hid_device *hid, unsigned int page,
 	return -1;
 }
 
-void usbhid_set_leds(struct hid_device *hid)
+static void usbhid_set_leds(struct hid_device *hid)
 {
 	struct hid_field *field;
 	int offset;
@@ -867,7 +867,6 @@ void usbhid_set_leds(struct hid_device *hid)
 		usbhid_submit_report(hid, field->report, USB_DIR_OUT);
 	}
 }
-EXPORT_SYMBOL_GPL(usbhid_set_leds);
 
 /*
  * Traverse the supplied list of reports and find the longest
include/linux/hid.h +0 -1
index 0c48991..b8058c5 100644
@@ -989,7 +989,6 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
 u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct);
 int usbhid_quirks_init(char **quirks_param);
 void usbhid_quirks_exit(void);
-void usbhid_set_leds(struct hid_device *hid);
 
 #ifdef CONFIG_HID_PID
 int hid_pidff_init(struct hid_device *hid);
Signature · 1 line
1.8.3.2
#3 lore raw

RFC 2/8 HID: usbhid: update LED fields unlocked

In reply to David Herrmann

Report fields can be updated from HID drivers unlocked via hid_set_field(). It is protected by input_lock in HID core so only a single input event is handled at a time. USBHID can thus update the field unlocked and doesn't conflict with any HID vendor/device drivers. Note, many HID drivers make heavy use of hid_set_field() in that way.

But usbhid also schedules a work to gather multiple LED changes in a single report. Hence, we used to lock the LED field update so the work can read a consistent state. However, hid_set_field() only writes a single integer field, which is guaranteed to be allocated all the time. So the worst possible race-condition is a garbage read on the LED field.

Therefore, there is no need to protect the update. In fact, the only thing that is prevented by locking hid_set_field(), is an LED update while the scheduled work currently writes an older LED update out. However, this means, a new work is scheduled directly when the old one is done writing the new state to the device. So we actually _win_ by not protecting the write and allowing the write to be combined with the current write. A new worker is still scheduled, but will not write any new state. So the LED will not blink unnecessarily on the device.

Assume we have the LED set to 0. Two request come in which enable the LED and immediately disable it. The current situation with two CPUs would be:

  usb_hidinput_input_event()       |      hid_led()
  ---------------------------------+----------------------------------
    spin_lock(&usbhid->lock);
    hid_set_field(1);
    spin_unlock(&usbhid->lock);
    schedule_work(...);
                                      spin_lock(&usbhid->lock);
                                      __usbhid_submit_report(..1..);
                                      spin_unlock(&usbhid->lock);
    spin_lock(&usbhid->lock);
    hid_set_field(0);
    spin_unlock(&usbhid->lock);
    schedule_work(...);
                                      spin_lock(&usbhid->lock);
                                      __usbhid_submit_report(..0..);
                                      spin_unlock(&usbhid->lock);

With the locking removed, we _might_ end up with (look at the changed __usbhid_submit_report() parameters in the first try!):

  usb_hidinput_input_event()       |      hid_led()
  ---------------------------------+----------------------------------
    hid_set_field(1);
    schedule_work(...);
                                      spin_lock(&usbhid->lock);
    hid_set_field(0);
    schedule_work(...);
                                      __usbhid_submit_report(..0..);
                                      spin_unlock(&usbhid->lock);
                                      ... next work ...

                                      spin_lock(&usbhid->lock);
                                      __usbhid_submit_report(..0..);
                                      spin_unlock(&usbhid->lock);

As one can see, we no longer send the "LED ON" signal as it is disabled immediately afterwards and the following "LED OFF" request overwrites the pending "LED ON".

It is important to note that hid_set_field() is not atomic, so we might also end up with any other value. But that doesn't matter either as we _always_ schedule the next work with a correct value and schedule_work() acts as memory barrier, anyways. So in the worst case, we run __usbhid_submit_report(..<garbage>..) in the first case and the following __usbhid_submit_report() will write the correct value. But LED states are booleans so any garbage will be converted to either 0 or 1 and the remote device will never see invalid requests.

Why all this? It avoids any custom locking around hid_set_field() in usbhid and finally allows us to provide a generic hidinput_input_event() handler for all HID transport drivers.

---
 drivers/hid/usbhid/hid-core.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)
drivers/hid/usbhid/hid-core.c +13 -3
index 5482bf4..62b5131 100644
@@ -664,6 +664,19 @@ static void hid_led(struct work_struct *work)
 		return;
 	}
 
+	/*
+	 * field->report is accessed unlocked regarding HID core. So there might
+	 * be another incoming SET-LED request from user-space, which changes
+	 * the LED state while we assemble our outgoing buffer. However, this
+	 * doesn't matter as hid_output_report() correctly converts it into a
+	 * boolean value no matter what information is currently set on the LED
+	 * field (even garbage). So the remote device will always get a valid
+	 * request.
+	 * And in case we send a wrong value, a next hid_led() worker is spawned
+	 * for every SET-LED request so the following hid_led() worker will send
+	 * the correct value, guaranteed!
+	 */
+
 	spin_lock_irqsave(&usbhid->lock, flags);
 	if (!test_bit(HID_DISCONNECTED, &usbhid->iofl)) {
 		usbhid->ledcount = hidinput_count_leds(hid);
@@ -678,7 +691,6 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct usbhid_device *usbhid = hid->driver_data;
 	struct hid_field *field;
-	unsigned long flags;
 	int offset;
 
 	if (type == EV_FF)
@@ -692,9 +704,7 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un
 		return -1;
 	}
 
-	spin_lock_irqsave(&usbhid->lock, flags);
 	hid_set_field(field, offset, value);
-	spin_unlock_irqrestore(&usbhid->lock, flags);
 
 	/*
 	 * Defer performing requested LED action.
Signature · 1 line
1.8.3.2
Reviewed-by on v1 2/8
#4 lore raw

Re: [RFC 2/8] HID: usbhid: update LED fields unlocked

Quoted 73 lines · David Herrmann

Report fields can be updated from HID drivers unlocked via hid_set_field(). It is protected by input_lock in HID core so only a single input event is handled at a time. USBHID can thus update the field unlocked and doesn't conflict with any HID vendor/device drivers. Note, many HID drivers make heavy use of hid_set_field() in that way.

But usbhid also schedules a work to gather multiple LED changes in a single report. Hence, we used to lock the LED field update so the work can read a consistent state. However, hid_set_field() only writes a single integer field, which is guaranteed to be allocated all the time. So the worst possible race-condition is a garbage read on the LED field.

Therefore, there is no need to protect the update. In fact, the only thing that is prevented by locking hid_set_field(), is an LED update while the scheduled work currently writes an older LED update out. However, this means, a new work is scheduled directly when the old one is done writing the new state to the device. So we actually _win_ by not protecting the write and allowing the write to be combined with the current write. A new worker is still scheduled, but will not write any new state. So the LED will not blink unnecessarily on the device.

Assume we have the LED set to 0. Two request come in which enable the LED and immediately disable it. The current situation with two CPUs would be:

  usb_hidinput_input_event()       |      hid_led()
  ---------------------------------+----------------------------------
    spin_lock(&usbhid->lock);
    hid_set_field(1);
    spin_unlock(&usbhid->lock);
    schedule_work(...);
                                      spin_lock(&usbhid->lock);
                                      __usbhid_submit_report(..1..);
                                      spin_unlock(&usbhid->lock);
    spin_lock(&usbhid->lock);
    hid_set_field(0);
    spin_unlock(&usbhid->lock);
    schedule_work(...);
                                      spin_lock(&usbhid->lock);
                                      __usbhid_submit_report(..0..);
                                      spin_unlock(&usbhid->lock);

With the locking removed, we _might_ end up with (look at the changed __usbhid_submit_report() parameters in the first try!):

  usb_hidinput_input_event()       |      hid_led()
  ---------------------------------+----------------------------------
    hid_set_field(1);
    schedule_work(...);
                                      spin_lock(&usbhid->lock);
    hid_set_field(0);
    schedule_work(...);
                                      __usbhid_submit_report(..0..);
                                      spin_unlock(&usbhid->lock);
                                      ... next work ...

                                      spin_lock(&usbhid->lock);
                                      __usbhid_submit_report(..0..);
                                      spin_unlock(&usbhid->lock);

As one can see, we no longer send the "LED ON" signal as it is disabled immediately afterwards and the following "LED OFF" request overwrites the pending "LED ON".

It is important to note that hid_set_field() is not atomic, so we might also end up with any other value. But that doesn't matter either as we _always_ schedule the next work with a correct value and schedule_work() acts as memory barrier, anyways. So in the worst case, we run __usbhid_submit_report(..<garbage>..) in the first case and the following __usbhid_submit_report() will write the correct value. But LED states are booleans so any garbage will be converted to either 0 or 1 and the remote device will never see invalid requests.

Quoted 6 lines

Why all this? It avoids any custom locking around hid_set_field() in usbhid and finally allows us to provide a generic hidinput_input_event() handler for all HID transport drivers.

---

Hi David,

that was a very good commit message!

Cheers,
Benjamin

#5 lore raw

Re: [RFC 2/8] HID: usbhid: update LED fields unlocked

Quoted 77 lines · Benjamin Tissoires
Quoted 77 lines

Report fields can be updated from HID drivers unlocked via hid_set_field(). It is protected by input_lock in HID core so only a single input event is handled at a time. USBHID can thus update the field unlocked and doesn't conflict with any HID vendor/device drivers. Note, many HID drivers make heavy use of hid_set_field() in that way.

But usbhid also schedules a work to gather multiple LED changes in a single report. Hence, we used to lock the LED field update so the work can read a consistent state. However, hid_set_field() only writes a single integer field, which is guaranteed to be allocated all the time. So the worst possible race-condition is a garbage read on the LED field.

Therefore, there is no need to protect the update. In fact, the only thing that is prevented by locking hid_set_field(), is an LED update while the scheduled work currently writes an older LED update out. However, this means, a new work is scheduled directly when the old one is done writing the new state to the device. So we actually _win_ by not protecting the write and allowing the write to be combined with the current write. A new worker is still scheduled, but will not write any new state. So the LED will not blink unnecessarily on the device.

Assume we have the LED set to 0. Two request come in which enable the LED and immediately disable it. The current situation with two CPUs would be:

  usb_hidinput_input_event()       |      hid_led()
  ---------------------------------+----------------------------------
    spin_lock(&usbhid->lock);
    hid_set_field(1);
    spin_unlock(&usbhid->lock);
    schedule_work(...);
                                      spin_lock(&usbhid->lock);
                                      __usbhid_submit_report(..1..);
                                      spin_unlock(&usbhid->lock);
    spin_lock(&usbhid->lock);
    hid_set_field(0);
    spin_unlock(&usbhid->lock);
    schedule_work(...);
                                      spin_lock(&usbhid->lock);
                                      __usbhid_submit_report(..0..);
                                      spin_unlock(&usbhid->lock);

With the locking removed, we _might_ end up with (look at the changed __usbhid_submit_report() parameters in the first try!):

  usb_hidinput_input_event()       |      hid_led()
  ---------------------------------+----------------------------------
    hid_set_field(1);
    schedule_work(...);
                                      spin_lock(&usbhid->lock);
    hid_set_field(0);
    schedule_work(...);
                                      __usbhid_submit_report(..0..);
                                      spin_unlock(&usbhid->lock);
                                      ... next work ...

                                      spin_lock(&usbhid->lock);
                                      __usbhid_submit_report(..0..);
                                      spin_unlock(&usbhid->lock);

As one can see, we no longer send the "LED ON" signal as it is disabled immediately afterwards and the following "LED OFF" request overwrites the pending "LED ON".

It is important to note that hid_set_field() is not atomic, so we might also end up with any other value. But that doesn't matter either as we _always_ schedule the next work with a correct value and schedule_work() acts as memory barrier, anyways. So in the worst case, we run __usbhid_submit_report(..<garbage>..) in the first case and the following __usbhid_submit_report() will write the correct value. But LED states are booleans so any garbage will be converted to either 0 or 1 and the remote device will never see invalid requests.

Why all this? It avoids any custom locking around hid_set_field() in usbhid and finally allows us to provide a generic hidinput_input_event() handler for all HID transport drivers.

Quoted 8 lines
Quoted 2 lines

---

Hi David,

that was a very good commit message!

I love this patch :) Thanks David, thanks Benjamin.

Signature · 2 lines
Jiri Kosina
SUSE Labs
#6 lore raw

RFC 3/8 HID: input: generic hidinput_input_event handler

In reply to David Herrmann

The hidinput_input_event() callback converts input events written from userspace into HID reports and sends them to the device. We currently implement this in every HID transport driver, even though most of them do the same.

This provides a generic hidinput_input_event() implementation which is mostly copied from usbhid. It uses a delayed worker to allow multiple LED events to be collected into a single output event.
We use the custom ->request() transport driver callback to allow drivers to adjust the outgoing report and handle the request asynchronously. If no custom ->request() callback is available, we fall back to the generic raw output report handler (which is synchronous).

Drivers can still provide custom hidinput_input_event() handlers (see logitech-dj) if the generic implementation doesn't fit their needs.

---
 drivers/hid/hid-input.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/hid.h     |  1 +
 2 files changed, 80 insertions(+), 1 deletion(-)
drivers/hid/hid-input.c +79 -1
index 7480799..308eee8 100644
@@ -1137,6 +1137,74 @@ unsigned int hidinput_count_leds(struct hid_device *hid)
 }
 EXPORT_SYMBOL_GPL(hidinput_count_leds);
 
+static void hidinput_led_worker(struct work_struct *work)
+{
+	struct hid_device *hid = container_of(work, struct hid_device,
+					      led_work);
+	struct hid_field *field;
+	struct hid_report *report;
+	int len;
+	__u8 *buf;
+
+	field = hidinput_get_led_field(hid);
+	if (!field)
+		return;
+
+	/*
+	 * field->report is accessed unlocked regarding HID core. So there might
+	 * be another incoming SET-LED request from user-space, which changes
+	 * the LED state while we assemble our outgoing buffer. However, this
+	 * doesn't matter as hid_output_report() correctly converts it into a
+	 * boolean value no matter what information is currently set on the LED
+	 * field (even garbage). So the remote device will always get a valid
+	 * request.
+	 * And in case we send a wrong value, a next led worker is spawned
+	 * for every SET-LED request so the following worker will send the
+	 * correct value, guaranteed!
+	 */
+
+	report = field->report;
+
+	/* use custom SET_REPORT request if possible (asynchronous) */
+	if (hid->ll_driver->request)
+		return hid->ll_driver->request(hid, report, HID_REQ_SET_REPORT);
+
+	/* fall back to generic raw-output-report */
+	len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf)
+		return;
+
+	hid_output_report(report, buf);
+	/* synchronous output report */
+	hid->hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT);
+	kfree(buf);
+}
+
+static int 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;
+
+	if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) {
+		hid_warn(dev, "event field not found\n");
+		return -1;
+	}
+
+	hid_set_field(field, offset, value);
+
+	schedule_work(&hid->led_work);
+	return 0;
+}
+
 static int hidinput_open(struct input_dev *dev)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
@@ -1183,7 +1251,10 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid)
 	}
 
 	input_set_drvdata(input_dev, hid);
-	input_dev->event = hid->ll_driver->hidinput_input_event;
+	if (hid->ll_driver->hidinput_input_event)
+		input_dev->event = hid->ll_driver->hidinput_input_event;
+	else if (hid->ll_driver->request || hid->hid_output_raw_report)
+		input_dev->event = hidinput_input_event;
 	input_dev->open = hidinput_open;
 	input_dev->close = hidinput_close;
 	input_dev->setkeycode = hidinput_setkeycode;
@@ -1278,6 +1349,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
 	int i, j, k;
 
 	INIT_LIST_HEAD(&hid->inputs);
+	INIT_WORK(&hid->led_work, hidinput_led_worker);
 
 	if (!force) {
 		for (i = 0; i < hid->maxcollection; i++) {
@@ -1379,6 +1451,12 @@ void hidinput_disconnect(struct hid_device *hid)
 		input_unregister_device(hidinput->input);
 		kfree(hidinput);
 	}
+
+	/* led_work is spawned by input_dev callbacks, but doesn't access the
+	 * parent input_dev at all. Once all input devices are removed, we
+	 * know that led_work will never get restarted, so we can cancel it
+	 * synchronously and are safe. */
+	cancel_work_sync(&hid->led_work);
 }
 EXPORT_SYMBOL_GPL(hidinput_disconnect);
 
include/linux/hid.h +1 -0
index b8058c5..ea4b828 100644
@@ -456,6 +456,7 @@ struct hid_device {							/* device report descriptor */
 	enum hid_type type;						/* device type (mouse, kbd, ...) */
 	unsigned country;						/* HID country */
 	struct hid_report_enum report_enum[HID_REPORT_TYPES];
+	struct work_struct led_work;					/* delayed LED worker */
 
 	struct semaphore driver_lock;					/* protects the current driver, except during input */
 	struct semaphore driver_input_lock;				/* protects the current driver */
Signature · 1 line
1.8.3.2
#7 lore raw

Re: [RFC 3/8] HID: input: generic hidinput_input_event handler

Quoted 63 lines · David Herrmann

The hidinput_input_event() callback converts input events written from userspace into HID reports and sends them to the device. We currently implement this in every HID transport driver, even though most of them do the same.

This provides a generic hidinput_input_event() implementation which is mostly copied from usbhid. It uses a delayed worker to allow multiple LED events to be collected into a single output event.
We use the custom ->request() transport driver callback to allow drivers to adjust the outgoing report and handle the request asynchronously. If no custom ->request() callback is available, we fall back to the generic raw output report handler (which is synchronous).

Drivers can still provide custom hidinput_input_event() handlers (see logitech-dj) if the generic implementation doesn't fit their needs.

---
 drivers/hid/hid-input.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/hid.h     |  1 +
 2 files changed, 80 insertions(+), 1 deletion(-)
drivers/hid/hid-input.c +33 -0
index 7480799..308eee8 100644
@@ -1137,6 +1137,74 @@ unsigned int hidinput_count_leds(struct hid_device *hid)
 }
 EXPORT_SYMBOL_GPL(hidinput_count_leds);
 
+static void hidinput_led_worker(struct work_struct *work)
+{
+       struct hid_device *hid = container_of(work, struct hid_device,
+                                             led_work);
+       struct hid_field *field;
+       struct hid_report *report;
+       int len;
+       __u8 *buf;
+
+       field = hidinput_get_led_field(hid);
+       if (!field)
+               return;
+
+       /*
+        * field->report is accessed unlocked regarding HID core. So there might
+        * be another incoming SET-LED request from user-space, which changes
+        * the LED state while we assemble our outgoing buffer. However, this
+        * doesn't matter as hid_output_report() correctly converts it into a
+        * boolean value no matter what information is currently set on the LED
+        * field (even garbage). So the remote device will always get a valid
+        * request.
+        * And in case we send a wrong value, a next led worker is spawned
+        * for every SET-LED request so the following worker will send the
+        * correct value, guaranteed!
+        */
+
+       report = field->report;
+
+       /* use custom SET_REPORT request if possible (asynchronous) */
+       if (hid->ll_driver->request)
+               return hid->ll_driver->request(hid, report, HID_REQ_SET_REPORT);
+
+       /* fall back to generic raw-output-report */

drivers/hid/hid-input.c unsigned int hidinput_count_leds(struct hid_device *hid) · line 1182

Quoted 10 lines
drivers/hid/hid-input.c +10 -0
+       len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
+       buf = kmalloc(len, GFP_KERNEL);
+       if (!buf)
+               return;
+
+       hid_output_report(report, buf);
+       /* synchronous output report */
+       hid->hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT);
+       kfree(buf);
+}

Instead of writing a specific fallback in case hid->ll_driver->request does not exist, I think it would make sense to implement a generic hid_hw_request function in hid-input, so that HIDP and UHID will benefit from it. I think it will be better because the implementation I made in i2c-hid.c is nearly the exact same calls than the fallback here.

Quoted 29 lines
drivers/hid/hid-input.c +25 -0
+
+static int 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;
+
+       if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) {
+               hid_warn(dev, "event field not found\n");
+               return -1;
+       }
+
+       hid_set_field(field, offset, value);
+
+       schedule_work(&hid->led_work);
+       return 0;
+}
+
 static int hidinput_open(struct input_dev *dev)
 {
        struct hid_device *hid = input_get_drvdata(dev);

drivers/hid/hid-input.c static struct hid_input *hidinput_allocate(struct hid_device *hid) · line 1257

Quoted 8 lines
drivers/hid/hid-input.c +4 -1
@@ -1183,7 +1251,10 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid)
        }
 
        input_set_drvdata(input_dev, hid);
-       input_dev->event = hid->ll_driver->hidinput_input_event;
+       if (hid->ll_driver->hidinput_input_event)
+               input_dev->event = hid->ll_driver->hidinput_input_event;
+       else if (hid->ll_driver->request || hid->hid_output_raw_report)
+               input_dev->event = hidinput_input_event;

with a generic hid_hw_request in hid-input, the else is simpler here.

Quoted 12 lines
drivers/hid/hid-input.c +1 -0
        input_dev->open = hidinput_open;
        input_dev->close = hidinput_close;
        input_dev->setkeycode = hidinput_setkeycode;
@@ -1278,6 +1349,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
        int i, j, k;
 
        INIT_LIST_HEAD(&hid->inputs);
+       INIT_WORK(&hid->led_work, hidinput_led_worker);
 
        if (!force) {
                for (i = 0; i < hid->maxcollection; i++) {

drivers/hid/hid-input.c void hidinput_disconnect(struct hid_device *hid) · line 1459

Quoted 9 lines
drivers/hid/hid-input.c +6 -0
@@ -1379,6 +1451,12 @@ void hidinput_disconnect(struct hid_device *hid)
                input_unregister_device(hidinput->input);
                kfree(hidinput);
        }
+
+       /* led_work is spawned by input_dev callbacks, but doesn't access the
+        * parent input_dev at all. Once all input devices are removed, we
+        * know that led_work will never get restarted, so we can cancel it
+        * synchronously and are safe. */
+       cancel_work_sync(&hid->led_work);

You missed the multi-lines comment formatting style on this one :)

Quoted 16 lines
drivers/hid/hid-input.c +0 -0
 }
 EXPORT_SYMBOL_GPL(hidinput_disconnect);
 
include/linux/hid.h +1 -1
index b8058c5..ea4b828 100644
@@ -456,6 +456,7 @@ struct hid_device {                                                 /* device report descriptor */
        enum hid_type type;                                             /* device type (mouse, kbd, ...) */
        unsigned country;                                               /* HID country */
        struct hid_report_enum report_enum[HID_REPORT_TYPES];
+       struct work_struct led_work;                                    /* delayed LED worker */
 
        struct semaphore driver_lock;                                   /* protects the current driver, except during input */
        struct semaphore driver_input_lock;                             /* protects the current driver */
--
Quoted 2 lines

1.8.3.2

Cheers,
Benjamin

#8 lore raw

Re: [RFC 3/8] HID: input: generic hidinput_input_event handler

Hi

Quoted 73 lines · Benjamin Tissoires
Quoted 72 lines · David Herrmann

The hidinput_input_event() callback converts input events written from userspace into HID reports and sends them to the device. We currently implement this in every HID transport driver, even though most of them do the same.

This provides a generic hidinput_input_event() implementation which is mostly copied from usbhid. It uses a delayed worker to allow multiple LED events to be collected into a single output event.
We use the custom ->request() transport driver callback to allow drivers to adjust the outgoing report and handle the request asynchronously. If no custom ->request() callback is available, we fall back to the generic raw output report handler (which is synchronous).

Drivers can still provide custom hidinput_input_event() handlers (see logitech-dj) if the generic implementation doesn't fit their needs.

---
 drivers/hid/hid-input.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/hid.h     |  1 +
 2 files changed, 80 insertions(+), 1 deletion(-)
drivers/hid/hid-input.c +42 -0
index 7480799..308eee8 100644
@@ -1137,6 +1137,74 @@ unsigned int hidinput_count_leds(struct hid_device *hid)
 }
 EXPORT_SYMBOL_GPL(hidinput_count_leds);
 
+static void hidinput_led_worker(struct work_struct *work)
+{
+       struct hid_device *hid = container_of(work, struct hid_device,
+                                             led_work);
+       struct hid_field *field;
+       struct hid_report *report;
+       int len;
+       __u8 *buf;
+
+       field = hidinput_get_led_field(hid);
+       if (!field)
+               return;
+
+       /*
+        * field->report is accessed unlocked regarding HID core. So there might
+        * be another incoming SET-LED request from user-space, which changes
+        * the LED state while we assemble our outgoing buffer. However, this
+        * doesn't matter as hid_output_report() correctly converts it into a
+        * boolean value no matter what information is currently set on the LED
+        * field (even garbage). So the remote device will always get a valid
+        * request.
+        * And in case we send a wrong value, a next led worker is spawned
+        * for every SET-LED request so the following worker will send the
+        * correct value, guaranteed!
+        */
+
+       report = field->report;
+
+       /* use custom SET_REPORT request if possible (asynchronous) */
+       if (hid->ll_driver->request)
+               return hid->ll_driver->request(hid, report, HID_REQ_SET_REPORT);
+
+       /* fall back to generic raw-output-report */
+       len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
+       buf = kmalloc(len, GFP_KERNEL);
+       if (!buf)
+               return;
+
+       hid_output_report(report, buf);
+       /* synchronous output report */
+       hid->hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT);
+       kfree(buf);
Quoted 8 lines

drivers/hid/hid-input.c unsigned int hidinput_count_leds(struct hid_device *hid) · line 1182

Quoted 1 line
drivers/hid/hid-input.c +1 -0
+}

Instead of writing a specific fallback in case hid->ll_driver->request does not exist, I think it would make sense to implement a generic hid_hw_request function in hid-input, so that HIDP and UHID will benefit from it. I think it will be better because the implementation I made in i2c-hid.c is nearly the exact same calls than the fallback here.

Yepp, that sounds about right. However, with the current hid_output_raw_report() callbacks we cannot implement this as they work differently on each transport driver. That's why I introduced the raw_request() and output_report() helpers. These will allow me to do that.

So Jiri, feel free to drop this patch and I will rebase it on the new series at the end with the new helpers. Otherwise, I will add a patch at the end which provides a generic fallback for request().

Quoted 49 lines
Quoted 29 lines
drivers/hid/hid-input.c +25 -0
+
+static int 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;
+
+       if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) {
+               hid_warn(dev, "event field not found\n");
+               return -1;
+       }
+
+       hid_set_field(field, offset, value);
+
+       schedule_work(&hid->led_work);
+       return 0;
+}
+
 static int hidinput_open(struct input_dev *dev)
 {
        struct hid_device *hid = input_get_drvdata(dev);

drivers/hid/hid-input.c static struct hid_input *hidinput_allocate(struct hid_device *hid) · line 1257

Quoted 8 lines
drivers/hid/hid-input.c +4 -1
@@ -1183,7 +1251,10 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid)
        }
 
        input_set_drvdata(input_dev, hid);
-       input_dev->event = hid->ll_driver->hidinput_input_event;
+       if (hid->ll_driver->hidinput_input_event)
+               input_dev->event = hid->ll_driver->hidinput_input_event;
+       else if (hid->ll_driver->request || hid->hid_output_raw_report)
+               input_dev->event = hidinput_input_event;

with a generic hid_hw_request in hid-input, the else is simpler here.

Quoted 9 lines
drivers/hid/hid-input.c +1 -0
        input_dev->open = hidinput_open;
        input_dev->close = hidinput_close;
        input_dev->setkeycode = hidinput_setkeycode;
@@ -1278,6 +1349,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
        int i, j, k;
 
        INIT_LIST_HEAD(&hid->inputs);
+       INIT_WORK(&hid->led_work, hidinput_led_worker);
 
Quoted 14 lines

drivers/hid/hid-input.c void hidinput_disconnect(struct hid_device *hid) · line 1459

Quoted 12 lines
drivers/hid/hid-input.c +6 -0
        if (!force) {
                for (i = 0; i < hid->maxcollection; i++) {
@@ -1379,6 +1451,12 @@ void hidinput_disconnect(struct hid_device *hid)
                input_unregister_device(hidinput->input);
                kfree(hidinput);
        }
+
+       /* led_work is spawned by input_dev callbacks, but doesn't access the
+        * parent input_dev at all. Once all input devices are removed, we
+        * know that led_work will never get restarted, so we can cancel it
+        * synchronously and are safe. */
+       cancel_work_sync(&hid->led_work);

You missed the multi-lines comment formatting style on this one :)

The ./net/ subsystem uses these comments quite a lot and there was a discussion between davem and linus with the conclusion that these comments are ok. But I actually don't care, so I can change to normal CodingStyle.

Thanks for reviewing!
David

Quoted 21 lines
Quoted 16 lines
drivers/hid/hid-input.c +0 -0
 }
 EXPORT_SYMBOL_GPL(hidinput_disconnect);
 
include/linux/hid.h +1 -1
index b8058c5..ea4b828 100644
@@ -456,6 +456,7 @@ struct hid_device {                                                 /* device report descriptor */
        enum hid_type type;                                             /* device type (mouse, kbd, ...) */
        unsigned country;                                               /* HID country */
        struct hid_report_enum report_enum[HID_REPORT_TYPES];
+       struct work_struct led_work;                                    /* delayed LED worker */
 
        struct semaphore driver_lock;                                   /* protects the current driver, except during input */
        struct semaphore driver_input_lock;                             /* protects the current driver */
--
Quoted 2 lines

1.8.3.2

Cheers,
Benjamin

#9 lore raw

Re: [RFC 3/8] HID: input: generic hidinput_input_event handler

Quoted 7 lines · David Herrmann
Quoted 7 lines
Quoted 6 lines
drivers/hid/hid-input.c +6 -0
+
+       /* led_work is spawned by input_dev callbacks, but doesn't access the
+        * parent input_dev at all. Once all input devices are removed, we
+        * know that led_work will never get restarted, so we can cancel it
+        * synchronously and are safe. */
+       cancel_work_sync(&hid->led_work);
Quoted 6 lines
Quoted 1 line

You missed the multi-lines comment formatting style on this one :)

The ./net/ subsystem uses these comments quite a lot and there was a discussion between davem and linus with the conclusion that these comments are ok. But I actually don't care, so I can change to normal CodingStyle.

I once got grilled by Dave for submitting patch to netdev with such comment, but that didn't change my opinion, and I don't care for my subsystem :)

Signature · 2 lines
Jiri Kosina
SUSE Labs
#10 lore raw

RFC 4/8 HID: usbhid: use generic hidinput_input_event()

In reply to David Herrmann

HID core provides the same functionality as we do, so drop the custom hidinput_input_event() handler.

---
 drivers/hid/usbhid/hid-core.c | 74 ++-----------------------------------------
 drivers/hid/usbhid/usbhid.h   |  3 --
 2 files changed, 3 insertions(+), 74 deletions(-)
drivers/hid/usbhid/hid-core.c +3 -71
index 62b5131..8c32357 100644
@@ -649,72 +649,6 @@ static void usbhid_submit_report(struct hid_device *hid, struct hid_report *repo
 	spin_unlock_irqrestore(&usbhid->lock, flags);
 }
 
-/* Workqueue routine to send requests to change LEDs */
-static void hid_led(struct work_struct *work)
-{
-	struct usbhid_device *usbhid =
-		container_of(work, struct usbhid_device, led_work);
-	struct hid_device *hid = usbhid->hid;
-	struct hid_field *field;
-	unsigned long flags;
-
-	field = hidinput_get_led_field(hid);
-	if (!field) {
-		hid_warn(hid, "LED event field not found\n");
-		return;
-	}
-
-	/*
-	 * field->report is accessed unlocked regarding HID core. So there might
-	 * be another incoming SET-LED request from user-space, which changes
-	 * the LED state while we assemble our outgoing buffer. However, this
-	 * doesn't matter as hid_output_report() correctly converts it into a
-	 * boolean value no matter what information is currently set on the LED
-	 * field (even garbage). So the remote device will always get a valid
-	 * request.
-	 * And in case we send a wrong value, a next hid_led() worker is spawned
-	 * for every SET-LED request so the following hid_led() worker will send
-	 * the correct value, guaranteed!
-	 */
-
-	spin_lock_irqsave(&usbhid->lock, flags);
-	if (!test_bit(HID_DISCONNECTED, &usbhid->iofl)) {
-		usbhid->ledcount = hidinput_count_leds(hid);
-		hid_dbg(usbhid->hid, "New ledcount = %u\n", usbhid->ledcount);
-		__usbhid_submit_report(hid, field->report, USB_DIR_OUT);
-	}
-	spin_unlock_irqrestore(&usbhid->lock, flags);
-}
-
-static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
-{
-	struct hid_device *hid = input_get_drvdata(dev);
-	struct usbhid_device *usbhid = hid->driver_data;
-	struct hid_field *field;
-	int offset;
-
-	if (type == EV_FF)
-		return input_ff_event(dev, type, code, value);
-
-	if (type != EV_LED)
-		return -1;
-
-	if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) {
-		hid_warn(dev, "event field not found\n");
-		return -1;
-	}
-
-	hid_set_field(field, offset, value);
-
-	/*
-	 * Defer performing requested LED action.
-	 * This is more likely gather all LED changes into a single URB.
-	 */
-	schedule_work(&usbhid->led_work);
-
-	return 0;
-}
-
 static int usbhid_wait_io(struct hid_device *hid)
 {
 	struct usbhid_device *usbhid = hid->driver_data;
@@ -1283,7 +1217,6 @@ static struct hid_ll_driver usb_hid_driver = {
 	.open = usbhid_open,
 	.close = usbhid_close,
 	.power = usbhid_power,
-	.hidinput_input_event = usb_hidinput_input_event,
 	.request = usbhid_request,
 	.wait = usbhid_wait_io,
 	.idle = usbhid_idle,
@@ -1377,8 +1310,6 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
 	setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
 	spin_lock_init(&usbhid->lock);
 
-	INIT_WORK(&usbhid->led_work, hid_led);
-
 	ret = hid_add_device(hid);
 	if (ret) {
 		if (ret != -ENODEV)
@@ -1411,7 +1342,6 @@ static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid)
 {
 	del_timer_sync(&usbhid->io_retry);
 	cancel_work_sync(&usbhid->reset_work);
-	cancel_work_sync(&usbhid->led_work);
 }
 
 static void hid_cease_io(struct usbhid_device *usbhid)
@@ -1531,15 +1461,17 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
 	struct usbhid_device *usbhid = hid->driver_data;
 	int status = 0;
 	bool driver_suspended = false;
+	unsigned int ledcount;
 
 	if (PMSG_IS_AUTO(message)) {
+		ledcount = hidinput_count_leds(hid);
 		spin_lock_irq(&usbhid->lock);	/* Sync with error handler */
 		if (!test_bit(HID_RESET_PENDING, &usbhid->iofl)
 		    && !test_bit(HID_CLEAR_HALT, &usbhid->iofl)
 		    && !test_bit(HID_OUT_RUNNING, &usbhid->iofl)
 		    && !test_bit(HID_CTRL_RUNNING, &usbhid->iofl)
 		    && !test_bit(HID_KEYS_PRESSED, &usbhid->iofl)
-		    && (!usbhid->ledcount || ignoreled))
+		    && (!ledcount || ignoreled))
 		{
 			set_bit(HID_SUSPENDED, &usbhid->iofl);
 			spin_unlock_irq(&usbhid->lock);
drivers/hid/usbhid/usbhid.h +0 -3
index dbb6af6..f633c24 100644
@@ -92,9 +92,6 @@ struct usbhid_device {
 	unsigned int retry_delay;                                       /* Delay length in ms */
 	struct work_struct reset_work;                                  /* Task context for resets */
 	wait_queue_head_t wait;						/* For sleeping */
-	int ledcount;							/* counting the number of active leds */
-
-	struct work_struct led_work;					/* Task context for setting LEDs */
 };
 
 #define	hid_to_usb_dev(hid_dev) \
Signature · 1 line
1.8.3.2
#12 lore raw

RFC 5/8 HID: i2c: use generic hidinput_input_event()

In reply to David Herrmann

HID core provides the same functionality, so drop the custom handler. Besides, the current handler doesn't schedule any outgoing report so it did not work, anyway.

---
 drivers/hid/i2c-hid/i2c-hid.c | 24 ------------------------
 1 file changed, 24 deletions(-)
drivers/hid/i2c-hid/i2c-hid.c +0 -24
index 879b0ed..db2253b 100644
@@ -756,29 +756,6 @@ static int i2c_hid_power(struct hid_device *hid, int lvl)
 	return ret;
 }
 
-static int i2c_hid_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;
-	}
-
-	return hid_set_field(field, offset, value);
-}
-
 static struct hid_ll_driver i2c_hid_ll_driver = {
 	.parse = i2c_hid_parse,
 	.start = i2c_hid_start,
@@ -787,7 +764,6 @@ static struct hid_ll_driver i2c_hid_ll_driver = {
 	.close = i2c_hid_close,
 	.power = i2c_hid_power,
 	.request = i2c_hid_request,
-	.hidinput_input_event = i2c_hid_hidinput_input_event,
 };
 
 static int i2c_hid_init_irq(struct i2c_client *client)
Signature · 1 line
1.8.3.2
Reviewed-by on v1 5/8
#13 lore raw

Re: [RFC 5/8] HID: i2c: use generic hidinput_input_event()

Quoted 6 lines · David Herrmann

HID core provides the same functionality, so drop the custom handler. Besides, the current handler doesn't schedule any outgoing report so it did not work, anyway.

---

Yep, by re-reading it, the led are not updated at all... :( Thanks!

Cheers,
Benjamin

#14 lore raw

RFC 6/8 HID: uhid: use generic hidinput_input_event()

In reply to David Herrmann

HID core provides the same functionality and can convert the input event to a raw output report. We can thus drop UHID_OUTPUT_EV and rely on the mandatory UHID_OUTPUT.

User-space wasn't able to do anything with UHID_OUTPUT_EV, anyway. They don't have access to the report fields.

---
 Documentation/hid/uhid.txt |  4 +++-
 drivers/hid/uhid.c         | 25 -------------------------
 include/uapi/linux/uhid.h  |  4 +++-
 3 files changed, 6 insertions(+), 27 deletions(-)
Documentation/hid/uhid.txt +3 -1
index 3c74121..dc35a2b 100644
@@ -149,11 +149,13 @@ needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads.
   is of type "struct uhid_data_req".
   This may be received even though you haven't received UHID_OPEN, yet.
 
-  UHID_OUTPUT_EV:
+  UHID_OUTPUT_EV (obsolete):
   Same as UHID_OUTPUT but this contains a "struct input_event" as payload. This
   is called for force-feedback, LED or similar events which are received through
   an input device by the HID subsystem. You should convert this into raw reports
   and send them to your device similar to events of type UHID_OUTPUT.
+  This is no longer sent by newer kernels. Instead, HID core converts it into a
+  raw output report and sends it via UHID_OUTPUT.
 
   UHID_FEATURE:
   This event is sent if the kernel driver wants to perform a feature request as
drivers/hid/uhid.c +0 -25
index fc307e0..f53f2d5 100644
@@ -116,30 +116,6 @@ static void uhid_hid_close(struct hid_device *hid)
 	uhid_queue_event(uhid, UHID_CLOSE);
 }
 
-static int uhid_hid_input(struct input_dev *input, unsigned int type,
-			  unsigned int code, int value)
-{
-	struct hid_device *hid = input_get_drvdata(input);
-	struct uhid_device *uhid = hid->driver_data;
-	unsigned long flags;
-	struct uhid_event *ev;
-
-	ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
-	if (!ev)
-		return -ENOMEM;
-
-	ev->type = UHID_OUTPUT_EV;
-	ev->u.output_ev.type = type;
-	ev->u.output_ev.code = code;
-	ev->u.output_ev.value = value;
-
-	spin_lock_irqsave(&uhid->qlock, flags);
-	uhid_queue(uhid, ev);
-	spin_unlock_irqrestore(&uhid->qlock, flags);
-
-	return 0;
-}
-
 static int uhid_hid_parse(struct hid_device *hid)
 {
 	struct uhid_device *uhid = hid->driver_data;
@@ -273,7 +249,6 @@ static struct hid_ll_driver uhid_hid_driver = {
 	.stop = uhid_hid_stop,
 	.open = uhid_hid_open,
 	.close = uhid_hid_close,
-	.hidinput_input_event = uhid_hid_input,
 	.parse = uhid_hid_parse,
 };
 
include/uapi/linux/uhid.h +3 -1
index e9ed951..414b74b 100644
@@ -30,7 +30,7 @@ enum uhid_event_type {
 	UHID_OPEN,
 	UHID_CLOSE,
 	UHID_OUTPUT,
-	UHID_OUTPUT_EV,
+	UHID_OUTPUT_EV,			/* obsolete! */
 	UHID_INPUT,
 	UHID_FEATURE,
 	UHID_FEATURE_ANSWER,
@@ -69,6 +69,8 @@ struct uhid_output_req {
 	__u8 rtype;
 } __attribute__((__packed__));
 
+/* Obsolete! Newer kernels will no longer send these events but instead convert
+ * it into raw output reports via UHID_OUTPUT. */
 struct uhid_output_ev_req {
 	__u16 type;
 	__u16 code;
Signature · 1 line
1.8.3.2
Acked-by on v1 6/8
#15 lore raw

Re: [RFC 6/8] HID: uhid: use generic hidinput_input_event()

Quoted 9 lines · David Herrmann

HID core provides the same functionality and can convert the input event to a raw output report. We can thus drop UHID_OUTPUT_EV and rely on the mandatory UHID_OUTPUT.

User-space wasn't able to do anything with UHID_OUTPUT_EV, anyway. They don't have access to the report fields.

---

I personally don't use UHID_OUTPUT_EV in hid-replay (nor UHID_OUTPUT either :-P ).

Cheers,
Benjamin

#16 lore raw

Re: [RFC 6/8] HID: uhid: use generic hidinput_input_event()

In reply to David Herrmann

Hi David,

Quoted 6 lines

HID core provides the same functionality and can convert the input event to a raw output report. We can thus drop UHID_OUTPUT_EV and rely on the mandatory UHID_OUTPUT.

User-space wasn't able to do anything with UHID_OUTPUT_EV, anyway. They don't have access to the report fields.

I like your patchset overall, but this looks like userspace breakage?

Thanks,
Henrik

#17 lore raw

Re: [RFC 6/8] HID: uhid: use generic hidinput_input_event()

Hi

Quoted 10 lines · On Thu, Jul 18, 2013 at 9:53 PM, <[email protected]> wrote:

Hi David,

Quoted 6 lines

HID core provides the same functionality and can convert the input event to a raw output report. We can thus drop UHID_OUTPUT_EV and rely on the mandatory UHID_OUTPUT.

User-space wasn't able to do anything with UHID_OUTPUT_EV, anyway. They don't have access to the report fields.

I like your patchset overall, but this looks like userspace breakage?

Not really. UHID_OUTPUT_EV provides input_event data to user-space, which user-space converts into raw output events. With this patch, we do the conversion in the kernel itself and just send the raw output event (which user-space supported even before this).

This breaks user-space only if the conversion was done differently in user-space. But this sounds highly unlikely to me. All uhid applications I am aware of ignored UHID_OUTPUT_EV so far as they don't have any report-information. So we actually *fix* all available users with that.

If there is an uhid user I am not aware of, we can easily revert that. I doubt that, though.

Cheers
David

#18 lore raw

RFC 7/8 HID: add transport driver documentation

In reply to David Herrmann

hid-transport.txt describes the transport driver layout which is required by HID-core to work correctly. HID-core supports many different transport drivers and new drivers can be added for any kind of I/O system.

The current layout doesn't really differentiate between intr and ctrl channels, which confuses some hid-drivers and may break devices. HIDP, USBHID and I2DHID all implement different semantics for each callback, so our device drivers aren't really transport-driver-agnostic.

To solve that, hid-transport.txt describes the layout of transport drivers that is required by HID core. Furthermore, it introduces some new callbacks which replace the current hid_get_raw_report() and hid_output_raw_report() hooks. These will be implemented in follow-up patches.

---
 Documentation/hid/hid-transport.txt | 299 ++++++++++++++++++++++++++++++++++++
 1 file changed, 299 insertions(+)
 create mode 100644 Documentation/hid/hid-transport.txt
Documentation/hid/hid-transport.txt new file +299 -0
new file mode 100644
index 0000000..034f11d
@@ -0,0 +1,299 @@
+                          HID I/O Transport Drivers
+                         ===========================
+
+The HID subsystem is independent of the underlying transport driver. Initially,
+only USB was supported, but other specifications adopted the HID design and
+provided new transport drivers. The kernel includes at least support for USB,
+Bluetooth, i2c and user-space I/O drivers.
+
+1) HID Bus
+==========
+
+The HID subsystem is designed as a bus. Any I/O subsystem may provide HID
+devices and register them with the HID bus. HID core then loads generic device
+drivers on top of it. The transport drivers are responsible of raw data
+transport and device setup/management. HID core is responsible of
+report-parsing, report interpretation and the user-space API. Device specifics
+and quirks are also handled by HID core and the HID device drivers.
+
+ +-----------+  +-----------+            +-----------+  +-----------+
+ | Device #1 |  | Device #i |            | Device #j |  | Device #k |
+ +-----------+  +-----------+            +-----------+  +-----------+
+          \\      //                              \\      //
+        +------------+                          +------------+
+        | I/O Driver |                          | I/O Driver |
+        +------------+                          +------------+
+              ||                                      ||
+     +------------------+                    +------------------+
+     | Transport Driver |                    | Transport Driver |
+     +------------------+                    +------------------+
+                       \___                ___/
+                           \              /
+                          +----------------+
+                          |    HID Core    |
+                          +----------------+
+                           /  |        |  \
+                          /   |        |   \
+             ____________/    |        |    \_________________
+            /                 |        |                      \
+           /                  |        |                       \
+ +----------------+  +-----------+  +------------------+  +------------------+
+ | Generic Driver |  | MT Driver |  | Custom Driver #1 |  | Custom Driver #2 |
+ +----------------+  +-----------+  +------------------+  +------------------+
+
+Example Drivers:
+  I/O: USB, I2C, Bluetooth-l2cap
+  Transport: USB-HID, I2C-HID, BT-HIDP
+
+Normally, a transport driver is specific for a given I/O driver. But the HID
+design also allows transport drivers to work with different I/O systems at the
+same time.
+
+Everything below "HID Core" is simplified in this graph as it is only of
+interest to HID device drivers. Transport drivers do not need to know the
+specifics.
+
+1.1) Device Setup
+-----------------
+
+I/O drivers normally provide hotplug detection or device enumeration APIs to the
+transport drivers. Transport drivers use this to find any suitable HID device.
+They allocate HID device objects and register them with HID core. Transport
+drivers are not required to register themselves with HID core. HID core is never
+aware of which transport drivers are available and is not interested in it. It
+is only interested in devices.
+
+Transport drivers attach a constant "struct hid_ll_driver" object with each
+device. Once a device is registered with HID core, the callbacks provided via
+this struct are used by HID core to communicate with the device.
+
+Transport drivers are responsible of detecting device failures and unplugging.
+HID core will operate an device as long as it is registered regardless of any
+device failures. Once transport drivers detect unplug or failure events, they
+must unregister the device from HID core and HID core will stop using the
+provided callbacks.
+
+1.2) Transport Driver Requirements
+----------------------------------
+
+HID core requires transport drivers to follow a given design. A Transport
+drivers must provide two bi-directional I/O channels to each HID device. These
+channels might be multiplexed on a single physical channel, though.
+
+ - Interrupt Channel (intr): The intr channel is used for asynchronous data
+   reports. No management commands or data acknowledgements are sent on this
+   channel. Any unrequested incoming or outgoing data report must be sent on
+   this channel and is never acknowledged by the remote side. Devices usually
+   send their input events on this channel. Outgoing events are normally
+   not send via intr, except if high throughput is required.
+ - Control Channel (ctrl): The ctrl channel is used for synchronous requests and
+   device management. Unrequested data input events must not be sent on this
+   channel and are normally ignored. Instead, devices only send management
+   events or answers to host requests on this channel.
+   Outgoing reports are usually sent on the ctrl channel via synchronous
+   SET_REPORT requests.
+
+Communication between devices and HID core is mostly done via HID reports. A
+report can be of one of three types:
+
+ - INPUT Report: Input reports provide data from device to host. This
+   data may include button events, axis events, battery status or more. This
+   data is generated by the device and sent to the host without requiring
+   explicit requests. Devices can choose to send data continously or only on
+   change.
+ - OUTPUT Report: Output reports change device states. They are sent from host
+   to device and may include LED requests, rumble requests or more. Output
+   reports are never sent from device to host, except if explicitly requested.
+   Hosts may choose to send output reports either continously or only on change.
+ - FEATURE Report: Feature reports can be anything that doesn't belong into the
+   other two categories. Some of them are generic and defined by the HID spec,
+   but most of them are used as custom device extensions. For instance, they may
+   provide battery status information.
+   Feature reports are never sent without requests. A host must explicitly
+   request a device to set or send a feature report. This also means, feature
+   reports are never sent on the intr channel as this channel is asynchronous.
+
+INPUT and OUTPUT reports can be sent as pure data reports on the intr channel.
+For INPUT reports this is the usual operational mode. But for OUTPUT reports,
+this is rarely done as OUTPUT reports are normally quite rare. But devices are
+free to make excessive use of asynchronous OUTPUT reports (for instance, custom
+HID audio speakers make great use of it).
+
+Raw reports must not be sent on the ctrl channel, though. Instead, the ctrl
+channel provides synchronous GET/SET_REPORT requests.
+
+ - GET_REPORT: A GET_REPORT request has a report ID as payload and is sent
+   from host to device. The device must answer with a data report for the
+   requested report ID on the ctrl channel as a synchronous acknowledgement.
+   Only one GET_REPORT request can be pending for each device. This restriction
+   is enforced by HID core as several transport drivers don't allow multiple
+   simultaneous GET_REPORT requests.
+   Note that data reports which are sent as answer to a GET_REPORT request are
+   not handled as generic device events. That is, if a device does not operate
+   in continuous data reporting mode, an answer to GET_REPORT does not replace
+   the raw data report on the intr channel on state change.
+   GET_REPORT is only used by custom HID device drivers to query device state.
+   Normally, HID core caches any device state so this request is not necessary
+   on devices that follow the HID specs.
+   GET_REPORT requests can be sent for any of the 3 report types and shall
+   return the current report state of the device.
+ - SET_REPORT: A SET_REPORT request has a report ID plus data as payload. It is
+   sent from host to device and a device must update it's current report state
+   according to the given data. Any of the 3 report types can be used.
+   A device must answer with a synchronous acknowledgement. However, HID core
+   does not require transport drivers to forward this acknowledgement to HID
+   core.
+   Same as for GET_REPORT, only one SET_REPORT can be pending at a time. This
+   restriction is enforced by HID core as some transport drivers do not support
+   multiple synchronous SET_REPORT requests.
+
+Other ctrl-channel requests are supported by USB-HID but are not available
+(or deprecated) in most other transport level specifications:
+
+ - GET/SET_IDLE: Only used by USB-HID. Do not implement!
+ - GET/SET_PROTOCOL: Not used by HID core. Do not implement!
+
+2) HID API
+==========
+
+2.1) Initialization
+-------------------
+
+Transport drivers normally use the following procedure to register a new device
+with HID core:
+
+	struct hid_device *hid;
+	int ret;
+
+	hid = hid_allocate_device();
+	if (IS_ERR(hid)) {
+		ret = PTR_ERR(hid);
+		goto err_<...>;
+	}
+
+	strlcpy(hid->name, <device-name-src>, 127);
+	strlcpy(hid->phys, <device-phys-src>, 63);
+	strlcpy(hid->uniq, <device-uniq-src>, 63);
+
+	hid->ll_driver = &custom_ll_driver;
+	hid->bus = <device-bus>;
+	hid->vendor = <device-vendor>;
+	hid->product = <device-product>;
+	hid->version = <device-version>;
+	hid->country = <device-country>;
+	hid->dev.parent = <pointer-to-parent-device>;
+	hid->driver_data = <transport-driver-data-field>;
+
+	ret = hid_add_device(hid);
+	if (ret)
+		goto err_<...>;
+
+Once hid_add_device() is entered, HID core might use the callbacks provided in
+"custom_ll_driver". To unregister a device, use:
+
+	hid_destroy_device(hid);
+
+Once hid_destroy_device() returns, HID core will no longer make use of any
+driver callbacks.
+
+2.2) hid_ll_driver operations
+-----------------------------
+
+The available HID callbacks are:
+ - int (*start) (struct hid_device *hdev)
+   Called from HID device drivers once they want to use the device. Transport
+   drivers can choose to setup their device in this callback. However, normally
+   devices are already set up before transport drivers register them to HID core
+   so this is mostly only used by USB-HID.
+
+ - void (*stop) (struct hid_device *hdev)
+   Called from HID device drivers once they are done with a device. Transport
+   drivers can free any buffers and deinitialize the device. But note that
+   ->start() might be called again if another HID device driver is loaded on the
+   device.
+   Transport drivers are free to ignore it and deinitialize devices after they
+   destroyed them via hid_destroy_device().
+
+ - int (*open) (struct hid_device *hdev)
+   Called from HID device drivers once they are interested in data reports.
+   Usually, while user-space didn't open any input API/etc., device drivers are
+   not interested in device data and transport drivers can put devices asleep.
+   However, once ->open() is called, transport drivers must be ready for I/O.
+   ->open() calls are never nested. So in between two ->open() calls there must
+   be a call to ->close().
+
+ - void (*close) (struct hid_device *hdev)
+   Called from HID device drivers after ->open() was called but they are no
+   longer interested in device reports. (Usually if user-space closed any input
+   devices of the driver).
+   Transport drivers can put devices asleep and terminate any I/O. However,
+   ->start() may be called again if the device driver is interested in input
+   reports again.
+
+ - int (*parse) (struct hid_device *hdev)
+   Called once during device setup after ->start() has been called. Transport
+   drivers must read the HID report-descriptor from the device and tell HID core
+   about it via hid_parse_report().
+
+ - int (*power) (struct hid_device *hdev, int level)
+   Called by HID core to give PM hints to transport drivers. Usually this is
+   analogical to the ->open() and ->close() hints and redundant.
+
+ - void (*request) (struct hid_device *hdev, struct hid_report *report,
+                    int reqtype)
+   Send an HID request on the ctrl channel. "report" contains the report that
+   should be sent and "reqtype" the request type. Request-type can be
+   HID_REQ_SET_REPORT or HID_REQ_GET_REPORT.
+   This callback is optional. If not provided, HID core will assemble a raw
+   report following the HID specs and send it via the ->raw_request() callback.
+   The transport driver is free to implement this asynchronously.
+
+ - int (*wait) (struct hid_device *hdev)
+   Used by HID core before calling ->request() again. A transport driver can use
+   it to wait for any pending requests to complete if only one request is
+   allowed at a time.
+
+ - int (*raw_request) (struct hid_device *hdev, unsigned char reportnum,
+                       __u8 *buf, size_t count, unsigned char rtype,
+                       int reqtype)
+   Same as ->request() but provides the report as raw buffer. This request shall
+   be synchronous. A transport driver must not use ->wait() to complete such
+   requests.
+
+ - int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len)
+   Send raw output report via intr channel. Used by some HID device drivers
+   which require high throughput for outgoing requests on the intr channel. This
+   must not cause SET_REPORT calls! This must be implemented as asynchronous
+   output report on the intr channel!
+
+ - int (*hidinput_input_event) (struct input_dev *idev, unsigned int type,
+                                unsigned int code, int value)
+   Obsolete callback used by logitech converters. It is called when userspace
+   writes input events to the input device (eg., EV_LED). A driver can use this
+   callback to convert it into an output report and send it to the device. If
+   this callback is not provided, HID core will use ->request() or
+   ->raw_request() respectively.
+
+ - int (*idle) (struct hid_device *hdev, int report, int idle, int reqtype)
+   Perform SET/GET_IDLE request. Only used by USB-HID, do not implement!
+
+2.3) Data Path
+--------------
+
+Transport drivers are responsible of reading data from I/O devices. They must
+handle any state-tracking themselves. HID core does not implement protocol
+handshakes or other management commands which can be required by the given HID
+transport specification.
+
+Every raw data packet read from a device must be fed into HID core via
+hid_input_report(). You must specify the channel-type (intr or ctrl) and report
+type (input/output/feature). Under normal conditions, only input reports are
+provided via this API.
+
+Responses to GET_REPORT requests via ->request() must also be provided via this
+API. Responses to ->raw_request() are synchronous and must be intercepted by the
+transport driver and not passed to hid_input_report().
+Acknowledgements to SET_REPORT requests are not of interest to HID core.
+
+----------------------------------------------------
+Written 2013, David Herrmann <[email protected]>
Signature · 1 line
1.8.3.2
#19 lore raw

Re: [RFC 7/8] HID: add transport driver documentation

Hi David,

Quoted 7 lines · David Herrmann

hid-transport.txt describes the transport driver layout which is required by HID-core to work correctly. HID-core supports many different transport drivers and new drivers can be added for any kind of I/O system.

The current layout doesn't really differentiate between intr and ctrl channels, which confuses some hid-drivers and may break devices. HIDP, USBHID and I2DHID all implement different semantics for each callback, so

typo: I2CHID

Quoted 20 lines

our device drivers aren't really transport-driver-agnostic.

To solve that, hid-transport.txt describes the layout of transport drivers that is required by HID core. Furthermore, it introduces some new callbacks which replace the current hid_get_raw_report() and hid_output_raw_report() hooks. These will be implemented in follow-up patches.

---
 Documentation/hid/hid-transport.txt | 299 ++++++++++++++++++++++++++++++++++++
 1 file changed, 299 insertions(+)
 create mode 100644 Documentation/hid/hid-transport.txt
Documentation/hid/hid-transport.txt new file +0 -0
new file mode 100644
index 0000000..034f11d

Documentation/hid/hid-transport.txt line 7

Quoted 7 lines
Documentation/hid/hid-transport.txt new file +7 -0
@@ -0,0 +1,299 @@
+                          HID I/O Transport Drivers
+                         ===========================
+
+The HID subsystem is independent of the underlying transport driver. Initially,
+only USB was supported, but other specifications adopted the HID design and
+provided new transport drivers. The kernel includes at least support for USB,
+Bluetooth, i2c and user-space I/O drivers.

IIRC, we should put I2C in capital letters.

Documentation/hid/hid-transport.txt line 17

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +10 -0
+
+1) HID Bus
+==========
+
+The HID subsystem is designed as a bus. Any I/O subsystem may provide HID
+devices and register them with the HID bus. HID core then loads generic device
+drivers on top of it. The transport drivers are responsible of raw data
+transport and device setup/management. HID core is responsible of
+report-parsing, report interpretation and the user-space API. Device specifics
+and quirks are also handled by HID core and the HID device drivers.

Hmm, the quirks part is not exactly what is currently implemented. Usbhid (Transport Driver) set and use quirks while you say here that they are handled by hid-core.

I personally prefer the way it is documented here (quirks handled in hid-core) so HIDP, i2c-hid and uhid will benefit from the dynamic quirks already implemented in usbhid. But that will require another patch :)

Quoted 23 lines
Documentation/hid/hid-transport.txt new file +23 -0
+
+ +-----------+  +-----------+            +-----------+  +-----------+
+ | Device #1 |  | Device #i |            | Device #j |  | Device #k |
+ +-----------+  +-----------+            +-----------+  +-----------+
+          \\      //                              \\      //
+        +------------+                          +------------+
+        | I/O Driver |                          | I/O Driver |
+        +------------+                          +------------+
+              ||                                      ||
+     +------------------+                    +------------------+
+     | Transport Driver |                    | Transport Driver |
+     +------------------+                    +------------------+
+                       \___                ___/
+                           \              /
+                          +----------------+
+                          |    HID Core    |
+                          +----------------+
+                           /  |        |  \
+                          /   |        |   \
+             ____________/    |        |    \_________________
+            /                 |        |                      \
+           /                  |        |                       \
+ +----------------+  +-----------+  +------------------+  +------------------+

Documentation/hid/hid-transport.txt line 50

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +10 -0
+ | Generic Driver |  | MT Driver |  | Custom Driver #1 |  | Custom Driver #2 |
+ +----------------+  +-----------+  +------------------+  +------------------+
+
+Example Drivers:
+  I/O: USB, I2C, Bluetooth-l2cap
+  Transport: USB-HID, I2C-HID, BT-HIDP
+
+Normally, a transport driver is specific for a given I/O driver. But the HID
+design also allows transport drivers to work with different I/O systems at the
+same time.

I do not follow you here. Which transport driver you have in mind?

Quoted 11 lines
Documentation/hid/hid-transport.txt new file +11 -0
+
+Everything below "HID Core" is simplified in this graph as it is only of
+interest to HID device drivers. Transport drivers do not need to know the
+specifics.
+
+1.1) Device Setup
+-----------------
+
+I/O drivers normally provide hotplug detection or device enumeration APIs to the
+transport drivers. Transport drivers use this to find any suitable HID device.
+They allocate HID device objects and register them with HID core. Transport

Documentation/hid/hid-transport.txt line 71

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +10 -0
+drivers are not required to register themselves with HID core. HID core is never
+aware of which transport drivers are available and is not interested in it. It
+is only interested in devices.
+
+Transport drivers attach a constant "struct hid_ll_driver" object with each
+device. Once a device is registered with HID core, the callbacks provided via
+this struct are used by HID core to communicate with the device.
+
+Transport drivers are responsible of detecting device failures and unplugging.
+HID core will operate an device as long as it is registered regardless of any

typo: ^^ "a"

Quoted 13 lines
Documentation/hid/hid-transport.txt new file +13 -0
+device failures. Once transport drivers detect unplug or failure events, they
+must unregister the device from HID core and HID core will stop using the
+provided callbacks.
+
+1.2) Transport Driver Requirements
+----------------------------------
+
+HID core requires transport drivers to follow a given design. A Transport
+drivers must provide two bi-directional I/O channels to each HID device. These
+channels might be multiplexed on a single physical channel, though.
+
+ - Interrupt Channel (intr): The intr channel is used for asynchronous data
+   reports. No management commands or data acknowledgements are sent on this

Documentation/hid/hid-transport.txt line 94

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +10 -0
+   channel. Any unrequested incoming or outgoing data report must be sent on
+   this channel and is never acknowledged by the remote side. Devices usually
+   send their input events on this channel. Outgoing events are normally
+   not send via intr, except if high throughput is required.
+ - Control Channel (ctrl): The ctrl channel is used for synchronous requests and
+   device management. Unrequested data input events must not be sent on this
+   channel and are normally ignored. Instead, devices only send management
+   events or answers to host requests on this channel.
+   Outgoing reports are usually sent on the ctrl channel via synchronous
+   SET_REPORT requests.

This is not entirely true for USB and I2C:
- for USB, OUTPUT reports can also be sent through a direct write on an URB.
- for I2C, the notion of intr vs ctrl is slightly different, but it remains roughly the same. Let me detail it so that you can make your idea:
The HID descriptors declares three I2C registers:
* the "Input" register: this one correspond to the intr you are
describing. The difference is that this register is only used from the communication from the device to the host (no outputs here). The big interest in this register is that the communication is device initiated thanks to an external line triggered by the device (in I2C, the communication is only master (host) initiated).
* the "Command" register: this one corresponds to the ctrl channel.
This channel/register is host initiated too (like on the USB side), and there is a slight head over due to the HID protocol regarding sending output events.
* the "Output" register: this one corresponds to the URBs described in
USB. Every communication coming from the host on this channel is considered as an OUTPUT report, reducing the HID protocol over head.

The HID/I2C also declares a forth register (the "data") register which
is used by the HID protocol while using the "Command" register.

To sum up, I think the differences between the channels are:
- Interrupt Channel (intr): asynchronous, device initiated, no acknowledgements (beside the fact that data has been read), read only.
- Control Channel (ctrl): synchronous, host initiated, acknowledged (because synchronous), read/write
- Output Channel (out): synchronous, host initiated, acknowledged (because synchronous), write only

Then, the usbhid transport layer makes the ctrl and out channels asynchronous...

Documentation/hid/hid-transport.txt line 101

Quoted 7 lines
Documentation/hid/hid-transport.txt new file +7 -0
+
+Communication between devices and HID core is mostly done via HID reports. A
+report can be of one of three types:
+
+ - INPUT Report: Input reports provide data from device to host. This
+   data may include button events, axis events, battery status or more. This
+   data is generated by the device and sent to the host without requiring

_with_ or without requiring explicit requests.
On I2C, the command GET_REPORT is even mandatory (which does not seems to be the case for USB, given the amount of QUIRK_NO_INIT_REPORT we have.)

Documentation/hid/hid-transport.txt line 102

Quoted 1 line
Documentation/hid/hid-transport.txt new file +1 -0
+   explicit requests. Devices can choose to send data continously or only on

My spell checker gives me a typo on: "continuously"

Documentation/hid/hid-transport.txt line 106

Quoted 4 lines
Documentation/hid/hid-transport.txt new file +4 -0
+   change.
+ - OUTPUT Report: Output reports change device states. They are sent from host
+   to device and may include LED requests, rumble requests or more. Output
+   reports are never sent from device to host, except if explicitly requested.

In this case, I think it would be better to say that Output reports are never sent from device to host, but the host can _retrieve_ its current state.

Documentation/hid/hid-transport.txt line 107

Quoted 1 line
Documentation/hid/hid-transport.txt new file +1 -0
+   Hosts may choose to send output reports either continously or only on change.

My spell checker gives me a typo on: "continuously"

Documentation/hid/hid-transport.txt line 111

Quoted 4 lines
Documentation/hid/hid-transport.txt new file +4 -0
+ - FEATURE Report: Feature reports can be anything that doesn't belong into the
+   other two categories. Some of them are generic and defined by the HID spec,
+   but most of them are used as custom device extensions. For instance, they may
+   provide battery status information.

I disagree. FEATURE are specific reports which are read/write, and not spontaneously emitted by the device. For instance, this allows the host to change the current mode of the device (mouse emulation or multitouch in the case I know most), or to set constants in the device (max reported touches in my case, but this can be hardware macros for keyboards, or something else).

Documentation/hid/hid-transport.txt line 113

Quoted 2 lines
Documentation/hid/hid-transport.txt new file +2 -0
+   Feature reports are never sent without requests. A host must explicitly
+   request a device to set or send a feature report. This also means, feature

I would prefer a "host must explicitly request a device to set or _retrieve_ a feature report".

Documentation/hid/hid-transport.txt line 116

Quoted 3 lines
Documentation/hid/hid-transport.txt new file +3 -0
+   reports are never sent on the intr channel as this channel is asynchronous.
+
+INPUT and OUTPUT reports can be sent as pure data reports on the intr channel.

In my mind, OUTPUT reports are not sent through the intr channel (because write only), but you already get my point I think :)

Documentation/hid/hid-transport.txt line 120

Quoted 4 lines
Documentation/hid/hid-transport.txt new file +4 -0
+For INPUT reports this is the usual operational mode. But for OUTPUT reports,
+this is rarely done as OUTPUT reports are normally quite rare. But devices are
+free to make excessive use of asynchronous OUTPUT reports (for instance, custom
+HID audio speakers make great use of it).

Ok, did not know about it.

Documentation/hid/hid-transport.txt line 127

Quoted 7 lines
Documentation/hid/hid-transport.txt new file +7 -0
+
+Raw reports must not be sent on the ctrl channel, though. Instead, the ctrl
+channel provides synchronous GET/SET_REPORT requests.
+
+ - GET_REPORT: A GET_REPORT request has a report ID as payload and is sent
+   from host to device. The device must answer with a data report for the
+   requested report ID on the ctrl channel as a synchronous acknowledgement.

Beware that the report ID is not mandatory in case the HID report descriptors declares only 1 report without report ID. But I'm fine with it anyway (it's understandable that way).

Documentation/hid/hid-transport.txt line 137

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +10 -0
+   Only one GET_REPORT request can be pending for each device. This restriction
+   is enforced by HID core as several transport drivers don't allow multiple
+   simultaneous GET_REPORT requests.
+   Note that data reports which are sent as answer to a GET_REPORT request are
+   not handled as generic device events. That is, if a device does not operate
+   in continuous data reporting mode, an answer to GET_REPORT does not replace
+   the raw data report on the intr channel on state change.
+   GET_REPORT is only used by custom HID device drivers to query device state.
+   Normally, HID core caches any device state so this request is not necessary
+   on devices that follow the HID specs.

FYI, HID/I2C spec says: "GET_REPORT is often used by applications on startup to retrieve the ``current state'' of the device rather than waiting for the device to generate the next input/feature report".

And as under Linux applications do not talk directly to the hid devices, I fully concurs to your point.

Documentation/hid/hid-transport.txt line 139

Quoted 2 lines
Documentation/hid/hid-transport.txt new file +2 -0
+   GET_REPORT requests can be sent for any of the 3 report types and shall
+   return the current report state of the device.

The HID/I2C spec explicitly says: "the DEVICE shall ignore a GET_REPORT requests with the REPORT TYPE set to Output, as it is not used in this specification." So under i2c, we can send GET_REPORT with OUTPUT, but we will not get anything from the device (this is why it is forbidden by the transport driver).

Documentation/hid/hid-transport.txt line 142

Quoted 3 lines
Documentation/hid/hid-transport.txt new file +3 -0
+ - SET_REPORT: A SET_REPORT request has a report ID plus data as payload. It is
+   sent from host to device and a device must update it's current report state
+   according to the given data. Any of the 3 report types can be used.

The HID/I2C spec explicitly says: "the DEVICE might choose to ignore input SET_REPORT requests as meaningless."...

Documentation/hid/hid-transport.txt line 154

Quoted 12 lines
Documentation/hid/hid-transport.txt new file +12 -0
+   A device must answer with a synchronous acknowledgement. However, HID core
+   does not require transport drivers to forward this acknowledgement to HID
+   core.
+   Same as for GET_REPORT, only one SET_REPORT can be pending at a time. This
+   restriction is enforced by HID core as some transport drivers do not support
+   multiple synchronous SET_REPORT requests.
+
+Other ctrl-channel requests are supported by USB-HID but are not available
+(or deprecated) in most other transport level specifications:
+
+ - GET/SET_IDLE: Only used by USB-HID. Do not implement!
+ - GET/SET_PROTOCOL: Not used by HID core. Do not implement!

The I2C declares also:
- RESET: mandatory (reset the device at any time)
- SET_POWER: mandatory on the device side (request from host to device to indicate preferred power setting).

Quoted 19 lines
Documentation/hid/hid-transport.txt new file +19 -0
+
+2) HID API
+==========
+
+2.1) Initialization
+-------------------
+
+Transport drivers normally use the following procedure to register a new device
+with HID core:
+
+       struct hid_device *hid;
+       int ret;
+
+       hid = hid_allocate_device();
+       if (IS_ERR(hid)) {
+               ret = PTR_ERR(hid);
+               goto err_<...>;
+       }
+

Documentation/hid/hid-transport.txt line 183

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +10 -0
+       strlcpy(hid->name, <device-name-src>, 127);
+       strlcpy(hid->phys, <device-phys-src>, 63);
+       strlcpy(hid->uniq, <device-uniq-src>, 63);
+
+       hid->ll_driver = &custom_ll_driver;
+       hid->bus = <device-bus>;
+       hid->vendor = <device-vendor>;
+       hid->product = <device-product>;
+       hid->version = <device-version>;
+       hid->country = <device-country>;

FYI, HID/I2C does not define any device-country field (I guess it will come in a later release...)

Documentation/hid/hid-transport.txt line 184

Quoted 1 line
Documentation/hid/hid-transport.txt new file +1 -0
+       hid->dev.parent = <pointer-to-parent-device>;

FYI, I have started implementing a devm API for HID (in the same way the input devm API is implemented), and dev.parent should not be overwritten.

Anyway the two last comments are not requesting any changes in the document.

Documentation/hid/hid-transport.txt line 192

Quoted 8 lines
Documentation/hid/hid-transport.txt new file +8 -0
+       hid->driver_data = <transport-driver-data-field>;
+
+       ret = hid_add_device(hid);
+       if (ret)
+               goto err_<...>;
+
+Once hid_add_device() is entered, HID core might use the callbacks provided in
+"custom_ll_driver". To unregister a device, use:
                    ^^^^

Maybe introduce a new paragraph (otherwise, it looks like hid_destroy_device is called from HID core).

Quoted 21 lines
Documentation/hid/hid-transport.txt new file +21 -0
+
+       hid_destroy_device(hid);
+
+Once hid_destroy_device() returns, HID core will no longer make use of any
+driver callbacks.
+
+2.2) hid_ll_driver operations
+-----------------------------
+
+The available HID callbacks are:
+ - int (*start) (struct hid_device *hdev)
+   Called from HID device drivers once they want to use the device. Transport
+   drivers can choose to setup their device in this callback. However, normally
+   devices are already set up before transport drivers register them to HID core
+   so this is mostly only used by USB-HID.
+
+ - void (*stop) (struct hid_device *hdev)
+   Called from HID device drivers once they are done with a device. Transport
+   drivers can free any buffers and deinitialize the device. But note that
+   ->start() might be called again if another HID device driver is loaded on the
+   device.

Documentation/hid/hid-transport.txt line 223

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +10 -0
+   Transport drivers are free to ignore it and deinitialize devices after they
+   destroyed them via hid_destroy_device().
+
+ - int (*open) (struct hid_device *hdev)
+   Called from HID device drivers once they are interested in data reports.
+   Usually, while user-space didn't open any input API/etc., device drivers are
+   not interested in device data and transport drivers can put devices asleep.
+   However, once ->open() is called, transport drivers must be ready for I/O.
+   ->open() calls are never nested. So in between two ->open() calls there must
+   be a call to ->close().

This is not true in either USB or I2C. And I guess with every BT or UHID devices. ->open() and ->close() are called upon open/close of the input node (or hidraw, or hiddev). So If two clients are opening the same input node, there will be two calls to ->open() without any call to ->close() in between.

I guess you mixed up this part with the ->start() ->stop() :)

Quoted 28 lines
Documentation/hid/hid-transport.txt new file +28 -0
+
+ - void (*close) (struct hid_device *hdev)
+   Called from HID device drivers after ->open() was called but they are no
+   longer interested in device reports. (Usually if user-space closed any input
+   devices of the driver).
+   Transport drivers can put devices asleep and terminate any I/O. However,
+   ->start() may be called again if the device driver is interested in input
+   reports again.
+
+ - int (*parse) (struct hid_device *hdev)
+   Called once during device setup after ->start() has been called. Transport
+   drivers must read the HID report-descriptor from the device and tell HID core
+   about it via hid_parse_report().
+
+ - int (*power) (struct hid_device *hdev, int level)
+   Called by HID core to give PM hints to transport drivers. Usually this is
+   analogical to the ->open() and ->close() hints and redundant.
+
+ - void (*request) (struct hid_device *hdev, struct hid_report *report,
+                    int reqtype)
+   Send an HID request on the ctrl channel. "report" contains the report that
+   should be sent and "reqtype" the request type. Request-type can be
+   HID_REQ_SET_REPORT or HID_REQ_GET_REPORT.
+   This callback is optional. If not provided, HID core will assemble a raw
+   report following the HID specs and send it via the ->raw_request() callback.
+   The transport driver is free to implement this asynchronously.
+
+ - int (*wait) (struct hid_device *hdev)

Documentation/hid/hid-transport.txt line 261

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +10 -0
+   Used by HID core before calling ->request() again. A transport driver can use
+   it to wait for any pending requests to complete if only one request is
+   allowed at a time.
+
+ - int (*raw_request) (struct hid_device *hdev, unsigned char reportnum,
+                       __u8 *buf, size_t count, unsigned char rtype,
+                       int reqtype)
+   Same as ->request() but provides the report as raw buffer. This request shall
+   be synchronous. A transport driver must not use ->wait() to complete such
+   requests.

2 questions/remarks here:
- is raw_request() meant to replace ->hid_output_raw_report() and ->hid_get_raw_report()?
- reportnum declared as an unsigned will be problematic regarding the rare devices not having any report ID in their report descriptors.

Documentation/hid/hid-transport.txt line 267

Quoted 6 lines
Documentation/hid/hid-transport.txt new file +6 -0
+
+ - int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len)
+   Send raw output report via intr channel. Used by some HID device drivers
+   which require high throughput for outgoing requests on the intr channel. This
+   must not cause SET_REPORT calls! This must be implemented as asynchronous
+   output report on the intr channel!

For me, there is something wrong here. The name infers that we are trying to send an output_report directly (so through the USB URB or through the output i2c register), but you are implementing it through the intr channel... :-S

Documentation/hid/hid-transport.txt line 275

Quoted 8 lines
Documentation/hid/hid-transport.txt new file +8 -0
+
+ - int (*hidinput_input_event) (struct input_dev *idev, unsigned int type,
+                                unsigned int code, int value)
+   Obsolete callback used by logitech converters. It is called when userspace
+   writes input events to the input device (eg., EV_LED). A driver can use this
+   callback to convert it into an output report and send it to the device. If
+   this callback is not provided, HID core will use ->request() or
+   ->raw_request() respectively.

I bet there will be a way to make this work with logitech devices too (if we implement a proper ->hid_output_raw_report() in each paired devices).

Documentation/hid/hid-transport.txt line 284

Quoted 9 lines
Documentation/hid/hid-transport.txt new file +9 -0
+
+ - int (*idle) (struct hid_device *hdev, int report, int idle, int reqtype)
+   Perform SET/GET_IDLE request. Only used by USB-HID, do not implement!
+
+2.3) Data Path
+--------------
+
+Transport drivers are responsible of reading data from I/O devices. They must
+handle any state-tracking themselves. HID core does not implement protocol

I don't get the "state-tracking" here. The reports states should be handled by core, and I do not see the other states (or you meant PM states?).

Quoted 16 lines
Documentation/hid/hid-transport.txt new file +15 -1
+handshakes or other management commands which can be required by the given HID
+transport specification.
+
+Every raw data packet read from a device must be fed into HID core via
+hid_input_report(). You must specify the channel-type (intr or ctrl) and report
+type (input/output/feature). Under normal conditions, only input reports are
+provided via this API.
+
+Responses to GET_REPORT requests via ->request() must also be provided via this
+API. Responses to ->raw_request() are synchronous and must be intercepted by the
+transport driver and not passed to hid_input_report().
+Acknowledgements to SET_REPORT requests are not of interest to HID core.
+
+----------------------------------------------------
+Written 2013, David Herrmann <[email protected]>
--
Quoted 2 lines

1.8.3.2

done!
Many thanks for this David. It was very interesting and detailed. It will make a great documentation.

Cheers,
Benjamin

#20 lore raw

Re: [RFC 7/8] HID: add transport driver documentation

Hi

Quoted 8 lines · Benjamin Tissoires

Hi David,

Quoted 4 lines · David Herrmann

hid-transport.txt describes the transport driver layout which is required by HID-core to work correctly. HID-core supports many different transport drivers and new drivers can be added for any kind of I/O system.

Quoted 5 lines
Quoted 3 lines

The current layout doesn't really differentiate between intr and ctrl channels, which confuses some hid-drivers and may break devices. HIDP, USBHID and I2DHID all implement different semantics for each callback, so

typo: I2CHID

Fixed, thx!

Quoted 14 lines
Quoted 14 lines

our device drivers aren't really transport-driver-agnostic.

To solve that, hid-transport.txt describes the layout of transport drivers that is required by HID core. Furthermore, it introduces some new callbacks which replace the current hid_get_raw_report() and hid_output_raw_report() hooks. These will be implemented in follow-up patches.

---
 Documentation/hid/hid-transport.txt | 299 ++++++++++++++++++++++++++++++++++++
 1 file changed, 299 insertions(+)
 create mode 100644 Documentation/hid/hid-transport.txt
Quoted 15 lines

Documentation/hid/hid-transport.txt line 7

Quoted 13 lines
Documentation/hid/hid-transport.txt new file +7 -0
new file mode 100644
index 0000000..034f11d
@@ -0,0 +1,299 @@
+                          HID I/O Transport Drivers
+                         ===========================
+
+The HID subsystem is independent of the underlying transport driver. Initially,
+only USB was supported, but other specifications adopted the HID design and
+provided new transport drivers. The kernel includes at least support for USB,
+Bluetooth, i2c and user-space I/O drivers.

IIRC, we should put I2C in capital letters.

Yepp, fixed.

Quoted 15 lines

Documentation/hid/hid-transport.txt line 17

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +10 -0
+
+1) HID Bus
+==========
+
+The HID subsystem is designed as a bus. Any I/O subsystem may provide HID
+devices and register them with the HID bus. HID core then loads generic device
+drivers on top of it. The transport drivers are responsible of raw data
+transport and device setup/management. HID core is responsible of
+report-parsing, report interpretation and the user-space API. Device specifics
+and quirks are also handled by HID core and the HID device drivers.

Hmm, the quirks part is not exactly what is currently implemented. Usbhid (Transport Driver) set and use quirks while you say here that they are handled by hid-core.

Quoted 3 lines

I personally prefer the way it is documented here (quirks handled in hid-core) so HIDP, i2c-hid and uhid will benefit from the dynamic quirks already implemented in usbhid. But that will require another patch :)

I actually meant things like wrong report-descriptors or special device drivers. Of course, there are also quirks that apply to the transport driver layer. I will rephrase that.

Quoted 27 lines
Quoted 27 lines
Documentation/hid/hid-transport.txt new file +27 -0
+
+ +-----------+  +-----------+            +-----------+  +-----------+
+ | Device #1 |  | Device #i |            | Device #j |  | Device #k |
+ +-----------+  +-----------+            +-----------+  +-----------+
+          \\      //                              \\      //
+        +------------+                          +------------+
+        | I/O Driver |                          | I/O Driver |
+        +------------+                          +------------+
+              ||                                      ||
+     +------------------+                    +------------------+
+     | Transport Driver |                    | Transport Driver |
+     +------------------+                    +------------------+
+                       \___                ___/
+                           \              /
+                          +----------------+
+                          |    HID Core    |
+                          +----------------+
+                           /  |        |  \
+                          /   |        |   \
+             ____________/    |        |    \_________________
+            /                 |        |                      \
+           /                  |        |                       \
+ +----------------+  +-----------+  +------------------+  +------------------+
+ | Generic Driver |  | MT Driver |  | Custom Driver #1 |  | Custom Driver #2 |
+ +----------------+  +-----------+  +------------------+  +------------------+
+
+Example Drivers:
Quoted 8 lines

Documentation/hid/hid-transport.txt line 50

Quoted 6 lines
Documentation/hid/hid-transport.txt new file +6 -0
+  I/O: USB, I2C, Bluetooth-l2cap
+  Transport: USB-HID, I2C-HID, BT-HIDP
+
+Normally, a transport driver is specific for a given I/O driver. But the HID
+design also allows transport drivers to work with different I/O systems at the
+same time.

I do not follow you here. Which transport driver you have in mind?

There is none such driver, I just wanted to point out that HID-core doesn't care. Turns out to be more confusing than helping so I guess I will just drop it.

Quoted 15 lines
Quoted 15 lines
Documentation/hid/hid-transport.txt new file +15 -0
+
+Everything below "HID Core" is simplified in this graph as it is only of
+interest to HID device drivers. Transport drivers do not need to know the
+specifics.
+
+1.1) Device Setup
+-----------------
+
+I/O drivers normally provide hotplug detection or device enumeration APIs to the
+transport drivers. Transport drivers use this to find any suitable HID device.
+They allocate HID device objects and register them with HID core. Transport
+drivers are not required to register themselves with HID core. HID core is never
+aware of which transport drivers are available and is not interested in it. It
+is only interested in devices.
+
Quoted 8 lines

Documentation/hid/hid-transport.txt line 71

Quoted 6 lines
Documentation/hid/hid-transport.txt new file +6 -0
+Transport drivers attach a constant "struct hid_ll_driver" object with each
+device. Once a device is registered with HID core, the callbacks provided via
+this struct are used by HID core to communicate with the device.
+
+Transport drivers are responsible of detecting device failures and unplugging.
+HID core will operate an device as long as it is registered regardless of any

typo: ^^ "a"

Fixed.

Quoted 47 lines
Quoted 13 lines
Documentation/hid/hid-transport.txt new file +13 -0
+device failures. Once transport drivers detect unplug or failure events, they
+must unregister the device from HID core and HID core will stop using the
+provided callbacks.
+
+1.2) Transport Driver Requirements
+----------------------------------
+
+HID core requires transport drivers to follow a given design. A Transport
+drivers must provide two bi-directional I/O channels to each HID device. These
+channels might be multiplexed on a single physical channel, though.
+
+ - Interrupt Channel (intr): The intr channel is used for asynchronous data
+   reports. No management commands or data acknowledgements are sent on this

Documentation/hid/hid-transport.txt line 94

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +10 -0
+   channel. Any unrequested incoming or outgoing data report must be sent on
+   this channel and is never acknowledged by the remote side. Devices usually
+   send their input events on this channel. Outgoing events are normally
+   not send via intr, except if high throughput is required.
+ - Control Channel (ctrl): The ctrl channel is used for synchronous requests and
+   device management. Unrequested data input events must not be sent on this
+   channel and are normally ignored. Instead, devices only send management
+   events or answers to host requests on this channel.
+   Outgoing reports are usually sent on the ctrl channel via synchronous
+   SET_REPORT requests.

This is not entirely true for USB and I2C:
- for USB, OUTPUT reports can also be sent through a direct write on an URB.
- for I2C, the notion of intr vs ctrl is slightly different, but it remains roughly the same. Let me detail it so that you can make your idea:
The HID descriptors declares three I2C registers:
* the "Input" register: this one correspond to the intr you are
describing. The difference is that this register is only used from the communication from the device to the host (no outputs here). The big interest in this register is that the communication is device initiated thanks to an external line triggered by the device (in I2C, the communication is only master (host) initiated).
* the "Command" register: this one corresponds to the ctrl channel.
This channel/register is host initiated too (like on the USB side), and there is a slight head over due to the HID protocol regarding sending output events.
* the "Output" register: this one corresponds to the URBs described in
USB. Every communication coming from the host on this channel is considered as an OUTPUT report, reducing the HID protocol over head.

The HID/I2C also declares a forth register (the "data") register which
is used by the HID protocol while using the "Command" register.

Quoted 7 lines

To sum up, I think the differences between the channels are:
- Interrupt Channel (intr): asynchronous, device initiated, no acknowledgements (beside the fact that data has been read), read only.
- Control Channel (ctrl): synchronous, host initiated, acknowledged (because synchronous), read/write
- Output Channel (out): synchronous, host initiated, acknowledged (because synchronous), write only

I intetionally described the channels as "bi-directional". So you actually describe the same scenario, but from a different view-point (mine obviously is HIDP). So if you consider the OUT channel to be the same as writing on INTR, you will get the same scenario. I am not entirely sure which is better, but I can change it to your description if it helps?

Regarding asynchronous vs. synchronous: I actually don't care how the I/O layer implements it. In this document, "asynchronous" means that I don't care for any acknowledgements. "synchronous" means, the remote device acknowledges the receival. Obviously, if an I/O system always acknowledges a SENT, then a transport driver can implement asynchronous transports as synchronous transports (which might mean having a separate buffer like USBHID does). But HID-core does not care.
Same situation if synchronous is not supported. HID-core assumes it is synchronous so the transport layer can simply fake an acknowledgement. However, this means that the transport-layer might have to take care of retransmissions if an asynchronous transport fails (which L2CAP in Bluetooth-Core does tranparently for HIDP).

I will try to be more verbose, but I intentionally posted all the callbacks below which should explain that "asynchronous" means it can be called in atomic-context and "synchronous" means it is allowed to sleep and wait for acknowledgement (speaking of code).

Thanks a lot for the I2C explanation, I think I understand it now and it does resemble USBHID and HIDP very much!

Quoted 14 lines

Then, the usbhid transport layer makes the ctrl and out channels asynchronous...

Documentation/hid/hid-transport.txt line 101

Quoted 7 lines
Documentation/hid/hid-transport.txt new file +7 -0
+
+Communication between devices and HID core is mostly done via HID reports. A
+report can be of one of three types:
+
+ - INPUT Report: Input reports provide data from device to host. This
+   data may include button events, axis events, battery status or more. This
+   data is generated by the device and sent to the host without requiring

_with_ or without requiring explicit requests.
On I2C, the command GET_REPORT is even mandatory (which does not seems to be the case for USB, given the amount of QUIRK_NO_INIT_REPORT we have.)

You're right, they can be sent synchronously via GET_REPORT, too.

But I don't know what you mean that GET_REPORT is mandatory? I thought an I2C device sends the data and fires an IRQ? Or is, upon interrupt receival, the host required to send a GET_REPORT to receive the pending input-event? I sadly have no idea how I2C works, but if it is _only_ host initiated, then this makes sense. Just want to go sure.

Anyway, even if I2C requires an explicit GET_REPORT, this doesn't conflict with this description. It's an implementation detail of I2C and HID-core doesn't care if input events require a GET_REPORT.

Quoted 3 lines

Documentation/hid/hid-transport.txt line 102

Quoted 1 line
Documentation/hid/hid-transport.txt new file +1 -0
+   explicit requests. Devices can choose to send data continously or only on

My spell checker gives me a typo on: "continuously"

Whoops, right.

Quoted 8 lines

Documentation/hid/hid-transport.txt line 106

Quoted 4 lines
Documentation/hid/hid-transport.txt new file +4 -0
+   change.
+ - OUTPUT Report: Output reports change device states. They are sent from host
+   to device and may include LED requests, rumble requests or more. Output
+   reports are never sent from device to host, except if explicitly requested.

In this case, I think it would be better to say that Output reports are never sent from device to host, but the host can _retrieve_ its current state.

Yepp, makes sense.

Quoted 3 lines

Documentation/hid/hid-transport.txt line 107

Quoted 1 line
Documentation/hid/hid-transport.txt new file +1 -0
+   Hosts may choose to send output reports either continously or only on change.

My spell checker gives me a typo on: "continuously"

Fixed.

Quoted 11 lines

Documentation/hid/hid-transport.txt line 111

Quoted 4 lines
Documentation/hid/hid-transport.txt new file +4 -0
+ - FEATURE Report: Feature reports can be anything that doesn't belong into the
+   other two categories. Some of them are generic and defined by the HID spec,
+   but most of them are used as custom device extensions. For instance, they may
+   provide battery status information.

I disagree. FEATURE are specific reports which are read/write, and not spontaneously emitted by the device. For instance, this allows the host to change the current mode of the device (mouse emulation or multitouch in the case I know most), or to set constants in the device (max reported touches in my case, but this can be hardware macros for keyboards, or something else).

This is actually what I meant. But I agree, it isn't really clear from my description.

Quoted 5 lines

Documentation/hid/hid-transport.txt line 113

Quoted 2 lines
Documentation/hid/hid-transport.txt new file +2 -0
+   Feature reports are never sent without requests. A host must explicitly
+   request a device to set or send a feature report. This also means, feature

I would prefer a "host must explicitly request a device to set or _retrieve_ a feature report".

Yepp, fixed.

Quoted 6 lines

Documentation/hid/hid-transport.txt line 116

Quoted 3 lines
Documentation/hid/hid-transport.txt new file +3 -0
+   reports are never sent on the intr channel as this channel is asynchronous.
+
+INPUT and OUTPUT reports can be sent as pure data reports on the intr channel.

In my mind, OUTPUT reports are not sent through the intr channel (because write only), but you already get my point I think :)

Yeah, as explained above I merged INTR and OUTPUT into a bi-directional channel.

Quoted 7 lines

Documentation/hid/hid-transport.txt line 120

Quoted 4 lines
Documentation/hid/hid-transport.txt new file +4 -0
+For INPUT reports this is the usual operational mode. But for OUTPUT reports,
+this is rarely done as OUTPUT reports are normally quite rare. But devices are
+free to make excessive use of asynchronous OUTPUT reports (for instance, custom
+HID audio speakers make great use of it).

Ok, did not know about it.

Quoted 11 lines

Documentation/hid/hid-transport.txt line 127

Quoted 7 lines
Documentation/hid/hid-transport.txt new file +7 -0
+
+Raw reports must not be sent on the ctrl channel, though. Instead, the ctrl
+channel provides synchronous GET/SET_REPORT requests.
+
+ - GET_REPORT: A GET_REPORT request has a report ID as payload and is sent
+   from host to device. The device must answer with a data report for the
+   requested report ID on the ctrl channel as a synchronous acknowledgement.

Beware that the report ID is not mandatory in case the HID report descriptors declares only 1 report without report ID. But I'm fine with it anyway (it's understandable that way).

Ugh, I am not entirely sure but afaik HIDP doesn't support implicit IDs. Could you tell me whether I2C supports it?

A report-descriptor can skip report IDs if there is only a single report? Didn't know that, but we definitely need to document it.

Quoted 11 lines

Documentation/hid/hid-transport.txt line 137

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +10 -0
+   Only one GET_REPORT request can be pending for each device. This restriction
+   is enforced by HID core as several transport drivers don't allow multiple
+   simultaneous GET_REPORT requests.
+   Note that data reports which are sent as answer to a GET_REPORT request are
+   not handled as generic device events. That is, if a device does not operate
+   in continuous data reporting mode, an answer to GET_REPORT does not replace
+   the raw data report on the intr channel on state change.
+   GET_REPORT is only used by custom HID device drivers to query device state.
+   Normally, HID core caches any device state so this request is not necessary
+   on devices that follow the HID specs.
Quoted 3 lines

FYI, HID/I2C spec says: "GET_REPORT is often used by applications on startup to retrieve the ``current state'' of the device rather than waiting for the device to generate the next input/feature report".

Yeah, for startup this makes sense. I will add a short note.

Quoted 11 lines

And as under Linux applications do not talk directly to the hid devices, I fully concurs to your point.

Documentation/hid/hid-transport.txt line 139

Quoted 2 lines
Documentation/hid/hid-transport.txt new file +2 -0
+   GET_REPORT requests can be sent for any of the 3 report types and shall
+   return the current report state of the device.

The HID/I2C spec explicitly says: "the DEVICE shall ignore a GET_REPORT requests with the REPORT TYPE set to Output, as it is not used in this specification." So under i2c, we can send GET_REPORT with OUTPUT, but we will not get anything from the device (this is why it is forbidden by the transport driver).

Heh, didn't know that, either. It's fine if I2C ignores it. However, I'd like to avoid forbidding it. There can be devices which use this (companies to crazy things..) and I cannot see a reason to forbid it in HID core. The transport drivers are free to adhere to their specifications, though. I guess that's fine?

Quoted 6 lines

Documentation/hid/hid-transport.txt line 142

Quoted 3 lines
Documentation/hid/hid-transport.txt new file +3 -0
+ - SET_REPORT: A SET_REPORT request has a report ID plus data as payload. It is
+   sent from host to device and a device must update it's current report state
+   according to the given data. Any of the 3 report types can be used.

The HID/I2C spec explicitly says: "the DEVICE might choose to ignore input SET_REPORT requests as meaningless."...

Same as above I think. But thanks for the clarification! I guess I will add a note that it is discouraged and devices are not supposed to handle it. This should clear all doubts.

Quoted 9 lines
Quoted 9 lines
Documentation/hid/hid-transport.txt new file +9 -0
+   A device must answer with a synchronous acknowledgement. However, HID core
+   does not require transport drivers to forward this acknowledgement to HID
+   core.
+   Same as for GET_REPORT, only one SET_REPORT can be pending at a time. This
+   restriction is enforced by HID core as some transport drivers do not support
+   multiple synchronous SET_REPORT requests.
+
+Other ctrl-channel requests are supported by USB-HID but are not available
+(or deprecated) in most other transport level specifications:
Quoted 8 lines

Documentation/hid/hid-transport.txt line 154

Quoted 3 lines
Documentation/hid/hid-transport.txt new file +3 -0
+
+ - GET/SET_IDLE: Only used by USB-HID. Do not implement!
+ - GET/SET_PROTOCOL: Not used by HID core. Do not implement!

The I2C declares also:
- RESET: mandatory (reset the device at any time)
- SET_POWER: mandatory on the device side (request from host to device to indicate preferred power setting).

Are they hooked up to HID-core? I'd like to avoid any commands which are handled transparently in the transport driver (like suspend/resume). However, SET_POWER sounds related to ->power() callback. I will look through it again and include it in the next revision if it is hooked up.

Quoted 24 lines
Quoted 24 lines
Documentation/hid/hid-transport.txt new file +24 -0
+
+2) HID API
+==========
+
+2.1) Initialization
+-------------------
+
+Transport drivers normally use the following procedure to register a new device
+with HID core:
+
+       struct hid_device *hid;
+       int ret;
+
+       hid = hid_allocate_device();
+       if (IS_ERR(hid)) {
+               ret = PTR_ERR(hid);
+               goto err_<...>;
+       }
+
+       strlcpy(hid->name, <device-name-src>, 127);
+       strlcpy(hid->phys, <device-phys-src>, 63);
+       strlcpy(hid->uniq, <device-uniq-src>, 63);
+
+       hid->ll_driver = &custom_ll_driver;
Quoted 8 lines

Documentation/hid/hid-transport.txt line 183

Quoted 5 lines
Documentation/hid/hid-transport.txt new file +5 -0
+       hid->bus = <device-bus>;
+       hid->vendor = <device-vendor>;
+       hid->product = <device-product>;
+       hid->version = <device-version>;
+       hid->country = <device-country>;

FYI, HID/I2C does not define any device-country field (I guess it will come in a later release...)

Right, I think BT also sets it to 0 in bluez. I will add a note that "0" is the default value.

Quoted 4 lines

Documentation/hid/hid-transport.txt line 184

Quoted 1 line
Documentation/hid/hid-transport.txt new file +1 -0
+       hid->dev.parent = <pointer-to-parent-device>;

FYI, I have started implementing a devm API for HID (in the same way the input devm API is implemented), and dev.parent should not be overwritten.

Cool! I will adjust the document once it is merged.

Quoted 11 lines

Anyway the two last comments are not requesting any changes in the document.

Documentation/hid/hid-transport.txt line 192

Quoted 8 lines
Documentation/hid/hid-transport.txt new file +8 -0
+       hid->driver_data = <transport-driver-data-field>;
+
+       ret = hid_add_device(hid);
+       if (ret)
+               goto err_<...>;
+
+Once hid_add_device() is entered, HID core might use the callbacks provided in
+"custom_ll_driver". To unregister a device, use:
                    ^^^^
Quoted 2 lines

Maybe introduce a new paragraph (otherwise, it looks like hid_destroy_device is called from HID core).

Indeed, will do that.

Quoted 38 lines
Quoted 21 lines
Documentation/hid/hid-transport.txt new file +21 -0
+
+       hid_destroy_device(hid);
+
+Once hid_destroy_device() returns, HID core will no longer make use of any
+driver callbacks.
+
+2.2) hid_ll_driver operations
+-----------------------------
+
+The available HID callbacks are:
+ - int (*start) (struct hid_device *hdev)
+   Called from HID device drivers once they want to use the device. Transport
+   drivers can choose to setup their device in this callback. However, normally
+   devices are already set up before transport drivers register them to HID core
+   so this is mostly only used by USB-HID.
+
+ - void (*stop) (struct hid_device *hdev)
+   Called from HID device drivers once they are done with a device. Transport
+   drivers can free any buffers and deinitialize the device. But note that
+   ->start() might be called again if another HID device driver is loaded on the
+   device.

Documentation/hid/hid-transport.txt line 223

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +10 -0
+   Transport drivers are free to ignore it and deinitialize devices after they
+   destroyed them via hid_destroy_device().
+
+ - int (*open) (struct hid_device *hdev)
+   Called from HID device drivers once they are interested in data reports.
+   Usually, while user-space didn't open any input API/etc., device drivers are
+   not interested in device data and transport drivers can put devices asleep.
+   However, once ->open() is called, transport drivers must be ready for I/O.
+   ->open() calls are never nested. So in between two ->open() calls there must
+   be a call to ->close().

This is not true in either USB or I2C. And I guess with every BT or UHID devices. ->open() and ->close() are called upon open/close of the input node (or hidraw, or hiddev). So If two clients are opening the same input node, there will be two calls to ->open() without any call to ->close() in between.

Quoted 1 line

I guess you mixed up this part with the ->start() ->stop() :)

I got confused, indeed. input-core only calls ->open() on first-open, but there might be multiple input devices. So you're right. I will fix this. Thanks for catching it!

Quoted 34 lines
Quoted 34 lines
Documentation/hid/hid-transport.txt new file +34 -0
+
+ - void (*close) (struct hid_device *hdev)
+   Called from HID device drivers after ->open() was called but they are no
+   longer interested in device reports. (Usually if user-space closed any input
+   devices of the driver).
+   Transport drivers can put devices asleep and terminate any I/O. However,
+   ->start() may be called again if the device driver is interested in input
+   reports again.
+
+ - int (*parse) (struct hid_device *hdev)
+   Called once during device setup after ->start() has been called. Transport
+   drivers must read the HID report-descriptor from the device and tell HID core
+   about it via hid_parse_report().
+
+ - int (*power) (struct hid_device *hdev, int level)
+   Called by HID core to give PM hints to transport drivers. Usually this is
+   analogical to the ->open() and ->close() hints and redundant.
+
+ - void (*request) (struct hid_device *hdev, struct hid_report *report,
+                    int reqtype)
+   Send an HID request on the ctrl channel. "report" contains the report that
+   should be sent and "reqtype" the request type. Request-type can be
+   HID_REQ_SET_REPORT or HID_REQ_GET_REPORT.
+   This callback is optional. If not provided, HID core will assemble a raw
+   report following the HID specs and send it via the ->raw_request() callback.
+   The transport driver is free to implement this asynchronously.
+
+ - int (*wait) (struct hid_device *hdev)
+   Used by HID core before calling ->request() again. A transport driver can use
+   it to wait for any pending requests to complete if only one request is
+   allowed at a time.
+
+ - int (*raw_request) (struct hid_device *hdev, unsigned char reportnum,
+                       __u8 *buf, size_t count, unsigned char rtype,
Quoted 8 lines

Documentation/hid/hid-transport.txt line 261

Quoted 4 lines
Documentation/hid/hid-transport.txt new file +4 -0
+                       int reqtype)
+   Same as ->request() but provides the report as raw buffer. This request shall
+   be synchronous. A transport driver must not use ->wait() to complete such
+   requests.

2 questions/remarks here:
- is raw_request() meant to replace ->hid_output_raw_report() and ->hid_get_raw_report()?

Both. raw_request() with HID_REQ_GET_REPORT replaces hid_get_raw_report() and with HID_REQ_SET_REPORT it replaces hid_output_raw_report().
However, at least HIDP implements hid_output_raw_report() with HID_OUTPUT_REPORT as raw output report instead of SET_REPORT. For this, "output_report()" below can be used.

Quoted 2 lines

- reportnum declared as an unsigned will be problematic regarding the rare devices not having any report ID in their report descriptors.

I thought reportnum 0 is an invalid ID? I will check again and change to signed if needed. Thanks!

Quoted 11 lines

Documentation/hid/hid-transport.txt line 267

Quoted 6 lines
Documentation/hid/hid-transport.txt new file +6 -0
+
+ - int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len)
+   Send raw output report via intr channel. Used by some HID device drivers
+   which require high throughput for outgoing requests on the intr channel. This
+   must not cause SET_REPORT calls! This must be implemented as asynchronous
+   output report on the intr channel!

For me, there is something wrong here. The name infers that we are trying to send an output_report directly (so through the USB URB or through the output i2c register), but you are implementing it through the intr channel... :-S

Again, "write on INTR" is OUTPUT channel for me. So I guess with the explanation above we are fine here?

Quoted 11 lines

Documentation/hid/hid-transport.txt line 275

Quoted 8 lines
Documentation/hid/hid-transport.txt new file +8 -0
+
+ - int (*hidinput_input_event) (struct input_dev *idev, unsigned int type,
+                                unsigned int code, int value)
+   Obsolete callback used by logitech converters. It is called when userspace
+   writes input events to the input device (eg., EV_LED). A driver can use this
+   callback to convert it into an output report and send it to the device. If
+   this callback is not provided, HID core will use ->request() or
+   ->raw_request() respectively.

I bet there will be a way to make this work with logitech devices too (if we implement a proper ->hid_output_raw_report() in each paired devices).

Yeah, I thought so, but I have no idea what the logitech-dj driver does. I guess we can drop this once we have no more users, but I wanted to avoid pushing to hard on it.

Quoted 5 lines
Quoted 5 lines
Documentation/hid/hid-transport.txt new file +5 -0
+
+ - int (*idle) (struct hid_device *hdev, int report, int idle, int reqtype)
+   Perform SET/GET_IDLE request. Only used by USB-HID, do not implement!
+
+2.3) Data Path
Quoted 8 lines

Documentation/hid/hid-transport.txt line 284

Quoted 4 lines
Documentation/hid/hid-transport.txt new file +4 -0
+--------------
+
+Transport drivers are responsible of reading data from I/O devices. They must
+handle any state-tracking themselves. HID core does not implement protocol

I don't get the "state-tracking" here. The reports states should be handled by core, and I do not see the other states (or you meant PM states?).

Ugh, I meant I/O-related state-tracking (or PM). I will rephrase that.

Quoted 14 lines
Quoted 14 lines
Documentation/hid/hid-transport.txt new file +14 -0
+handshakes or other management commands which can be required by the given HID
+transport specification.
+
+Every raw data packet read from a device must be fed into HID core via
+hid_input_report(). You must specify the channel-type (intr or ctrl) and report
+type (input/output/feature). Under normal conditions, only input reports are
+provided via this API.
+
+Responses to GET_REPORT requests via ->request() must also be provided via this
+API. Responses to ->raw_request() are synchronous and must be intercepted by the
+transport driver and not passed to hid_input_report().
+Acknowledgements to SET_REPORT requests are not of interest to HID core.
+
+----------------------------------------------------
Quoted 8 lines
Quoted 4 lines
Documentation/hid/hid-transport.txt new file +1 -1
+Written 2013, David Herrmann <[email protected]>
--

1.8.3.2

done!
Many thanks for this David. It was very interesting and detailed. It will make a great documentation.

Thanks a lot for reviewing. I will fix the remaining issues and with the "write-on-INTR is OUTPUT" I guess we are on the same page (except for minor issues)?

Cheers
David

#21 lore raw

Re: [RFC 7/8] HID: add transport driver documentation

Hi,

On Wed, Jul 17, 2013 at 5:05 PM, David Herrmann <[email protected]> wrote:
[snipped]

Quoted 15 lines
Quoted 15 lines

Documentation/hid/hid-transport.txt line 17

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +10 -0
+
+1) HID Bus
+==========
+
+The HID subsystem is designed as a bus. Any I/O subsystem may provide HID
+devices and register them with the HID bus. HID core then loads generic device
+drivers on top of it. The transport drivers are responsible of raw data
+transport and device setup/management. HID core is responsible of
+report-parsing, report interpretation and the user-space API. Device specifics
+and quirks are also handled by HID core and the HID device drivers.

Hmm, the quirks part is not exactly what is currently implemented. Usbhid (Transport Driver) set and use quirks while you say here that they are handled by hid-core.

Quoted 7 lines
Quoted 3 lines

I personally prefer the way it is documented here (quirks handled in hid-core) so HIDP, i2c-hid and uhid will benefit from the dynamic quirks already implemented in usbhid. But that will require another patch :)

I actually meant things like wrong report-descriptors or special device drivers. Of course, there are also quirks that apply to the transport driver layer. I will rephrase that.

Thanks

Quoted 1 line

[snipped]

Quoted 8 lines
Quoted 7 lines

To sum up, I think the differences between the channels are:
- Interrupt Channel (intr): asynchronous, device initiated, no acknowledgements (beside the fact that data has been read), read only.
- Control Channel (ctrl): synchronous, host initiated, acknowledged (because synchronous), read/write
- Output Channel (out): synchronous, host initiated, acknowledged (because synchronous), write only

Quoted 6 lines

I intetionally described the channels as "bi-directional". So you actually describe the same scenario, but from a different view-point (mine obviously is HIDP). So if you consider the OUT channel to be the same as writing on INTR, you will get the same scenario. I am not entirely sure which is better, but I can change it to your description if it helps?

Ok, no all this makes sense to me. FYI the USBHID stack has also 2 separate channels for intr. One Interrupt request and one URB for writing out Intr. So mixing the two Intr lines into one seems specific to BT. Anyway, I think you should rephrase it to make it more obvious that these are not channels, but channel _types_. The Control channel (type) is a general purposes channel used to send punctual queries to the device (so it has a head over), whereas the intr channel type is a dedicated line (half duplex or full duplex) in which each side (host or device) knows what to do with each packet. Of course, your English is far better than mine, so feel free to write whatever you want :)

Quoted 15 lines

Regarding asynchronous vs. synchronous: I actually don't care how the I/O layer implements it. In this document, "asynchronous" means that I don't care for any acknowledgements. "synchronous" means, the remote device acknowledges the receival. Obviously, if an I/O system always acknowledges a SENT, then a transport driver can implement asynchronous transports as synchronous transports (which might mean having a separate buffer like USBHID does). But HID-core does not care.
Same situation if synchronous is not supported. HID-core assumes it is synchronous so the transport layer can simply fake an acknowledgement. However, this means that the transport-layer might have to take care of retransmissions if an asynchronous transport fails (which L2CAP in Bluetooth-Core does tranparently for HIDP).

Quoted 4 lines

I will try to be more verbose, but I intentionally posted all the callbacks below which should explain that "asynchronous" means it can be called in atomic-context and "synchronous" means it is allowed to sleep and wait for acknowledgement (speaking of code).

Thanks for the explanation of synchronous vs asynchronous. I was indeed basing the (a)synchronous thing on the implementation of the transport layer. So yes, keep your version.

Quoted 19 lines

Thanks a lot for the I2C explanation, I think I understand it now and it does resemble USBHID and HIDP very much!

Quoted 14 lines

Then, the usbhid transport layer makes the ctrl and out channels asynchronous...

Documentation/hid/hid-transport.txt line 101

Quoted 7 lines
Documentation/hid/hid-transport.txt new file +7 -0
+
+Communication between devices and HID core is mostly done via HID reports. A
+report can be of one of three types:
+
+ - INPUT Report: Input reports provide data from device to host. This
+   data may include button events, axis events, battery status or more. This
+   data is generated by the device and sent to the host without requiring

_with_ or without requiring explicit requests.
On I2C, the command GET_REPORT is even mandatory (which does not seems to be the case for USB, given the amount of QUIRK_NO_INIT_REPORT we have.)

Quoted 7 lines

You're right, they can be sent synchronously via GET_REPORT, too.

But I don't know what you mean that GET_REPORT is mandatory? I thought an I2C device sends the data and fires an IRQ? Or is, upon interrupt receival, the host required to send a GET_REPORT to receive the pending input-event? I sadly have no idea how I2C works, but if it is _only_ host initiated, then this makes sense. Just want to go sure.

Indeed, when a device sends the data, it fires an IRQ. Then, the host reads the INPUT register and fetch the data (no head over). GET_REPORT is used when some driver wants to retrieve the current state of the report without waiting for the data to be spontaneously sent. It is mostly used at the start of the driver (or the application).

I think Microsoft made this request mandatory because so many HID USB devices are simply blocked when the host calls a GET_REPORT. This way, we know for sure that the GET_REPORT command will not block the device because it may be often used. On top of that, it is the only way to retrieve the FEATURE reports...

[snipped]

Quoted 8 lines
Quoted 6 lines

Documentation/hid/hid-transport.txt line 116

Quoted 3 lines
Documentation/hid/hid-transport.txt new file +3 -0
+   reports are never sent on the intr channel as this channel is asynchronous.
+
+INPUT and OUTPUT reports can be sent as pure data reports on the intr channel.

In my mind, OUTPUT reports are not sent through the intr channel (because write only), but you already get my point I think :)

Yeah, as explained above I merged INTR and OUTPUT into a bi-directional channel.

Ok. Now it makes sense.

Quoted 20 lines
Quoted 8 lines

Documentation/hid/hid-transport.txt line 120

Quoted 5 lines
Documentation/hid/hid-transport.txt new file +4 -0
+For INPUT reports this is the usual operational mode. But for OUTPUT reports,
+this is rarely done as OUTPUT reports are normally quite rare. But devices are
+free to make excessive use of asynchronous OUTPUT reports (for instance, custom
+HID audio speakers make great use of it).

Ok, did not know about it.

Quoted 11 lines

Documentation/hid/hid-transport.txt line 127

Quoted 7 lines
Documentation/hid/hid-transport.txt new file +7 -0
+
+Raw reports must not be sent on the ctrl channel, though. Instead, the ctrl
+channel provides synchronous GET/SET_REPORT requests.
+
+ - GET_REPORT: A GET_REPORT request has a report ID as payload and is sent
+   from host to device. The device must answer with a data report for the
+   requested report ID on the ctrl channel as a synchronous acknowledgement.

Beware that the report ID is not mandatory in case the HID report descriptors declares only 1 report without report ID. But I'm fine with it anyway (it's understandable that way).

Quoted 5 lines

Ugh, I am not entirely sure but afaik HIDP doesn't support implicit IDs. Could you tell me whether I2C supports it?

A report-descriptor can skip report IDs if there is only a single report? Didn't know that, but we definitely need to document it.

Actually, this was a "feature" from the USB specification. But it is up to the hardware maker to decide if it will implement it that way or not (because it will influence the INPUT report themselves). I2C only inherited it (so yes, it does support it). However, I never saw a device not using the report ID...

Quoted 30 lines
Quoted 12 lines

Documentation/hid/hid-transport.txt line 137

Quoted 11 lines
Documentation/hid/hid-transport.txt new file +10 -0
+   Only one GET_REPORT request can be pending for each device. This restriction
+   is enforced by HID core as several transport drivers don't allow multiple
+   simultaneous GET_REPORT requests.
+   Note that data reports which are sent as answer to a GET_REPORT request are
+   not handled as generic device events. That is, if a device does not operate
+   in continuous data reporting mode, an answer to GET_REPORT does not replace
+   the raw data report on the intr channel on state change.
+   GET_REPORT is only used by custom HID device drivers to query device state.
+   Normally, HID core caches any device state so this request is not necessary
+   on devices that follow the HID specs.
Quoted 3 lines

FYI, HID/I2C spec says: "GET_REPORT is often used by applications on startup to retrieve the ``current state'' of the device rather than waiting for the device to generate the next input/feature report".

Yeah, for startup this makes sense. I will add a short note.

Quoted 11 lines

And as under Linux applications do not talk directly to the hid devices, I fully concurs to your point.

Documentation/hid/hid-transport.txt line 139

Quoted 2 lines
Documentation/hid/hid-transport.txt new file +2 -0
+   GET_REPORT requests can be sent for any of the 3 report types and shall
+   return the current report state of the device.

The HID/I2C spec explicitly says: "the DEVICE shall ignore a GET_REPORT requests with the REPORT TYPE set to Output, as it is not used in this specification." So under i2c, we can send GET_REPORT with OUTPUT, but we will not get anything from the device (this is why it is forbidden by the transport driver).

Quoted 5 lines

Heh, didn't know that, either. It's fine if I2C ignores it. However, I'd like to avoid forbidding it. There can be devices which use this (companies to crazy things..) and I cannot see a reason to forbid it in HID core. The transport drivers are free to adhere to their specifications, though. I guess that's fine?

perfectly fine :)

Quoted 11 lines
Quoted 7 lines

Documentation/hid/hid-transport.txt line 142

Quoted 4 lines

+ - SET_REPORT: A SET_REPORT request has a report ID plus data as payload. It is

Documentation/hid/hid-transport.txt new file +2 -0
+   sent from host to device and a device must update it's current report state
+   according to the given data. Any of the 3 report types can be used.

The HID/I2C spec explicitly says: "the DEVICE might choose to ignore input SET_REPORT requests as meaningless."...

Same as above I think. But thanks for the clarification! I guess I will add a note that it is discouraged and devices are not supposed to handle it. This should clear all doubts.

Beware that devices should not use SET_REPORT for _input_ reports. But still, in the end, it is up to the manufacturer of the device to define it's own protocol. So I think I will need to change I2C to not prevent SET_REPORT on INPUT reports, or GET_REPORT on OUTPUT reports because I am sure one company will decide to use it that way to initialize their device...

Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
Documentation/hid/hid-transport.txt new file +9 -0
+   A device must answer with a synchronous acknowledgement. However, HID core
+   does not require transport drivers to forward this acknowledgement to HID
+   core.
+   Same as for GET_REPORT, only one SET_REPORT can be pending at a time. This
+   restriction is enforced by HID core as some transport drivers do not support
+   multiple synchronous SET_REPORT requests.
+
+Other ctrl-channel requests are supported by USB-HID but are not available
+(or deprecated) in most other transport level specifications:
Quoted 8 lines
Quoted 3 lines

+
+ - GET/SET_IDLE: Only used by USB-HID. Do not implement!
+ - GET/SET_PROTOCOL: Not used by HID core. Do not implement!

The I2C declares also:
- RESET: mandatory (reset the device at any time)
- SET_POWER: mandatory on the device side (request from host to device to indicate preferred power setting).

Quoted 1 line

Are they hooked up to HID-core? I'd like to avoid any commands which

No they are not. Actually I double checked with the USB spec, and it is indeed specific to the I2C spec...

Quoted 4 lines

are handled transparently in the transport driver (like suspend/resume). However, SET_POWER sounds related to ->power() callback. I will look through it again and include it in the next revision if it is hooked up.

I would say don't bother with the two of them. I thought they were more generic but as they are specific to I2C, it does not make sense to include them in this documentation. And you are right, SET_POWER should be handled through ->power().
Also I need to remind you that the I2C implementation is not widely tested because they are so few devices including it. This should change in the near future (the power consumption is very low compared to USB, so I expect OEM will start integrating them), but actually, I still didn't got my hand on a real consumer product with HID/I2C... So adjustments are expected to come, but the basic features are here, so that people will not complain of not having a touchpad/keyboard/touchscreen not working... :)

Quoted 147 lines
Quoted 25 lines
Quoted 25 lines
Documentation/hid/hid-transport.txt new file +24 -0
+
+2) HID API
+==========
+
+2.1) Initialization
+-------------------
+
+Transport drivers normally use the following procedure to register a new device
+with HID core:
+
+       struct hid_device *hid;
+       int ret;
+
+       hid = hid_allocate_device();
+       if (IS_ERR(hid)) {
+               ret = PTR_ERR(hid);
+               goto err_<...>;
+       }
+
+       strlcpy(hid->name, <device-name-src>, 127);
+       strlcpy(hid->phys, <device-phys-src>, 63);
+       strlcpy(hid->uniq, <device-uniq-src>, 63);
+
+       hid->ll_driver = &custom_ll_driver;
Quoted 8 lines

Documentation/hid/hid-transport.txt line 183

Quoted 5 lines
Documentation/hid/hid-transport.txt new file +5 -0
+       hid->bus = <device-bus>;
+       hid->vendor = <device-vendor>;
+       hid->product = <device-product>;
+       hid->version = <device-version>;
+       hid->country = <device-country>;

FYI, HID/I2C does not define any device-country field (I guess it will come in a later release...)

Right, I think BT also sets it to 0 in bluez. I will add a note that "0" is the default value.

Quoted 4 lines
Quoted 1 line

+ hid->dev.parent = <pointer-to-parent-device>;

FYI, I have started implementing a devm API for HID (in the same way the input devm API is implemented), and dev.parent should not be overwritten.

Cool! I will adjust the document once it is merged.

Quoted 11 lines

Anyway the two last comments are not requesting any changes in the document.

Documentation/hid/hid-transport.txt line 192

Quoted 8 lines
Documentation/hid/hid-transport.txt new file +8 -0
+       hid->driver_data = <transport-driver-data-field>;
+
+       ret = hid_add_device(hid);
+       if (ret)
+               goto err_<...>;
+
+Once hid_add_device() is entered, HID core might use the callbacks provided in
+"custom_ll_driver". To unregister a device, use:
                    ^^^^
Quoted 2 lines

Maybe introduce a new paragraph (otherwise, it looks like hid_destroy_device is called from HID core).

Indeed, will do that.

Quoted 38 lines
Quoted 21 lines
Documentation/hid/hid-transport.txt new file +21 -0
+
+       hid_destroy_device(hid);
+
+Once hid_destroy_device() returns, HID core will no longer make use of any
+driver callbacks.
+
+2.2) hid_ll_driver operations
+-----------------------------
+
+The available HID callbacks are:
+ - int (*start) (struct hid_device *hdev)
+   Called from HID device drivers once they want to use the device. Transport
+   drivers can choose to setup their device in this callback. However, normally
+   devices are already set up before transport drivers register them to HID core
+   so this is mostly only used by USB-HID.
+
+ - void (*stop) (struct hid_device *hdev)
+   Called from HID device drivers once they are done with a device. Transport
+   drivers can free any buffers and deinitialize the device. But note that
+   ->start() might be called again if another HID device driver is loaded on the
+   device.

Documentation/hid/hid-transport.txt line 223

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +10 -0
+   Transport drivers are free to ignore it and deinitialize devices after they
+   destroyed them via hid_destroy_device().
+
+ - int (*open) (struct hid_device *hdev)
+   Called from HID device drivers once they are interested in data reports.
+   Usually, while user-space didn't open any input API/etc., device drivers are
+   not interested in device data and transport drivers can put devices asleep.
+   However, once ->open() is called, transport drivers must be ready for I/O.
+   ->open() calls are never nested. So in between two ->open() calls there must
+   be a call to ->close().

This is not true in either USB or I2C. And I guess with every BT or UHID devices. ->open() and ->close() are called upon open/close of the input node (or hidraw, or hiddev). So If two clients are opening the same input node, there will be two calls to ->open() without any call to ->close() in between.

Quoted 1 line

I guess you mixed up this part with the ->start() ->stop() :)

I got confused, indeed. input-core only calls ->open() on first-open, but there might be multiple input devices. So you're right. I will fix this. Thanks for catching it!

Quoted 34 lines
Quoted 34 lines
Documentation/hid/hid-transport.txt new file +34 -0
+
+ - void (*close) (struct hid_device *hdev)
+   Called from HID device drivers after ->open() was called but they are no
+   longer interested in device reports. (Usually if user-space closed any input
+   devices of the driver).
+   Transport drivers can put devices asleep and terminate any I/O. However,
+   ->start() may be called again if the device driver is interested in input
+   reports again.
+
+ - int (*parse) (struct hid_device *hdev)
+   Called once during device setup after ->start() has been called. Transport
+   drivers must read the HID report-descriptor from the device and tell HID core
+   about it via hid_parse_report().
+
+ - int (*power) (struct hid_device *hdev, int level)
+   Called by HID core to give PM hints to transport drivers. Usually this is
+   analogical to the ->open() and ->close() hints and redundant.
+
+ - void (*request) (struct hid_device *hdev, struct hid_report *report,
+                    int reqtype)
+   Send an HID request on the ctrl channel. "report" contains the report that
+   should be sent and "reqtype" the request type. Request-type can be
+   HID_REQ_SET_REPORT or HID_REQ_GET_REPORT.
+   This callback is optional. If not provided, HID core will assemble a raw
+   report following the HID specs and send it via the ->raw_request() callback.
+   The transport driver is free to implement this asynchronously.
+
+ - int (*wait) (struct hid_device *hdev)
+   Used by HID core before calling ->request() again. A transport driver can use
+   it to wait for any pending requests to complete if only one request is
+   allowed at a time.
+
+ - int (*raw_request) (struct hid_device *hdev, unsigned char reportnum,
+                       __u8 *buf, size_t count, unsigned char rtype,
Quoted 8 lines

Documentation/hid/hid-transport.txt line 261

Quoted 4 lines
Documentation/hid/hid-transport.txt new file +4 -0
+                       int reqtype)
+   Same as ->request() but provides the report as raw buffer. This request shall
+   be synchronous. A transport driver must not use ->wait() to complete such
+   requests.

2 questions/remarks here:
- is raw_request() meant to replace ->hid_output_raw_report() and ->hid_get_raw_report()?

Quoted 6 lines

Both. raw_request() with HID_REQ_GET_REPORT replaces hid_get_raw_report() and with HID_REQ_SET_REPORT it replaces hid_output_raw_report().
However, at least HIDP implements hid_output_raw_report() with HID_OUTPUT_REPORT as raw output report instead of SET_REPORT. For this, "output_report()" below can be used.

thanks. I definitively prefers this. The fact is that USB is very tolerant and can rely on the USB descriptor to know if it needs to use SET_REPORT or OUTPUT_REPORT. I tried to implement the same kind of thing in I2C, but I am not very happy with the final implementation. So definitively having an explicit way to handle this will allow us to write specific drivers in case there is some troubles with the automatic handling.

Quoted 6 lines
Quoted 3 lines

- reportnum declared as an unsigned will be problematic regarding the rare devices not having any report ID in their report descriptors.

I thought reportnum 0 is an invalid ID? I will check again and change to signed if needed. Thanks!

You are right. reportnum == 0 is treated in the USBHID stack as a report without a report ID. So keeping the unsigned is good here.

Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
Documentation/hid/hid-transport.txt new file +6 -0
+
+ - int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len)
+   Send raw output report via intr channel. Used by some HID device drivers
+   which require high throughput for outgoing requests on the intr channel. This
+   must not cause SET_REPORT calls! This must be implemented as asynchronous
+   output report on the intr channel!
Quoted 7 lines
Quoted 4 lines

For me, there is something wrong here. The name infers that we are trying to send an output_report directly (so through the USB URB or through the output i2c register), but you are implementing it through the intr channel... :-S

Again, "write on INTR" is OUTPUT channel for me. So I guess with the explanation above we are fine here?

sure, now it's clear.

Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Documentation/hid/hid-transport.txt new file +8 -0
+
+ - int (*hidinput_input_event) (struct input_dev *idev, unsigned int type,
+                                unsigned int code, int value)
+   Obsolete callback used by logitech converters. It is called when userspace
+   writes input events to the input device (eg., EV_LED). A driver can use this
+   callback to convert it into an output report and send it to the device. If
+   this callback is not provided, HID core will use ->request() or
+   ->raw_request() respectively.
Quoted 6 lines
Quoted 2 lines

I bet there will be a way to make this work with logitech devices too (if we implement a proper ->hid_output_raw_report() in each paired devices).

Yeah, I thought so, but I have no idea what the logitech-dj driver does. I guess we can drop this once we have no more users, but I wanted to avoid pushing to hard on it.

Well, I know it well because I helped Logitech pushing hid-logitech-dj upstream. Don't bother with it currently, we will remove this part from the documentation once all the drivers are converted.

Quoted 15 lines
Quoted 14 lines

Documentation/hid/hid-transport.txt line 284

Quoted 10 lines
Documentation/hid/hid-transport.txt new file +9 -0
+
+ - int (*idle) (struct hid_device *hdev, int report, int idle, int reqtype)
+   Perform SET/GET_IDLE request. Only used by USB-HID, do not implement!
+
+2.3) Data Path
+--------------
+
+Transport drivers are responsible of reading data from I/O devices. They must
+handle any state-tracking themselves. HID core does not implement protocol

I don't get the "state-tracking" here. The reports states should be handled by core, and I do not see the other states (or you meant PM states?).

Quoted 1 line

Ugh, I meant I/O-related state-tracking (or PM). I will rephrase that.

Thanks

Quoted 24 lines
Quoted 15 lines
Quoted 15 lines
Documentation/hid/hid-transport.txt new file +14 -0
+handshakes or other management commands which can be required by the given HID
+transport specification.
+
+Every raw data packet read from a device must be fed into HID core via
+hid_input_report(). You must specify the channel-type (intr or ctrl) and report
+type (input/output/feature). Under normal conditions, only input reports are
+provided via this API.
+
+Responses to GET_REPORT requests via ->request() must also be provided via this
+API. Responses to ->raw_request() are synchronous and must be intercepted by the
+transport driver and not passed to hid_input_report().
+Acknowledgements to SET_REPORT requests are not of interest to HID core.
+
+----------------------------------------------------
Quoted 8 lines
Quoted 4 lines

+Written 2013, David Herrmann <[email protected]> --
1.8.3.2

done!
Many thanks for this David. It was very interesting and detailed. It will make a great documentation.

Quoted 4 lines

Thanks a lot for reviewing. I will fix the remaining issues and with the "write-on-INTR is OUTPUT" I guess we are on the same page (except for minor issues)?

Yep, we are on the same page. Once you will have sent the v2, Jiri will have a look on it and I think we will be good :)

Cheers,
Benjamin

#22 lore raw

RFC 8/8 HID: implement new transport-driver callbacks

In reply to David Herrmann

->raw_request() and ->output_report() implement the SET/GET_REPORT and raw intr OUTPUT requests for all transport drivers. Once these are implemented, we can remove the old get_raw_report() and output_raw_report() per-device callbacks.

Note that the different device drivers expect different semantics for each call. The new helpers are properly documented and the semantics are known. We now need to carefully convert each driver to use the correct callback and then remove the old helpers.

UHID isn't converted, yet. I need to invest how user-space currently handles UHID_OUTPUT to see whether it's a SET_REPORT or intr-OUTPUT report.

---
 drivers/hid/uhid.c            | 27 +++++++++++++
 drivers/hid/usbhid/hid-core.c | 77 ++++++++++++++++++++++++++++++++++++
 include/linux/hid.h           |  8 +++-
 net/bluetooth/hidp/core.c     | 90 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 201 insertions(+), 1 deletion(-)
drivers/hid/uhid.c +27 -0
index f53f2d5..a344172 100644
@@ -244,12 +244,39 @@ static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,
 	return count;
 }
 
+static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf,
+				  size_t count)
+{
+	struct uhid_device *uhid = hid->driver_data;
+	unsigned long flags;
+	struct uhid_event *ev;
+
+	if (count < 1 || count > UHID_DATA_MAX)
+		return -EINVAL;
+
+	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
+	if (!ev)
+		return -ENOMEM;
+
+	ev->type = UHID_OUTPUT;
+	ev->u.output.size = count;
+	ev->u.output.rtype = UHID_OUTPUT_REPORT;
+	memcpy(ev->u.output.data, buf, count);
+
+	spin_lock_irqsave(&uhid->qlock, flags);
+	uhid_queue(uhid, ev);
+	spin_unlock_irqrestore(&uhid->qlock, flags);
+
+	return count;
+}
+
 static struct hid_ll_driver uhid_hid_driver = {
 	.start = uhid_hid_start,
 	.stop = uhid_hid_stop,
 	.open = uhid_hid_open,
 	.close = uhid_hid_close,
 	.parse = uhid_hid_parse,
+	.output_report = uhid_hid_output_report,
 };
 
 #ifdef CONFIG_COMPAT
drivers/hid/usbhid/hid-core.c +77 -0

101 lines not on this page · raw message

include/linux/hid.h +7 -1
index ea4b828..c0bf05f 100644
@@ -693,8 +693,14 @@ struct hid_ll_driver {
 			struct hid_report *report, int reqtype);
 
 	int (*wait)(struct hid_device *hdev);
-	int (*idle)(struct hid_device *hdev, int report, int idle, int reqtype);
 
+	int (*raw_request) (struct hid_device *hdev, unsigned char reportnum,
+			    __u8 *buf, size_t len, unsigned char rtype,
+			    int reqtype);
+
+	int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len);
+
+	int (*idle)(struct hid_device *hdev, int report, int idle, int reqtype);
 };
 
 #define	PM_HINT_FULLON	1<<5
net/bluetooth/hidp/core.c +90 -0

108 lines not on this page · raw message

Signature · 1 line
1.8.3.2
#23 lore raw
In reply to David Herrmann

Hi David,

Quoted 6 lines · David Herrmann

Hi

This series provides some cleanups for HID transport drivers:
Patch #1: Cleanup which can be picked up
Patch #2-#6: Provide generic hidinput_input_event() helpers Patch #7-#8: Transport-driver Cleanup

First, thank you very much for this work. Especially the patch #7. I did not read it entirely though, but it looks great.

I won't have the brain in a good state this evening to do an entire review right now, but here are a few preliminary comments:

Quoted 5 lines

Patch #7-#8 is an attempt to clean up the transport driver callbacks. Lets look at how the following hooks are currently implemented:
1) GET_REPORT: Issue a synchronous GET_REPORT via the ctrl-channel
2) SET_REPORT: Issue a synchronous SET_REPORT via the ctrl-channel

I am not 100% sure GET/SET_REPORT are synchronous. At least, they ensure for the usbhid module some kind of PM negotiation which allows communication.

Quoted 10 lines

3) OUTPUT: Issue an asynchronous OUTPUT report on the intr-channel

USB-HID:
1) GET_REPORT can be issued via ->hid_get_raw_report() for all three report types correctly.
2) SET_REPORT can only be issued for FEATURE reports via ->hid_output_raw_report(). INPUT and OUTPUT reports on the same callback cause an asynchronous report on the intr channel.
3) OUTPUT reports can be issued via ->hid_output_raw_report() as described above in SET_REPORT.

I'm afraid no (for the 3 points). See dcd9006b1b053c7 in Linus' tree. hid_hw_request uses usbhid_submit_report() which is very different from ->hid_output_raw_report() or ->hid_get_raw_report() thanks to all the glue around the USB communication.

Quoted 11 lines

HIDP:
1) GET_REPORT: same as for USB-HID
2) SET_REPORT can only be issued for FEATURE reports via ->hid_output_raw_report(). SET_REPORT for INPUT reports is forbidden (why?) and SET_REPORT for OUTPUT reports is actually implemented as 3)
3) OUTPUT reports can be issued via ->hid_output_raw_report().

I2C:
1) GET_REPORT implemented for INPUT and FEATURE via ->hid_get_raw_report(). Forbidden for OUTPUT reports (why?).

To me (I will double check later):
- an INPUT report is read only, and send spontaneous events.
- an OUTPUT report is write only.
- a FEATURE is read/write but does not send spontaneous events

Then, the device firmware may allow you to do other things, but I doubt there is any sense to write something on an INPUT report for example.

Quoted 15 lines

2) SET_REPORT can be issued for OUTPUT and FEATURE reports via

    ->hid_output_raw_report() but is forbidden for INPUT reports (why?).

3) OUTPUT reports cannot be issued at all.

UHID:
1) GET_REPORT only supported for FEATURE via ->hid_get_raw_report()
2) SET_REPORT not supported at all
3) OUTPUT supported via ->hid_output_raw_report()

As this is all very inconsistent, I added two new callbacks:
->raw_request() is the same as ->request() but with a raw buffer. It should be implemented by the transport drivers as SET/GET_REPORT calls in the ctrl-channel.

Quoted 7 lines

->output_report() is supposed to send an OUTPUT report on the intr-channel (same channel asynchronous input reports are received from).

Please see the hid-transport.txt for a description. The last patch tries to implement them. I have no idea how to implement it for i2c, it seems that i2c multiplexes ctrl and intr channels on a single i2c channel. I don't know how to send raw output reports at all.. Help welcome!

I can help. Actually, on I2C, there is no ctrl and intr channels. You just send plain reports from both sides, and the device can notify the host from an INPUT report thanks to an interrupt line (external to the actual I2C communication). Anyway, I'll implement and test the I2C stuffs if you need.

Quoted 4 lines

If there is more interest in this work, I will clean it up, try to convert the other hid_ll_driver drivers and resend as a proper patchset.

Yes, I am very interested. Before cleaning it up, I think we already have a lot to discuss (at least, I have some points :-P ).

Again, thanks for this work.

Cheers,
Benjamin

Quoted 30 lines

Cheers
David

David Herrmann (8):
HID: usbhid: make usbhid_set_leds() static
HID: usbhid: update LED fields unlocked
HID: input: generic hidinput_input_event handler
HID: usbhid: use generic hidinput_input_event()
HID: i2c: use generic hidinput_input_event()
HID: uhid: use generic hidinput_input_event()
HID: add transport driver documentation
HID: implement new transport-driver callbacks

 Documentation/hid/hid-transport.txt | 299 ++++++++++++++++++++++++++++++++++++
 Documentation/hid/uhid.txt          |   4 +-
 drivers/hid/hid-input.c             |  80 +++++++++-
 drivers/hid/i2c-hid/i2c-hid.c       |  24 ---
 drivers/hid/uhid.c                  |  52 ++++---
 drivers/hid/usbhid/hid-core.c       | 144 +++++++++--------
 drivers/hid/usbhid/usbhid.h         |   3 -
 include/linux/hid.h                 |  10 +-
 include/uapi/linux/uhid.h           |   4 +-
 net/bluetooth/hidp/core.c           |  90 +++++++++++
 10 files changed, 590 insertions(+), 120 deletions(-)
 create mode 100644 Documentation/hid/hid-transport.txt

--
1.8.3.2

#24 lore raw
In reply to David Herrmann
Quoted 1 line · David Herrmann

This series provides some cleanups for HID transport drivers:

Hi David,

thanks a lot for your work, again.

I have now applied all the patches from the series, except for:

- 3/8, waiting for v2
- 7/8, waiting for v2 as well, plus haven't finished reviewing it fully
- 8/8, still thinking about it and reviewing

Signature · 2 lines
Jiri Kosina
SUSE Labs
#25 lore raw

Hi Jiri

Quoted 11 lines · Jiri Kosina
Quoted 1 line · David Herrmann

This series provides some cleanups for HID transport drivers:

Hi David,

thanks a lot for your work, again.

I have now applied all the patches from the series, except for:

- 3/8, waiting for v2

Ugh, patches 4-6 use the generic helper from 3 implicitly. So with the current tree, they will not be able to send any LED events. I think my conclusion in the discussion on #3 was wrong. I cannot rebase it on the end of the series. Sorry, I missed that.

For v2, as mentioned in the thread, I cannot write a generic fallback for hid_hw_request() without patch #8. That's because the transport drivers handle the raw reports differently. #8 fixes that.

So imho we should apply #3 as it is now. If we ever have a generic hid_hw_request(), we can just move the code from the LED handler to hid_hw_request_generic().

Quoted 2 lines

- 7/8, waiting for v2 as well, plus haven't finished reviewing it fully
- 8/8, still thinking about it and reviewing

They're both RFC so no hurry. Especially Patch #8 depends highly on the ideas in #7 which will change according to Benjamin's comments in v2.

Thanks for applying the first few fixes. Sorry for the confusion in #3. I'd vote for applying it directly, Benjamin, what do you think? I was busy the last few weeks, but I will have time to work on this again starting next week.

Regards
David

#26 lore raw
Quoted 15 lines · David Herrmann
Quoted 9 lines
Quoted 1 line

This series provides some cleanups for HID transport drivers:

Hi David,

thanks a lot for your work, again.

I have now applied all the patches from the series, except for:

- 3/8, waiting for v2

Ugh, patches 4-6 use the generic helper from 3 implicitly. So with the current tree, they will not be able to send any LED events. I think my conclusion in the discussion on #3 was wrong. I cannot rebase it on the end of the series. Sorry, I missed that.

Quoted 7 lines

For v2, as mentioned in the thread, I cannot write a generic fallback for hid_hw_request() without patch #8. That's because the transport drivers handle the raw reports differently. #8 fixes that.

So imho we should apply #3 as it is now. If we ever have a generic hid_hw_request(), we can just move the code from the LED handler to hid_hw_request_generic().

Bah, brainfart on my side as well. You are right. I am now pulling 3/8 into the tree as well and pushing it out.

Signature · 2 lines
Jiri Kosina
SUSE Labs