The linux HID subsystem offers kernel driver programmers a whole bunch of hooks to insert custom code before, after and while automatic processing. Those hooks are defined in <linux/hid.h>
, unfortunately they aren’t documented very well similarly to the whole loading process of driver modules in Linux.
Table of contents
- Table of contents
- The driver an its hooks
- raw_event hook
- parameters
- expected return value (int)
- explanation
- event hook
- parameters
- expected return value (int)
- explanation
- event hook
- report_fixup hook
- parameters
- expected return value
- explanation
- input_mapping hook
- parameters
- expected return value (int)
- explanation
- input_mapped hook
- input_configured hook
- feature_mapping hook
- suspend hook
- resume hook
- reset_resume hook
- References
The driver an its hooks
struct hid_driver { char *name; const struct hid_device_id *id_table; struct list_head dyn_list; spinlock_t dyn_lock; bool (*match)(struct hid_device *dev, bool ignore_special_driver); int (*probe)(struct hid_device *dev, const struct hid_device_id *id); void (*remove)(struct hid_device *dev); const struct hid_report_id *report_table; int (*raw_event)(struct hid_device *hdev, struct hid_report *report, u8 *data, int size); const struct hid_usage_id *usage_table; int (*event)(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value); void (*report)(struct hid_device *hdev, struct hid_report *report); __u8 *(*report_fixup)(struct hid_device *hdev, __u8 *buf, unsigned int *size); int (*input_mapping)(struct hid_device *hdev, struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max); int (*input_mapped)(struct hid_device *hdev, struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max); int (*input_configured)(struct hid_device *hdev, struct hid_input *hidinput); void (*feature_mapping)(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage); #ifdef CONFIG_PM int (*suspend)(struct hid_device *hdev, pm_message_t message); int (*resume)(struct hid_device *hdev); int (*reset_resume)(struct hid_device *hdev); #endif /* private: */ struct device_driver driver; };
raw_event hook
int (*raw_event)(struct hid_device *hdev, struct hid_report *report, u8 *data, int size);
parameters
struct hid_device *hdev |
struct hid_report *report |
u8 *data |
int size |
expected return value (int)
-1 | error |
0 | continue automatic processing (no action performed) |
1 | no further (automatic) processing |
explanation
The raw_event hook is called whenever a HID report comes in. Every report the device can send (or receive) is described by the devices report descriptor.
Before the hook is called, a list named report_table is checked for the specific report, if it isn’t in it, the report is not passed to the raw_event hook. When report_table is not defined (NULL), then every report is passed to the hook.
This hook is typically used for reports which are not processed automatically or not correctly by the hid-core. A common example would be a battery status report, which are often not processed correctly.
event hook
int (*event)(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value);
parameters
struct hid_device *hdev |
struct hid_field *field |
struct hid_usage *usage |
__s32 value |
expected return value (int)
-1 | error |
0 | continue automatic processing (no action performed) |
1 | no further (automatic) processing |
explanation
The event hook is called for every event which was automatically processed from the report. That means, if a report comes in – hid-core will automatically process it and divide it into several events. When one of those events fits the descriptions in usage_table (or when usage_table is NULL), then the event_hook is called.
What that means is, that a report which contains more than one fields (like ten buttons and four axis), a event is created for every one – and not only for the one which changed.
event hook
int (*event)(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value);
report_fixup hook
__u8 *(*report_fixup)(struct hid_device *hdev, __u8 *buf, unsigned int *size);
parameters
struct hid_device *hdev |
__u8 *buf |
unsigned int *size |
expected return value
__u8* | Return the address of the report descriptor to parse (the old one or the new one) |
explanation
This hook is called before the HID device descriptor is parsed. You can use it in order to fixup a corrupted descriptor. The descriptor is given in the second argument (*buf), the size by the third one (*size).
The return value is a pointer to a new report, or it is just rdesc if you have changed values in place or leaved it untouched. That means that you can either replace values in the original report (in place), or return a complete new one which is then parsed instead of the original one.
For the latter, return the address of the replacement-descriptor and overwrite the size value like this:
static u8 replacement_rdesc[] = { 0x05, 0x01, 0x09, 0x04 //... }; static u8 *my_report_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *rsize) { *rsize = sizeof(replacement_rdesc); return replacement_rdesc; }
If you just want to replace some single values, try the following:
static u8 *my_report_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *rsize) { // Caution! // Make sure that the addresses you want to fix do exist rdesc[123] = 0xDE; rdesc[124] = 0xAD; return rdesc; }
In both cases you have to register the hook like this:
static struct hid_driver my_driver = { // ... .report_fixup = my_report_fixup, // ... };
input_mapping hook
int (*input_mapping)(struct hid_device *hdev, struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max);
parameters
struct hid_device *hdev |
struct hid_input *hidinput |
struct hid_field *field |
struct hid_usage *usage |
unsigned long **bit |
int *max |
expected return value (int)
negative | completely ignore this usage (e.g. double or invalid usage) |
0 | continue with parsing of this usage by generic code (no special handling needed) |
positive | skip generic parsing (needed special handling which was done in the hook already) |
explanation
This hook is invoked at input registering before mapping an usage, it is called once for every hid-usage. What does that mean?
Every HID usage is mapped to a special input event, this does normally happen automatically. Take for example the Usage ID 0x30 of the HID Usage Page GENERIC DESKTOP (described here) – it’s name is X and it is of type DV (dynamic value).
How is that dynamic X represented in Linux? By an axis! A fitting representation would be ABS_X of type EV_ABS. That kind of mapping is done automatically for most inputs, but if that automatic process fails – you can use this hook to map the HID usage to an input event of you choice!
static int my_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { unsigned int hid_usage_ = usage->hid & HID_USAGE; unsigned int hid_usage_page = usage->hid & HID_USAGE_PAGE; if(hid_usage_page == HID_UP_GENDESK && hid_usage_ == 0x30) { hid_map_usage_clear(hi, usage, bit, max, EV_ABS, ABS_X); return 1; } /* Something went wrong, ignore this field */ return -1; }
As always, don’t forget to register the hook in your driver
static struct hid_driver my_driver = { // ... .input_mapping = my_mapping, // ... };
input_mapped hook
int (*input_mapped)(struct hid_device *hdev, struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max);
input_configured hook
int (*input_configured)(struct hid_device *hdev, struct hid_input *hidinput);
feature_mapping hook
void (*feature_mapping)(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage);
suspend hook
int (*suspend)(struct hid_device *hdev, pm_message_t message);
resume hook
int (*resume)(struct hid_device *hdev);
reset_resume hook
int (*reset_resume)(struct hid_device *hdev);