I recently studied the Bluetooth keyboard and mouse, so I took a rough look at the Linux HID framework.
HID Bus
The HID bus is initialized in the hid-init of the hid-core.c:
Bus_register (& hid_bus_type );
Definition of hid_bus_type:
Static struct bus_type hid_bus_type = {
. Name = "hid ",
. Match = hid_bus_match,
. Probe = hid_device_probe,
. Remove = hid_device_remove,
. Uevent = hid_uevent,
};
Generally, HID drivers seldom define their own probe functions, so the matching of HID devices is basically done by bus probe and match functions.
HID matching
Hid_bus_match is used to check whether the VID and PID of the device and driver match. The Code is as follows:
Static int hid_bus_match (struct device * Dev, struct device_driver * DRV)
Struct hid_driver * hdrv = container_of (DRV, struct hid_driver, driver );
Struct hid_device * hdev = container_of (Dev, struct hid_device, Dev );
// Match the vendorid and productid of hdev and hdrv.
If (! Hid_match_device (hdev, hdrv ))
Return 0;
// If the driver starts with generic-, it can be matched as long as it is not in the blacklist.
If (! Strncmp (hdrv-> name, "generic-", 8 ))
Return! Hid_match_id (hdev, hid_blacklist );
Return 1;
After the PID and VID are matched, go to the hid_device_probe function:
Static int hid_device_probe (struct device * dev)
Struct hid_driver * hdrv = container_of (dev-> driver, struct hid_driver, driver );
Struct hid_device * hdev = container_of (dev, struct hid_device, dev );
Const struct hid_device_id * id;
Int ret = 0;
If (! Hdev-> driver ){
// Match again. It seems to be a bit different from the previous hid_bus_match
Id = hid_match_device (hdev, hdrv );
If (id = NULL)
Return-ENODEV;
Hdev-> driver = hdrv;
If (hdrv-> probe) {// if the driver defines its own probe function, the probe is called, but the general HID driver does not
Ret = hdrv-> probe (hdev, id );
} Else {// default probe Process
Ret = hid_parse (hdev );
If (! Ret)
Ret = hid_hw_start (hdev, HID_CONNECT_DEFAULT );
}
If (ret)
Hdev-> driver = NULL;
}
Return ret;
The role of the hid_parse function is to parse the HID descriptor. The specific implementation is completed by the hid_device-> ll_driver-> parse function. The document on the HID descriptor can be downloaded at www.usb.org.
Static inline int _ must_check hid_parse (struct hid_device * hdev)
Ret = hdev-> ll_driver-> parse (hdev );
Because the parsing of the HID descriptor is a common operation, a parsing function hid_parse_report is implemented in the HID framework. Generally, you only need to call the hid_parse_report function in the hdev-> ll_driver-> parse function.
Hid_parse_report is complex. Its function is to parse the HID Descriptor and put the parsed result in hid_device-> report_enum [type]-> report_list. Each parsed HID structure is described by a hid_report. The type in report_enum can be HID_INPUT_REPORT, HID_OUTPUT_REPORT, or HID_FEATURE_REPORT.
After parse, the probe function will call hid_hw_start again to start the HID device:
Hid_hw_start (hdev, HID_CONNECT_DEFAULT );
Note that HID_CONNECT_DEFAULT is defined:
# Define HID_CONNECT_DEFAULT (HID_CONNECT_HIDINPUT | HID_CONNECT_HIDRAW | \
HID_CONNECT_HIDDEV | HID_CONNECT_FF)
In hid_hw_start, the system first calls hdev-> ll_driver-> start to start the device, and then hid_connect associates the device with the HID framework.
The hdev-> ll_driver-> start function is provided by a specific hid device and is provided by the bus to which the device belongs. It is used for underlying initialization.
Hid_connect associates hid_dev with a specific driver.
Int hid_connect (struct hid_device * hdev, unsigned int connect_mask)
If (hdev-> quirks & HID_QUIRK_HIDDEV_FORCE) // this is generally not the case.
Connect_mask | = (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV );
If (hdev-> bus! = BUS_USB) // if it is not a USB bus, remove the HID_CONNECT_HIDDEV mark.
Connect_mask & = ~ Hid_connect_hiddev;
If (hid_hiddev (hdev) // match certain vendorid and productid
Connect_mask | = hid_connect_hiddev_force;
If (connect_mask & hid_connect_hidinput )&&! Hidinput_connect (hdev,
Connect_mask & hid_connect_hidinput_force ))
Hdev-> claimed | = hid_claimed_input;
If (connect_mask & HID_CONNECT_HIDDEV) & hdev-> hiddev_connect &&
! Hdev-> hiddev_connect (hdev,
Connect_mask & HID_CONNECT_HIDDEV_FORCE ))
Hdev-> claimed | = HID_CLAIMED_HIDDEV;
If (connect_mask & HID_CONNECT_HIDRAW )&&! Hidraw_connect (hdev ))
Hdev-> claimed | = HID_CLAIMED_HIDRAW;
It can be seen that hid_connect supports three types of devices. The first is the input device, which calls hidinput_connect registration; the second is the hid_dev device, which calls hdev-> hiddev_connect registration; the last is the raw device, which calls hidraw_connect registration.
HID input
The most common component of HID is the input device, which uses hidinput_connect to register with the system. The main function of hidinput_connect is to create an input_dev device for every report in hiddev and register it in the input framework.
Int hidinput_connect (struct hid_device * hid, unsigned int force)
// Create an input device for each report
For (k = HID_INPUT_REPORT; k <= max_report_type; k ++)
List_for_each_entry (report, & hid-> report_enum [k]. report_list, list ){
If (! Hidinput ){
Hidinput = kzarloc (sizeof (* hidinput), GFP_KERNEL );
Input_dev = input_allocate_device ();
...
Input_set_drvdata (input_dev, hid );
Input_dev-> event = hid-> ll_driver-> hidinput_input_event;
Input_dev-> open = hidinput_open;
Input_dev-> close = hidinput_close;
Input_dev-> setkeycode = hidinput_setkeycode;
Input_dev-> getkeycode = hidinput_getkeycode;
Input_dev-> name = hid-> name;
Input_dev-> phys = hid-> phys;
Input_dev-> uniq = hid-> uniq;
Input_dev-> id. bustype = hid-> bus;
Input_dev-> id. vendor = hid-> vendor;
Input_dev-> id. product = hid-> product;
Input_dev-> id. version = hid-> version;
Input_dev-> dev. parent = hid-> dev. parent;
Hidinput-> input = input_dev;
List_add_tail (& hidinput-> list, & hid-> inputs );
}
For (I = 0; I <report-> maxfield; I ++)
For (j = 0; j <report-> field [I]-> maxusage; j ++)
Hidinput_configure_usage (hidinput, report-> field [I],
Report-> field [I]-> usage + j );
}
HID dev
The HID dev device is currently only used in the USB bus. Its hiddev_connect function pointer for registration currently only has one instance, which is assigned a value in the usbhid_probe function.
Hid-> hiddev_connect = hiddev_connect;
HID raw dev
Hidraw. c defines a class hidraw and creates a device driver.
Alloc_chrdev_region (& dev_id, HIDRAW_FIRST_MINOR, HIDRAW_MAX_DEVICES, "hidraw ");
Cdev_init (& hidraw_cdev, & hidraw_ops );
A basic character device driver is defined in hidraw_ops.
Static const struct file_operations hidraw_ops = {
. Owner = THIS_MODULE,
. Read = hidraw_read,
. Write = hidraw_write,
. Poll = hidraw_poll,
. Open = hidraw_open,
. Release = hidraw_release,
. Unlocked_ioctl = hidraw_ioctl,
};
Because it is a raw device, no data will be parsed in this driver. It simply transmits the application layer data to the lower-layer device and the data generated by the device to the application layer. You can view the code for specific implementation.
Data Transmission
In HID, data is transmitted from the application layer to the device, and from the device to the application layer.
For HID input devices, the standard input framework is used from the device to the application layer. The underlying device sends the received data to the HID framework through the hid_input_report function, the HID framework parses and finally calls functions such as input_report_key to upload data.
From the application layer to the device is also completed by the input framework:
Hidinput_connect
Input_dev-> event = hid-> ll_driver-> hidinput_input_event;