1. Overview
Take the USB keyboard driver in the Linux5.10 kernel as an example to analyze: https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.10.tar.gz
file path:linux-5.10/drivers/hid/usbhid/usbkbd.c
Two, explore
usb_kbd_event
- The main logic of the light is in the function
usb_kbd_event
, whichusb_kbd_event
is assigned toinput_dev->event
input_dev->event = usb_kbd_event;
the usbkbd module, which provides a method of controlling the on and off, but the specific decision of which light is on or which light is off lies in the input module. - Let's analyze in detail how the usbkbd module controls the light. Its core code is as follows:
kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
(!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
(!!test_bit(LED_NUML, dev->led));
-
The implementation here
test_bit
is as follows, and its function is to obtain the value of the nrth bit of the addr memory.BITS_PER_LONG
Indicates the number of bits occupied by a long type. It is 64 on 64-bit systemsBITS_PER_LONG
and 32 on 32-bit systems.BIT_WORD(nr)
Divide by nrBITS_PER_LONG
to get the subscript of the element where the nr bit is located.BITS_PER_LONG-1
It means that in aunsigned long
type of variable, you can only shift the leftmost bit to the rightmost at most, that is to say, you canBITS_PER_LONG-1
shift(nr & (BITS_PER_LONG-1))
This is actually to calculate the number of bits that nr needs to shift right in the element it sits in .- So on the whole, it is to
addr[BIT_WORD(nr)]
find the element where the nrth bit is located, and(nr & (BITS_PER_LONG-1))
calculate the right shift number of the nrth bit in the element.(addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))
Move the nrth bit to the rightmost.1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)))
After the shift is completed, perform an AND operation on the bit and 1 to determine whether it is 1.
#ifdef CONFIG_64BIT #define BITS_PER_LONG 64 #else #define BITS_PER_LONG 32 #endif /* CONFIG_64BIT */ #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) /** * _test_bit - Determine whether a bit is set * @nr: bit number to test * @addr: Address to start counting from */ static __always_inline bool _test_bit(unsigned long nr, const volatile unsigned long *addr) { return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); }
-
Clear
test_bit
, let's take a look at the specific business logic. This time we only focus onLED_NUML
,LED_CAPSL
andLED_SCROLLL
. The meaning of the following piece of code is very clear. The value is taken out from the , , and bit ofdev->led
this memory and placed in the 2nd, 1, and 0th bits of the new variable. That is to say, if the second bit of the variable is 1, it means that the light of the keyboard is turned on, and if it is 0, it is turned off, etc., while the first bit means , and the 0th bit means .LED_SCROLLL
LED_CAPSL
LED_NUML
kbd->newleds
kbd->newleds
Scroll Lock
Scroll Lock
Caps Lock
Num Lock
(!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | (!!test_bit(LED_NUML, dev->led))
-
Then by
usb_submit_urb(kbd->led, GFP_ATOMIC)
sending the new led value to the device. -
We know that USB has four transmission methods, control transmission (control), batch transmission (bulk), interrupt transmission (interrupt) and isochronous transmission (isochronous). We just changed the value of the leds here, and then submitted the urb, so what transmission method do we use here?
usb_fill_control_urb
-
Back to our
usb_kbd_probe
function, this function will be called when the usbkbd driver is loaded; and our previousinput_dev->event = usb_kbd_event;
operations are also completed in this function. -
The following is the core code related to the lamp. The lights are controlled using the USB control transfer. The more important thing to control transmission is its setup package, whose structure is as follows:
-
bRequestType
Field, our request to control the light is not a standard USB request defined in the USB protocol, but a request defined by the input device class, so it isUSB_TYPE_CLASS
. After our request is passed, it is received by the control interface, so the receiver isUSB_RECIP_INTERFACE
. The transmission direction is that the command sent by the host to the deviceHost-to-device
is zero, so there is no need to set it specially, the default is fine. -
bRequest
field, because ours is a unique request type for input devices. So you need to check the usbhid protocol , the protocol means to control the use of LED lightsSet_Report(Output)
. The correspondingSET_REPORT
value is 0x09, so what we have herebRequest
is 0x09 to indicate that this is aSET_REPORT
request.
-
Set_Report(Output)
The request is defined as follows, itswValue
field containsReport Type
andReport ID
,Report Type
in the high byte, the control command of our LED light is from host to device, which is out, soReport Type
the value is 0x02, we don’t use itReport ID
, so its value is zero. So the value of ourwValue
field is0x0200
:
-
wIndex
Fill in the interface field, and directly fill inbInterfaceNumber
the field of the control interface. -
wLength
Indicates the size of the data to be transmitted. We transmit a uint8_t type representing the state of the light, so its size is 1. -
usb_fill_control_urb
It is to fill in the data we filled in above into the urb, that is,kbd->led
inside.
kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
kbd->cr->bRequest = 0x09;
kbd->cr->wValue = cpu_to_le16(0x200);
kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
kbd->cr->wLength = cpu_to_le16(1);
usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
(void *) kbd->cr, kbd->leds, 1,
usb_kbd_led, kbd);
3. Summary
usb_fill_control_urb
Prepare the urb in advance, and then directlyusb_kbd_event
modify the leds data when needed, and then send the urb.