diff mbox series

[v1,3/5] HID: add on_transport_error() field to struct hid_driver

Message ID 20211229231141.303919-4-dmanti@microsoft.com
State New
Headers show
Series Add spi-hid, transport for HID over SPI bus | expand

Commit Message

Dmitry Antipov Dec. 29, 2021, 11:11 p.m. UTC
This new API allows a transport driver to notify the HID device driver
about a transport layer error.

Signed-off-by: Dmitry Antipov <dmanti@microsoft.com>
---
 include/linux/hid.h | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

Comments

Benjamin Tissoires Jan. 3, 2022, 3:26 p.m. UTC | #1
On Thu, Dec 30, 2021 at 12:11 AM Dmitry Antipov <daantipov@gmail.com> wrote:
>
> This new API allows a transport driver to notify the HID device driver
> about a transport layer error.

I do not see entirely the purpose of this new callback:

- when we receive the device initiated reset, this is a specific
device event, so it would make sense...
- but for things like HID_TRANSPORT_ERROR_TYPE_BUS_OUTPUT_TRANSFER, I
would expect the caller to return that error code instead of having an
async function called.

I think it might be simpler to add a more specific
.device_initiated_reset() callback instead of trying to be generic.

>
> Signed-off-by: Dmitry Antipov <dmanti@microsoft.com>
> ---
>  include/linux/hid.h | 19 +++++++++++++++++++
>  1 file changed, 19 insertions(+)
>
> diff --git a/include/linux/hid.h b/include/linux/hid.h
> index 1f134c8f8972..97041c322a0f 100644
> --- a/include/linux/hid.h
> +++ b/include/linux/hid.h
> @@ -703,6 +703,20 @@ struct hid_usage_id {
>         __u32 usage_code;
>  };
>
> +enum hid_transport_error_type {
> +       HID_TRANSPORT_ERROR_TYPE_BUS_INPUT_TRANSFER_START = 0,
> +       HID_TRANSPORT_ERROR_TYPE_BUS_INPUT_TRANSFER_BODY,
> +       HID_TRANSPORT_ERROR_TYPE_BUS_INPUT_TRANSFER_HEADER,

Those 3 enums above are completely SPI specifics, but they are
declared in the generic hid.h header.
Also, if I am a driver, what am I supposed to do when I receive such an error?
Up till now, the most we did was to raise a warning to the user, and
paper over it. I am open to some smarter behavior, but I do not see
what a mouse driver is supposed to do with that kind of error.

> +       HID_TRANSPORT_ERROR_TYPE_BUS_OUTPUT_TRANSFER,

Seems like this would better handled as a return code than an async callback

> +       HID_TRANSPORT_ERROR_TYPE_DEVICE_INITIATED_RESET,

OK for this (but see my comment in the commit description)

> +       HID_TRANSPORT_ERROR_TYPE_HEADER_DATA,
> +       HID_TRANSPORT_ERROR_TYPE_INPUT_REPORT_DATA,
> +       HID_TRANSPORT_ERROR_TYPE_REPORT_TYPE,

Those look like SPI specifics

> +       HID_TRANSPORT_ERROR_TYPE_GET_FEATURE_RESPONSE,

Seems like this would be better handled as a return code than an async
callback (and it should already be the case because
hid_ll_raw_request() is synchronous and can fail if the HW complains).

> +       HID_TRANSPORT_ERROR_TYPE_REGULATOR_ENABLE,
> +       HID_TRANSPORT_ERROR_TYPE_REGULATOR_DISABLE

Again, what am I supposed to do with those 2 if they fail, besides
emitting a dev_err(), which the low level transport driver can do?


Cheers,
Benjamin

> +};
> +
>  /**
>   * struct hid_driver
>   * @name: driver name (e.g. "Footech_bar-wheel")
> @@ -726,6 +740,7 @@ struct hid_usage_id {
>   * @suspend: invoked on suspend (NULL means nop)
>   * @resume: invoked on resume if device was not reset (NULL means nop)
>   * @reset_resume: invoked on resume if device was reset (NULL means nop)
> + * @on_transport_error: invoked on error hit by transport driver
>   *
>   * probe should return -errno on error, or 0 on success. During probe,
>   * input will not be passed to raw_event unless hid_device_io_start is
> @@ -777,6 +792,10 @@ struct hid_driver {
>         void (*feature_mapping)(struct hid_device *hdev,
>                         struct hid_field *field,
>                         struct hid_usage *usage);
> +       void (*on_transport_error)(struct hid_device *hdev,
> +                       int err_type,
> +                       int err_code,
> +                       bool handled);
>  #ifdef CONFIG_PM
>         int (*suspend)(struct hid_device *hdev, pm_message_t message);
>         int (*resume)(struct hid_device *hdev);
> --
> 2.25.1
>
Benjamin Tissoires Jan. 13, 2022, 10:02 a.m. UTC | #2
On Sat, Jan 8, 2022 at 2:10 AM Dmitry Antipov <dmanti@microsoft.com> wrote:
>
> On Tue, Jan 4, 2022 at 7:52 AM Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
> >
> > On Tue, Jan 4, 2022 at 3:08 AM Dmitry Antipov <dmanti@microsoft.com>
> > wrote:
> > >
> > > > -----Original Message-----
> > > > From: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > > > Sent: Monday, January 3, 2022 7:27 AM
> > > > To: Dmitry Antipov <daantipov@gmail.com>
> > > > Cc: Jiri Kosina <jikos@kernel.org>; open list:HID CORE LAYER <linux-
> > > > input@vger.kernel.org>; Felipe Balbi <balbi@kernel.org>; Dmitry
> > > > Antipov <dmanti@microsoft.com>
> > > > Subject: [EXTERNAL] Re: [PATCH v1 3/5] HID: add on_transport_error()
> > > > field to struct hid_driver
> > > >
> > > > On Thu, Dec 30, 2021 at 12:11 AM Dmitry Antipov
> > > > <daantipov@gmail.com>
> > > > wrote:
> > > > >
> > > > > This new API allows a transport driver to notify the HID device
> > > > > driver about a transport layer error.
> > > >
> > > > I do not see entirely the purpose of this new callback:
> > > >
> > > > - when we receive the device initiated reset, this is a specific
> > > > device event, so it would make sense...
> > > > - but for things like
> > HID_TRANSPORT_ERROR_TYPE_BUS_OUTPUT_TRANSFER,
> > > > I would expect the caller to return that error code instead of
> > > > having an async function called.
> > > >
> > > > I think it might be simpler to add a more specific
> > > > .device_initiated_reset() callback instead of trying to be generic.
> > > >
> > >
> > > The intention of this new callback is to notify the device driver of a
> > > transport-layer error for at least two reasons:
> > > 1. Delegating the decision making. For certain types of errors the
> > > spec states that the host _may_ reset the device. Right now there are
> > > not many devices that support HID over SPI, but I wanted to allow the
> > > flexibility for each vendor to decide what cases to error-handle.
> >
> > Looking at section 9 (Error handling) of the HID SPI protocol spec, it seems that
> > the only time the host may (or not) decide to reset the device is when receiving
> > a timeout error.
> > And looking at the phrasing there, I think we ought to simply reset the device
> > anyway.
> >
> > So now that I have the spec under my eyes, I would think that for this part, the
> > host is expected to reset the device, which in turn makes this a spi-hid
> > responsibility.
> >
> > So I would suggest adding a callback notifying that the device has been reset,
> > and with a flag telling whether it's host or device initiated.
> > Then in hid-microsoft, hid-multitouch we can deal with that situation.
> >
> > Putting this at the transport layer allows for a common behavior which won't
> > depend on the leaf HID driver in use.
>
> Please note the "ready" flag that is wired to a sysfs attribute in
> spi-hid in patch 5/5. In our case the touch digitizer sends the raw
> data, so we process it and convert it into input events in a userspace
> service we call the touch daemon. The touch daemon detects digitizer
> resets via the ready flag: any time the flag goes from "not ready" to
> "ready", it is interpreted as digitizer coming out of reset and the
> touch daemon then sends some system state info to the digitizer, among
> other things. While the ready flag is "not ready", in our architecture,
> the userspace will not send ioctl's or write into the hidraw device.

So that means that this device is forwarding the raw touch map?

>
> All this means that the code in hid-microsoft won't be implementing this
> new notify_of_reset() callback. Since in the final submission there
> won't be an implementation of this callback, is it worth adding at this
> stage? Can it go in as a REVISIT or a FIXME comment until such
> notification to the leaf driver is needed?

If there is no users, then it's probably best to not implement it. We
could add a comment, yes, but maybe not a FIXME, just a regular
comment.

>
> > > 2. Telemetry instrumentation to gather statistics on various error
> > > conditions hit in spi-hid. The way we implement this is by publishing
> > > sysfs attributes with error counters from the device driver and epoll
> > > on these attributes from userspace. Here is a snippet from a
> > > yet-to-be- sent patch to hid-microsoft.c:
> >
> > Oh, that's interesting. How about we put those stats in api-hid-core.c, so that
> > anybody can benefit from it?
> > Those are per-device anyway so that might be a useful way to debug issues
> > when there are weird behaviors.
>
> I haven't found an api-hid-core.c. Are you suggesting I create a new
> file at drivers/hid that would extend hid-core.c? If yes, can you please
> tell what you expect to be in the HID core vs the transport driver?

Sorry I meant i2c-hid-core.c :(

>
> > >
> > > static void ms_on_transport_error(struct hid_device *hdev,
> > >                                         int err_type,
> > >                                         int err_code,
> > >                                         bool handled) {
> > >         struct ms_data *ms = hid_get_drvdata(hdev);
> > >
> > >         if (ms->quirks & MS_TRANSPORT_ERROR_HANDLING) {
> > >
> > >                 switch (err_type) {
> > >                         case
> > HID_TRANSPORT_ERROR_TYPE_BUS_INPUT_TRANSFER_START:
> > >                         case
> > HID_TRANSPORT_ERROR_TYPE_BUS_INPUT_TRANSFER_BODY:
> > >                         case
> > HID_TRANSPORT_ERROR_TYPE_BUS_INPUT_TRANSFER_HEADER:
> > >                         case HID_TRANSPORT_ERROR_TYPE_HEADER_DATA:
> > >                         case HID_TRANSPORT_ERROR_TYPE_BUS_OUTPUT_TRANSFER:
> > >                                 ms->bus_error_count++;
> > >                                 ms->bus_last_error = err_code;
> > >                                 break;
> > >                         case HID_TRANSPORT_ERROR_TYPE_DEVICE_INITIATED_RESET:
> > >                                 ms->dir_count++;
> > >                                 break;
> > >                         case HID_TRANSPORT_ERROR_TYPE_INPUT_REPORT_DATA:
> > >                         case HID_TRANSPORT_ERROR_TYPE_REPORT_TYPE:
> > >                         case HID_TRANSPORT_ERROR_TYPE_GET_FEATURE_RESPONSE:
> > >                                 if (!handled && (hdev->ll_driver->reset != 0))
> > >                                         hdev->ll_driver->reset(hdev);
> > >                                 break;
> > >                         case HID_TRANSPORT_ERROR_TYPE_REGULATOR_ENABLE:
> > >                         case HID_TRANSPORT_ERROR_TYPE_REGULATOR_DISABLE:
> > >                                 ms->regulator_error_count++;
> > >                                 ms->regulator_last_error = err_code;
> > >                                 break;
> > >                 }
> > >         }
> > > }
> > >
> > > Please let me know what you think. Would it be ok to make a decision
> > > to error-handle (reset the device) at a transport layer certain cases
> > > that are not required by the spec?
> >
> > I would suggest we stay as close as possible to the spec. When the spec says we
> > need to reset, we do it, and notify the driver.
> > TBH, the only thing that works in the long run is to map the implementation
> > from Windows, when this gets more widespread.
> > And we can always quirk the devices that need a special error handling or
> > revisit at that particular time when we get the device in question.
> >
> >
> > >
> > > If you have a suggestion on how to pipe telemetry counters to
> > > userspace without this generic callback I can try it out as well.
> >
> > So as I mentioned we should probably set those in spi-hid. The other and more
> > modern approach is to use BPF, but that would be only when the program is
> > loaded. So I would keep the raw values in spi-hid, export them through sysfs,
> > and possibly allow for some tracing through BPF if we want to get something
> > more dynamic (like real time reading of values).
>
> Does api-hid-core.c play a role in the suggested non-BPF, basic approach?

Again, sorry for the confusion. I think you should keep in mind that
BPF might be a solution in the long run, but right now it's not
merged, so please ignore it for the time being :)

Cheers,
Benjamin

>
> >
> > Cheers,
> > Benjamin
> >
> > >
> > > > >
> > > > > Signed-off-by: Dmitry Antipov <dmanti@microsoft.com>
> > > > > ---
> > > > >  include/linux/hid.h | 19 +++++++++++++++++++
> > > > >  1 file changed, 19 insertions(+)
> > > > >
> > > > > diff --git a/include/linux/hid.h b/include/linux/hid.h index
> > > > > 1f134c8f8972..97041c322a0f 100644
> > > > > --- a/include/linux/hid.h
> > > > > +++ b/include/linux/hid.h
> > > > > @@ -703,6 +703,20 @@ struct hid_usage_id {
> > > > >         __u32 usage_code;
> > > > >  };
> > > > >
> > > > > +enum hid_transport_error_type {
> > > > > +       HID_TRANSPORT_ERROR_TYPE_BUS_INPUT_TRANSFER_START = 0,
> > > > > +       HID_TRANSPORT_ERROR_TYPE_BUS_INPUT_TRANSFER_BODY,
> > > > > +       HID_TRANSPORT_ERROR_TYPE_BUS_INPUT_TRANSFER_HEADER,
> > > >
> > > > Those 3 enums above are completely SPI specifics, but they are
> > > > declared in the generic hid.h header.
> > > > Also, if I am a driver, what am I supposed to do when I receive such an
> > error?
> > > > Up till now, the most we did was to raise a warning to the user, and
> > > > paper over it. I am open to some smarter behavior, but I do not see
> > > > what a mouse driver is supposed to do with that kind of error.
> > > >
> > > > > +       HID_TRANSPORT_ERROR_TYPE_BUS_OUTPUT_TRANSFER,
> > > >
> > > > Seems like this would better handled as a return code than an async
> > > > callback
> > > >
> > > > > +       HID_TRANSPORT_ERROR_TYPE_DEVICE_INITIATED_RESET,
> > > >
> > > > OK for this (but see my comment in the commit description)
> > > >
> > > > > +       HID_TRANSPORT_ERROR_TYPE_HEADER_DATA,
> > > > > +       HID_TRANSPORT_ERROR_TYPE_INPUT_REPORT_DATA,
> > > > > +       HID_TRANSPORT_ERROR_TYPE_REPORT_TYPE,
> > > >
> > > > Those look like SPI specifics
> > > >
> > > > > +       HID_TRANSPORT_ERROR_TYPE_GET_FEATURE_RESPONSE,
> > > >
> > > > Seems like this would be better handled as a return code than an
> > > > async callback (and it should already be the case because
> > > > hid_ll_raw_request() is synchronous and can fail if the HW complains).
> > > >
> > > > > +       HID_TRANSPORT_ERROR_TYPE_REGULATOR_ENABLE,
> > > > > +       HID_TRANSPORT_ERROR_TYPE_REGULATOR_DISABLE
> > > >
> > > > Again, what am I supposed to do with those 2 if they fail, besides
> > > > emitting a dev_err(), which the low level transport driver can do?
> > > >
> > > >
> > > > Cheers,
> > > > Benjamin
> > > >
> > > > > +};
> > > > > +
> > > > >  /**
> > > > >   * struct hid_driver
> > > > >   * @name: driver name (e.g. "Footech_bar-wheel") @@ -726,6 +740,7
> > > > > @@ struct hid_usage_id {
> > > > >   * @suspend: invoked on suspend (NULL means nop)
> > > > >   * @resume: invoked on resume if device was not reset (NULL means
> > nop)
> > > > >   * @reset_resume: invoked on resume if device was reset (NULL
> > > > > means
> > > > > nop)
> > > > > + * @on_transport_error: invoked on error hit by transport driver
> > > > >   *
> > > > >   * probe should return -errno on error, or 0 on success. During probe,
> > > > >   * input will not be passed to raw_event unless
> > > > > hid_device_io_start is @@ -777,6 +792,10 @@ struct hid_driver {
> > > > >         void (*feature_mapping)(struct hid_device *hdev,
> > > > >                         struct hid_field *field,
> > > > >                         struct hid_usage *usage);
> > > > > +       void (*on_transport_error)(struct hid_device *hdev,
> > > > > +                       int err_type,
> > > > > +                       int err_code,
> > > > > +                       bool handled);
> > > > >  #ifdef CONFIG_PM
> > > > >         int (*suspend)(struct hid_device *hdev, pm_message_t message);
> > > > >         int (*resume)(struct hid_device *hdev);
> > > > > --
> > > > > 2.25.1
> > > > >
> > >
>
diff mbox series

Patch

diff --git a/include/linux/hid.h b/include/linux/hid.h
index 1f134c8f8972..97041c322a0f 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -703,6 +703,20 @@  struct hid_usage_id {
 	__u32 usage_code;
 };
 
+enum hid_transport_error_type {
+	HID_TRANSPORT_ERROR_TYPE_BUS_INPUT_TRANSFER_START = 0,
+	HID_TRANSPORT_ERROR_TYPE_BUS_INPUT_TRANSFER_BODY,
+	HID_TRANSPORT_ERROR_TYPE_BUS_INPUT_TRANSFER_HEADER,
+	HID_TRANSPORT_ERROR_TYPE_BUS_OUTPUT_TRANSFER,
+	HID_TRANSPORT_ERROR_TYPE_DEVICE_INITIATED_RESET,
+	HID_TRANSPORT_ERROR_TYPE_HEADER_DATA,
+	HID_TRANSPORT_ERROR_TYPE_INPUT_REPORT_DATA,
+	HID_TRANSPORT_ERROR_TYPE_REPORT_TYPE,
+	HID_TRANSPORT_ERROR_TYPE_GET_FEATURE_RESPONSE,
+	HID_TRANSPORT_ERROR_TYPE_REGULATOR_ENABLE,
+	HID_TRANSPORT_ERROR_TYPE_REGULATOR_DISABLE
+};
+
 /**
  * struct hid_driver
  * @name: driver name (e.g. "Footech_bar-wheel")
@@ -726,6 +740,7 @@  struct hid_usage_id {
  * @suspend: invoked on suspend (NULL means nop)
  * @resume: invoked on resume if device was not reset (NULL means nop)
  * @reset_resume: invoked on resume if device was reset (NULL means nop)
+ * @on_transport_error: invoked on error hit by transport driver
  *
  * probe should return -errno on error, or 0 on success. During probe,
  * input will not be passed to raw_event unless hid_device_io_start is
@@ -777,6 +792,10 @@  struct hid_driver {
 	void (*feature_mapping)(struct hid_device *hdev,
 			struct hid_field *field,
 			struct hid_usage *usage);
+	void (*on_transport_error)(struct hid_device *hdev,
+			int err_type,
+			int err_code,
+			bool handled);
 #ifdef CONFIG_PM
 	int (*suspend)(struct hid_device *hdev, pm_message_t message);
 	int (*resume)(struct hid_device *hdev);