UHID - User-space I/O driver support for HID subsystem

UHID allows user-space to implement HID transport drivers. Please seeHID I/O Transport Drivers for an introduction into HID transport drivers. This documentrelies heavily on the definitions declared there.

With UHID, a user-space transport driver can create kernel hid-devices for eachdevice connected to the user-space controlled bus. The UHID API defines the I/Oevents provided from the kernel to user-space and vice versa.

There is an example user-space application in ./samples/uhid/uhid-example.c

The UHID API

UHID is accessed through a character misc-device. The minor number is allocateddynamically so you need to rely on udev (or similar) to create the device node.This is /dev/uhid by default.

If a new device is detected by your HID I/O Driver and you want to register thisdevice with the HID subsystem, then you need to open /dev/uhid once for eachdevice you want to register. All further communication is done by read()’ing orwrite()’ing “structuhid_event” objects. Non-blocking operations are supportedby setting O_NONBLOCK:

struct uhid_event {      __u32 type;      union {              struct uhid_create2_req create2;              struct uhid_output_req output;              struct uhid_input2_req input2;              ...      } u;};

The “type” field contains the ID of the event. Depending on the ID differentpayloads are sent. You must not split a single event across multiple read()’s ormultiple write()’s. A single event must always be sent as a whole. Furthermore,only a single event can be sent per read() or write(). Pending data is ignored.If you want to handle multiple events in a single syscall, then use vectoredI/O withreadv()/writev().The “type” field defines the payload. For each type, there is apayload-structure available in the union “u” (except for empty payloads). Thispayload contains management and/or device data.

The first thing you should do is send a UHID_CREATE2 event. This willregister the device. UHID will respond with a UHID_START event. You can nowstart sending data to and reading data from UHID. However, unless UHID sends theUHID_OPEN event, the internally attached HID Device Driver has no user attached.That is, you might put your device asleep unless you receive the UHID_OPENevent. If you receive the UHID_OPEN event, you should start I/O. If the lastuser closes the HID device, you will receive a UHID_CLOSE event. This may befollowed by a UHID_OPEN event again and so on. There is no need to performreference-counting in user-space. That is, you will never receive multipleUHID_OPEN events without a UHID_CLOSE event. The HID subsystem performsref-counting for you.You may decide to ignore UHID_OPEN/UHID_CLOSE, though. I/O is allowed eventhough the device may have no users.

If you want to send data on the interrupt channel to the HID subsystem, you senda HID_INPUT2 event with your raw data payload. If the kernel wants to send dataon the interrupt channel to the device, you will read a UHID_OUTPUT event.Data requests on the control channel are currently limited to GET_REPORT andSET_REPORT (no other data reports on the control channel are defined so far).Those requests are always synchronous. That means, the kernel sendsUHID_GET_REPORT and UHID_SET_REPORT events and requires you to forward them tothe device on the control channel. Once the device responds, you must forwardthe response via UHID_GET_REPORT_REPLY and UHID_SET_REPORT_REPLY to the kernel.The kernel blocks internal driver-execution during such round-trips (times outafter a hard-coded period).

If your device disconnects, you should send a UHID_DESTROY event. This willunregister the device. You can now send UHID_CREATE2 again to register a newdevice.If you close() the fd, the device is automatically unregistered and destroyedinternally.

write()

write() allows you to modify the state of the device and feed input data intothe kernel. The kernel will parse the event immediately and if the event ID isnot supported, it will return -EOPNOTSUPP. If the payload is invalid, then-EINVAL is returned, otherwise, the amount of data that was read is returned andthe request was handled successfully. O_NONBLOCK does not affect write() aswrites are always handled immediately in a non-blocking fashion. Future requestsmight make use of O_NONBLOCK, though.

UHID_CREATE2:

This creates the internal HID device. No I/O is possible until you send thisevent to the kernel. The payload is of typestructuhid_create2_req andcontains information about your device. You can start I/O now.

UHID_DESTROY:

This destroys the internal HID device. No further I/O will be accepted. Theremay still be pending messages that you can receive with read() but no furtherUHID_INPUT events can be sent to the kernel.You can create a new device by sending UHID_CREATE2 again. There is no need toreopen the character device.

UHID_INPUT2:

You must send UHID_CREATE2 before sending input to the kernel! This eventcontains a data-payload. This is the raw data that you read from your deviceon the interrupt channel. The kernel will parse the HID reports.

UHID_GET_REPORT_REPLY:

If you receive a UHID_GET_REPORT request you must answer with this request.You must copy the “id” field from the request into the answer. Set the “err”field to 0 if no error occurred or to EIO if an I/O error occurred.If “err” is 0 then you should fill the buffer of the answer with the resultsof the GET_REPORT request and set “size” correspondingly.

UHID_SET_REPORT_REPLY:

This is the SET_REPORT equivalent of UHID_GET_REPORT_REPLY. Unlike GET_REPORT,SET_REPORT never returns a data buffer, therefore, it’s sufficient to set the“id” and “err” fields correctly.

read()

read() will return a queued output report. No reaction is required to any ofthem but you should handle them according to your needs.

UHID_START:

This is sent when the HID device is started. Consider this as an answer toUHID_CREATE2. This is always the first event that is sent. Note that thisevent might not be available immediately after write(UHID_CREATE2) returns.Device drivers might require delayed setups.This event contains a payload of type uhid_start_req. The “dev_flags” fielddescribes special behaviors of a device. The following flags are defined:

  • UHID_DEV_NUMBERED_FEATURE_REPORTS

  • UHID_DEV_NUMBERED_OUTPUT_REPORTS

  • UHID_DEV_NUMBERED_INPUT_REPORTS

    Each of these flags defines whether a given report-type uses numberedreports. If numbered reports are used for a type, all messages fromthe kernel already have the report-number as prefix. Otherwise, noprefix is added by the kernel.For messages sent by user-space to the kernel, you must adjust theprefixes according to these flags.

UHID_STOP:

This is sent when the HID device is stopped. Consider this as an answer toUHID_DESTROY.

If you didn’t destroy your device via UHID_DESTROY, but the kernel sends anUHID_STOP event, this should usually be ignored. It means that the kernelreloaded/changed the device driver loaded on your HID device (or some othermaintenance actions happened).

You can usually ignore any UHID_STOP events safely.

UHID_OPEN:

This is sent when the HID device is opened. That is, the data that the HIDdevice provides is read by some other process. You may ignore this event butit is useful for power-management. As long as you haven’t received this eventthere is actually no other process that reads your data so there is no need tosend UHID_INPUT2 events to the kernel.

UHID_CLOSE:

This is sent when there are no more processes which read the HID data. It isthe counterpart of UHID_OPEN and you may as well ignore this event.

UHID_OUTPUT:

This is sent if the HID device driver wants to send raw data to the I/Odevice on the interrupt channel. You should read the payload and forward it tothe device. The payload is of type “structuhid_output_req”.This may be received even though you haven’t received UHID_OPEN yet.

UHID_GET_REPORT:

This event is sent if the kernel driver wants to perform a GET_REPORT requeston the control channel as described in the HID specs. The report-type andreport-number are available in the payload.The kernel serializes GET_REPORT requests so there will never be two inparallel. However, if you fail to respond with a UHID_GET_REPORT_REPLY, therequest might silently time out.Once you read a GET_REPORT request, you shall forward it to the HID device andremember the “id” field in the payload. Once your HID device responds to theGET_REPORT (or if it fails), you must send a UHID_GET_REPORT_REPLY to thekernel with the exact same “id” as in the request. If the request alreadytimed out, the kernel will ignore the response silently. The “id” field isnever re-used, so conflicts cannot happen.

UHID_SET_REPORT:

This is the SET_REPORT equivalent of UHID_GET_REPORT. On receipt, you shallsend a SET_REPORT request to your HID device. Once it replies, you must tellthe kernel about it via UHID_SET_REPORT_REPLY.The same restrictions as for UHID_GET_REPORT apply.


Written 2012, David Herrmann <dh.herrmann@gmail.com>