Understand the USB device driver framework in one article

From: https://www.eet-china.com/mp/a55310.html

hello, everybody. Today I'm going to take you to learn about USB device driver.

Kernel version: 4.4.94

1. Linux USB subsystem

Before introducing device-side drivers, let's look at the Linux USB subsystem. The subsystem here is relative to the entire Linux kernel, not a single device. The communication framework between USB host and device is summarized as a whole.

Linux kernel s have already integrated a relatively complete USB protocol stack. Because of its large size, including multiple categories of device drivers, the USB protocol stack in Linux system is also known as the USB subsystem.

1.1 Host

Host side, simplify abstraction three layers:

  • Various types of device drivers: mass sotrage, CDC, HID, etc.
  • USB device driver: USB core processing
  • Host Controller Driver: Different USB host controllers (OHCI/EHCI/UHCI), abstracted as HDC.

1.2 Device Side

Device side, also abstracted as three layers:

  • Device Function Driver: mass sotage, CDC, HID, etc., corresponding to host class device driver
  • Gadget device driver: middle layer, direct downward communication with UDC, establish links; Provides a common interface up, shielding USB requests and transferring details.
  • Device Controller Driver: UDC Driver, which handles USB device controllers directly.

2. USB device driver

2.1 gadget drive frame disassembly 1

We will disassemble the USB device driver with the following driver frame:

As mentioned above, the Gadget device layer plays a crucial role. Provides a common driver framework for the upper layer and connects with the lower UDC through the Gadget Interface.

Compsite Framwork provides a generic usb_gadget_driver templates, including various methods, for use by the upper Function driver. (driver/usb/gadget/compsite.c)

From the figure above, we can see that for USB device-side driver development, more attention is paid to the Function driver layer. USB control related processes, the kernel provides a middle layer to help us block out.

2.2 gadget drive frame disassembly 2

Kernel version: Linux Kernel 4.4.94, this is the version we'll take apart

Kernel of 4.x relative to 3. The core of X is decomposed more perfectly on the gadget drive, showing a directory structure, clear hierarchy, reasonable division of labor, and easier to understand.

Relative to 3.x version, 4.4.94, this kernel, splits the original driver/usb/gadget directory. Common interfaces remain unchanged, such as compsite.c and functions.c. The usb function driver is subdivided into legacy and functions.

With these backgrounds in mind, let's look at the 4.4.94 kernel, the gadget driver framework, as shown in the figure below (drawn by the author as he sees it):

  • Legacy: Entry driven by the entire Gadget device. Located under driver/usb/gadget/legacy, driver sample s for commonly used USB-type devices are provided. Its function is to configure USB device descriptor information to provide a usb_composite_driver, and then register it with the composite layer.
  • Functions: various USB subclass device function drivers. In driver/usb/gadget/functions, the corresponding sample is also given. Its function is to configure the interface description of the USB subclass protocol and other subclass protocols, such as uvc protocol, hid, etc.
  • Note: For a compsite device with one or more functions, there are also multiple functions driver s

Did you see from this diagram that device-side driver development seems to be getting easier and easier? Yes, in fact, we just need to add the corresponding usb device descriptor information, along with several other configurations, based on legacy's source code.

In other words, we just need to be concerned about the legacy loss. The functions layer will be slightly adapted to business needs, but the overall change will be small.

The complexity of the usb driver framework makes it difficult for beginners to understand it because it combines drivers with complex protocols. In fact, legacy alone includes other drivers, such as the well-known v4l2 driver framework in webcam.

So when I'm learning the USB drive frame, I have to grasp the big and small, [master the main thread, ignore the details]. When we break down one complex drive at a time, we actually find that it is not so terrible.

2.3 usb compsite device building

For ease of understanding, let's take a brief look at the process of building a usb compsite device:

Suppose you build a usb composite device and need to support the three functions of uac, uac, hid. Its driving framework is as follows:

  • First, we need a driver entry legacy to configure device description information, supported protocols, etc.
  • Then add a configuration that supports multiple interfaces, where uvc uac hid is supported, one function driver per interface
  • Finally, we register it with the compsite layer
  • There is a usb function driver list for function driver, which is automatically added to a chain list when the function driver is registered with the kernel. functions.c is used to manage all function drivers

3. USB gadget driver profiling

3.1 Related Data Structure

Before we comb the whole framework, let's first comb through several important data structures, from bottom to top:

usb_udc:

udc, embedded usb_gadget_driver and usb_gadget

struct usb_udc {
 struct usb_gadget_driver *driver;
 struct usb_gadget  *gadget;
 struct device   dev;
 struct list_head  list;
 bool    vbus;
};

usb gadget:

usb underlying operations, including udc, endpoint requests, and so on.

struct usb_gadget {
 struct work_struct  work;         /* Work queue*/
 struct usb_udc   *udc;          /* udc */
 /* readonly to gadget driver */
 const struct usb_gadget_ops *ops; /*gadget Set of device operation functions*/
 struct usb_ep   *ep0;           /* Control endpoint, only responding to setup package*/
 struct list_head  ep_list;      /* Link all the endpoints of the device into a linked list, where ep0 is not.*/
 enum usb_device_speed  speed; /* High, Full and Low Speed*/
 enum usb_device_speed  max_speed; /* Maximum Speed*/
 enum usb_device_state  state;
 const char   *name;
 struct device   dev;
 unsigned   out_epnum;       /* out ep number */
 unsigned   in_epnum;         /* in ep number */
 struct usb_otg_caps  *otg_caps;

 unsigned   sg_supported:1;
 unsigned   is_otg:1;
 unsigned   is_a_peripheral:1;
 unsigned   b_hnp_enable:1;
 unsigned   a_hnp_support:1;
 unsigned   a_alt_hnp_support:1;
 unsigned   quirk_ep_out_aligned_size:1;
 unsigned   quirk_altset_not_supp:1;
 unsigned   quirk_stall_not_supp:1;
 unsigned   quirk_zlp_not_supp:1;
 unsigned   is_selfpowered:1;
 unsigned   deactivated:1;
 unsigned   connected:1;
};

usb_gadget_driver:

Usb_ Gadget_ Driver - driver for USB'slave'devices. USB drives the universal architecture from the device.

  • Role: Provide a generic usb gadget driver template, register it downward to udc, provide bind callback to functions driver, etc.

  • Attention: bind callback, function driver name, setup processing request

struct usb_gadget_driver {
 char   *function;    /* String describing the gadget's function */
 enum usb_device_speed max_speed; /* Highest speed the driver handles */
 int   (*bind)(struct usb_gadget *gadget, /* the driver's bind callback */
     struct usb_gadget_driver *driver);
 void   (*unbind)(struct usb_gadget *);
 int   (*setup)(struct usb_gadget *, /* Processing ep0 request*/
     const struct usb_ctrlrequest *);
 void   (*disconnect)(struct usb_gadget *); 
 void   (*suspend)(struct usb_gadget *);
 void   (*resume)(struct usb_gadget *);
 void   (*reset)(struct usb_gadget *);

 /* FIXME support safe rmmod */
 struct device_driver driver;
};

usb_composite_driver:

usb_composite_driver, an entry for device drivers, manages device configuration information and saves device descriptors. (

Important: Focus on the bind method.

struct usb_composite_driver {
 const char    *name; /* Driver name*/
 const struct usb_device_descriptor *dev ; /* Device Descriptor*/
 struct usb_gadget_strings  **strings;
 enum usb_device_speed   max_speed;
 unsigned  needs_serial:1;

 int   (*bind)(struct usb_composite_dev *cdev); /* bind Method*/
 int   (*unbind)(struct usb_composite_dev *);

 void   (*disconnect)(struct usb_composite_dev *);

 /* global suspend hooks */
 void   (*suspend)(struct usb_composite_dev *);
 void   (*resume)(struct usb_composite_dev *);
 struct usb_gadget_driver  gadget_driver;     /* usb gadget driver */
};

usb_composite_dev:

Embedded gadget objects, as well as some configuration and requests for usb devices, are primarily used for initialization.

struct usb_composite_dev {
 struct usb_gadget  *gadget;
 struct usb_request  *req;
 struct usb_request  *os_desc_req;

 struct usb_configuration *config; /* usb Configuration Information*/

 /* OS String is a custom (yet popular) extension to the USB standard. */
 u8    qw_sign[OS_STRING_QW_SIGN_LEN];
 u8    b_vendor_code;
 struct usb_configuration *os_desc_config;
 unsigned int   use_os_string:1;

 /* private: */
 /* internals */
 unsigned int   suspended:1;
 struct usb_device_descriptor desc; /* Device Descriptor*/
 struct list_head  configs;
 struct list_head  gstrings;
 struct usb_composite_driver *driver; /* composite driver */
 u8    next_string_id;
 char    *def_manufacturer;

 /* the gadget driver won't enable the data pullup
  * while the deactivation count is nonzero.
  */
 unsigned   deactivations;

 /* the composite driver won't complete the control transfer's
  * data/status stages till delayed_status is zero.
  */
 int    delayed_status;

 /* protects deactivations and delayed_status counts*/
 spinlock_t   lock;

 unsigned   setup_pending:1;
 unsigned   os_desc_pending:1;
};

3.2 Driver Profiles

As shown in the figure for a generic usb gadget driver profile, only two functions are listed in the diagram, and if more than one function can be added. Regarding the udc controller section, I don't continue to draw. Note that we always keep a principle, [grab big and small] and grasp the important thread.

For usb device-side drivers, more complex pictures contain more content. If you don't feel clear, reply back to the gadget driver to get a HD image

Hierarchical Blocking

Ideas that are stratified up and down and separated left from right.

  • Device Function Driver
    • legacy driven entry
    • functions Driven Implementation
  • Gadget device layer: most important is compsite_ The bind method, which serves as a starting point.
  • udc device controller layer. The real processing of the usb protocol.

Drive Trend

  • Down: usb_ Composite_ Driver -> usb_ Gadget_ Driver->usb_ UDC
  • Callback up: udc_ Bind_ To_ Driver -> composite_ Bind -> webcam_ The two main structures of bind are usb_gadget_driver and usb_compsite_dev. The former is registered downwards in the udc list to establish a binding relationship with the UDC controller. The latter provides an interface up to the various functions and other configuration information for the upper layer to configure the USB device.

code analysis

  1. Register usb_composite_driver
module_usb_composite_driver(webcam_driver)
     module_driver(webcam_driver, usb_composite_probe, \
         usb_composite_unregister)
  1. usb_composite_probe
usb_composite_probe(webcam_driver);
        driver->gadget_driver = composite_driver_template;
        gadget_driver = &driver->gadget_driver;
        ...
        usb_gadget_probe_driver(composite_driver_template);
                udc_bind_to_driver(udc, driver);
                        composite_driver_template->bind(udc->gadget, composite_driver_template);
                        usb_gadget_udc_start(udc);
  1. composite_bind
composite_bind(udc->gadget,composite_driver_template);
        cdev->gadget = gadget;
        composite_dev_prepare(webcam_driver,cdev);
                cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); /* Application endpoint 0*/
                cdev->req->complete = composite_setup_complete;
                cdev->driver = webcam_driver;
                usb_ep_autoconfig_reset(gadget);
        webcam_driver->bind(cdev);
  1. webcam_bind
webcam_bind(cdev);
        usb_get_function_instance("uvc");
                try_get_usb_function_instance("uvc");
                        uvc_alloc_inst();
        usb_add_config();
                webcam_config_bind();
                        usb_get_function();
                        usb_add_function();
                others_config_bind();

Other

Function driver is not detailed here. This block diagram is only a general usb device driver framework diagram. For the specific usb function driver, we do not do a specific analysis here.

With f_uvc simple example, detailed process see the kernel source.

DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);

DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
        usb_function_register(&uvcusb_func);
                list_for_each_entry(fd, &func_list, list)
                list_add_tail();

DECLARE_USB_FUNCTION_INIT 

A generic driver template for registering usb_function_driver, and add to func_ On the list.

#define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc)  \
 static struct usb_function_driver _name ## usb_func = {  \
  .name = __stringify(_name),    \
  .mod  = THIS_MODULE,     \
  .alloc_inst = _inst_alloc,    \
  .alloc_func = _func_alloc,    \
 };        \
 MODULE_ALIAS("usbfunc:"__stringify(_name));

#define DECLARE_USB_FUNCTION_INIT(_name, _inst_alloc, _func_alloc) \
 DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc)  \
 static int __init _name ## mod_init(void)   \
 {        \
  return usb_function_register(&_name ## usb_func); \
 }        \
 static void __exit _name ## mod_exit(void)   \
 {        \
  usb_function_unregister(&_name ## usb_func);  \
 }        \
 module_init(_name ## mod_init);     \
 module_exit(_name ## mod_exit)

4. Summary

In this paper, the usb device-side driver framework is gradually stripped off in a dismantled way, leading you to re-understand the usb device-side driver. At the same time, a general driver framework model of the compsite device is given, and the entire driver process is analyzed from the source code level.

For USB or other similar advanced drivers, the author has a suggestion that at the beginning of school, it is more important to [grasp the primary and secondary, ignore the details].

For example, a composite usb device may contain, uvc,uac,hid, and so on. Videos are driven by uvc function and v4l2. UAC also has corresponding drivers. Derivative expansion can be very complex.

So when we master the device-side driver framework and process, we need to add other usb function drivers later to study their protocols or drivers, as well as derivative drivers.

------------ END ------------

 

Background reply "USB" Linux Read more articles.

Added by mooler on Thu, 10 Mar 2022 20:14:38 +0200