【usb】linux kernel USB keyboard driver analysis--LED light control

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, which usb_kbd_eventis assigned to input_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_bitis as follows, and its function is to obtain the value of the nrth bit of the addr memory.

    • BITS_PER_LONGIndicates the number of bits occupied by a long type. It is 64 on 64-bit systems BITS_PER_LONGand 32 on 32-bit systems. BIT_WORD(nr) Divide by nr BITS_PER_LONGto get the subscript of the element where the nr bit is located.
    • BITS_PER_LONG-1It means that in a unsigned longtype of variable, you can only shift the leftmost bit to the rightmost at most, that is to say, you can BITS_PER_LONG-1shift (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 on LED_NUML, LED_CAPSLand LED_SCROLLL. The meaning of the following piece of code is very clear. The value is taken out from the , , and bit of dev->ledthis 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_SCROLLLLED_CAPSLLED_NUMLkbd->newledskbd->newledsScroll LockScroll LockCaps LockNum 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_probefunction, this function will be called when the usbkbd driver is loaded; and our previous input_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:Please add a picture description

  • bRequestTypeField, 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 is USB_TYPE_CLASS. After our request is passed, it is received by the control interface, so the receiver is USB_RECIP_INTERFACE. The transmission direction is that the command sent by the host to the device Host-to-deviceis zero, so there is no need to set it specially, the default is fine.

  • bRequestfield, 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 lights Set_Report(Output). The corresponding SET_REPORTvalue is 0x09, so what we have here bRequestis 0x09 to indicate that this is a SET_REPORTrequest.
    Please add a picture description
    insert image description here

  • Set_Report(Output) The request is defined as follows, its wValuefield contains Report Typeand Report ID, Report Typein the high byte, the control command of our LED light is from host to device, which is out, so Report Typethe value is 0x02, we don’t use it Report ID, so its value is zero. So the value of our wValuefield is 0x0200:
    Please add a picture description

    Please add a picture description

  • wIndexFill in the interface field, and directly fill in bInterfaceNumberthe field of the control interface.

  • wLengthIndicates 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_urbIt is to fill in the data we filled in above into the urb, that is, kbd->ledinside.

	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_urbPrepare the urb in advance, and then directly usb_kbd_eventmodify the leds data when needed, and then send the urb.

4. References

USB HID protocolUSB
2.0 Specification

Guess you like

Origin blog.csdn.net/C2681595858/article/details/129900209
usb