Linux HID drivers and how they are called

Linux HID drivers and how they are called

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 as well as the whole loading process of driver modules in Linux.

Table of contents

What’s about hid-generic?

TODO

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

parameters

struct hid_device *hdev
struct hid_report *report
u8 *data
int size

expected return value (int)

-1error
0continue automatic processing (no action performed)
1no 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

parameters

struct hid_device *hdev
struct hid_field *field
struct hid_usage *usage
__s32 value

expected return value (int)

-1error
0continue automatic processing (no action performed)
1no 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.

report_fixup hook

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

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)

negativecompletely ignore this usage (e.g. double or invalid usage)
0continue with parsing of this usage by generic code (no special handling needed)
positiveskip 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

parameters

expected return values

explanation

input_configured hook

parameters

expected return value

explanation

References

https://stackoverflow.com/questions/54056731/where-does-the-systemd-builtin-kmod-gets-the-module-aliases-from/54056886#54056886