Summary of USB basic knowledge

1.Introduction to USB basic concepts

USB (Universal Serial Bus) is a new PC serial communication protocol jointly initiated by Intel, Microsoft and other companies in 1995. It is based on universal connection technology to realize simple and fast connection of peripherals, achieving the purpose of user convenience, reducing costs and expanding the scope of PC connection peripherals. Its biggest feature is that it supports hot-swappable and plug-and-play. Up to 127 peripherals can be connected in series. It can provide 5-volt power supply to low-voltage devices and reduce the number of PC I/O interfaces. Before the emergence of USB, there were too many and complicated interfaces in the computer field. After the emergence of USB, the types of interfaces were reduced. In general, a universal interface was designed, and various peripherals can use the same interface, so it is the first In the name of "Universal".

1.1 USB common terms

Before proceeding with USB system development, it is necessary to understand some common terms that may be encountered in USB development. USB system development is divided into USB host development and USB device development. In a USB system, there is only one USB host at a certain time, and the rest are USB devices. However, in order to make a USB system have both USB host function and USB slave function, USB OTG appeared. Therefore, USB development mainly includes USB host, USB device, and USB OTG system development. Here are some commonly used books:

USB Host : There is only one USB host in any USB system. The host is the device that plays the role of the master device in the USB bus and is responsible for managing data transmission and port management in the USB bus. The interface between USB and the host system is called the host controller. For example, when a U disk (USB mass storage device) communicates with a PC, the PC here is the USB Host.

USB Device : The downstream device of the USB host, which acts as a slave device in the USB bus and provides specific functions for the system. The USB host can support up to 127 USB devices.

USB OTG : OTG means On The Go, which means ongoing.

USB device : USB device is divided into two parts according to function: hub and functional components.

Logical device : A logical device is a combination of a series of endpoints. Communication between a logical device and the host occurs between a host buffer and an endpoint of the device.

USB Hub : USB Hub can convert one USB port into multiple, expanding the number of devices that the USB host can connect to. The USB Host has a Root Hub. The first USB device is a root hub (Root_hub) which controls The entire USB bus connected to it. This controller is a bridge connecting the PCI bus and the USB bus. It is also the first USB device on the bus. The USB Hub is a USB Device for the upstream and acts as a USB for the downstream. Host, so the USB device itself does not know whether it is connected to the Hub or the Root Hub.

PIPE : The most basic form of USB communication is through the endpoint in the USB device, and the data transmission between the host and the endpoint is through pipe.

Endpoint : The destination or source of communication between a host and a device. The endpoints are directional, the host to the slave becomes the out endpoint, and the slave to the host becomes the in endpoint. Control endpoints can transmit data in both directions, while other endpoints can only transmit data in one direction. The communication between the host and the device ultimately acts on each endpoint of the device and is a logical terminal of the communication flow between the host and the device. Each USB device has a unique address, assigned by the host, and each endpoint in the device is internal to the device. Have a unique endpoint number. This endpoint number is given when designing the device. Each device must have endpoint 0, which is used for device enumeration and some basic control functions of the device. Except for endpoint 0, the remaining endpoints cannot communicate with the host before the device is configured. Only the characteristics of these endpoints are reported to the host and confirmed. The endpoint is located inside the USB system and is an addressable FIFO space, similar to the entrance or exit of a highway toll gate. One endpoint corresponds to one direction.

Pipe communication method : There are two data communication methods in pipe, one is stream and the other is message. Message requires that the incoming and outgoing directions must require the same pipe. By default, ep0 is used as the message pipe.

Transmission method : There are four types of USB endpoints, which correspond to different data transmission methods, namely control transfers, interrupt transfers, Bluk Data transfers, and Isochronous Data Transfers. Control transfers are usually used for configuration. Device, obtain device information, and send commands to the device.

Interface : A logical device may contain several interfaces, each interface containing 1 or more endpoints. Each interface represents a function. One interface corresponds to one driver. For example, a USB speaker contains a keyboard interface and an audio streaming interface.

Class protocol : In addition to defining some general software and hardware electrical characteristics, the USB protocol also contains a variety of class protocols, which are used to define respective standard interfaces for different functions and the data interaction content and format on the specific bus. For example, the Mass storage class of USB flash disk and the CDC class of common data exchange.

1.2 USB protocol version

  • USB 1.0 (low speed), maximum transfer rate is 1.5Mbps
  • USB 1.1 (full speed), maximum transfer rate is 12Mbps
  • USB 2.0 (high speed), maximum transfer rate 480Mbps
  • USB 3.0 (super speed), maximum transfer rate 5Gbps

Insert image description here

1.3 USB electrical characteristics

The USB connector contains four lines: 2 for power supply (VBUS and GND), 2 for USB data transmission D+ (USB Data Positive signal line, USB-DP line, abbreviated as D+) and D - (USB data negative signal line, USB Data Minus, USB-DM line, abbreviated as D-). VBUS provides 5V power supply with current up to 500mA. D+ and D- are bidirectional signal lines with a signal transmission rate of 12Mbps (83ns per bit). D+ and D- signal level is 3.3V.
Insert image description here
There are 5 lines in the USB OTG interface: 2 are used to transmit data (D+, D-); 1 is the power line (VBUS); 1 is the ground line (GND), and 1 is the ID line.
USB signals are transmitted using twisted pairs labeled D+ and D-, which each use half-duplex differential signaling and work together to cancel electromagnetic interference from long wires.

1.4 USB hardware interface

Insert image description here
Insert image description here

1.5 Characteristics of USB

  • Can be hot-swapped and plug-and-play.
  • Easy to carry. Most USB devices are known for being "small, light, and thin", which is very convenient for users to carry large amounts of data with them. Of course, a USB hard drive is the first choice.
  • Standards are unified. The most common ones are hard drives with IDE interfaces, serial mouse and keyboard, and parallel port printers and scanners. But with USB, these application peripherals can all be connected to personal computers using the same standards. Then there are USB hard drives, USB Mouse, USB printer and more.
  • Multiple devices can be connected. USB often has multiple interfaces on personal computers, which can connect several devices at the same time. If you connect a USB HUB with four ports, you can connect them again; four USB devices, and so on, you can connect them as many times as you want. , connect all your home devices to a PC at the same time without any problems (note: can connect to up to 127 devices).

1.6 USB topology

The bus structure of USB adopts a tiered star topology structure, as shown in the figure below. The center of each star is a hub, and each device can be connected through the interface on the hub. As can be seen from the figure, USB devices include two types: USB hubs and USB devices. At the top is the Host (host). The connection from the Host is connected down to the Hub (Hub), and then the Hub is extended down one level or one step in a ladder-like manner, and is connected to the device on the next level or another hub. In fact, a hub can also be considered a device. The maximum number of layers is 6 ((a total of 7 layers including the last level of equipment)). The number of external connections per star can vary, with hubs generally having 2, 4 or 7 interfaces.
The USB topology system consists of three elements: host (Root Hub and USB host controller are bound together), Hub and device. In USB on the PC platform, the PC is the host and root Hub, and users can connect devices and lower-level Hubs to it. These additional Hubs can connect to lower-level Hubs and devices, thus forming a star structure.
Insert image description here
Insert image description here
The Hub in the picture is a special type of USB device, which is a set of USB connection points. There is an embedded Hub in the host called the root Hub. The host provides several connection points through the root hub. In order to prevent ring connections, star connections are used to reflect hierarchy.
In the USB architecture, the hub is responsible for detecting the connection and disconnection of the device and reporting to the host (Host) using its Interrupt IN Endpoint. When the system starts, the host polls the status of its root hub (Root Hub) to see if there are devices (including sub-hubs and devices on sub-hubs) connected.

1.7 Overall structure of USB bus

The entire USB bus can be described in three parts: USB connection, USB device, and USB host.
Insert image description here
USB Host:
There is only one host in the USB bus. The interface part between the USB bus and the computer host system is the host controller, which can be regarded as a combination of hardware, firmware and software. A root hub is integrated into the host system to provide one or more connection points.
USB devices:
First of all, USB devices can be divided into two major categories: hub category (devices that provide additional USB access points) and functional device categories (devices that implement certain functions for the system, such as ISDN adapters, digital joysticks, etc.).
According to functions, USB devices can be divided into many categories, such as: audio, human-computer interaction, display, communication, power supply, printer, mass storage, physical feedback and other devices. Each USB device must provide self-identification information and common settings. 
USB devices have a standard USB interface. Its role is to: interpret the USB protocol; respond to standard USB operations, such as suspend and setup, etc.; provide device Some descriptive information.
In actual design applications, the interface of USB devices has its own characteristics. The correct design of the USB interface is closely related to the performance of the device. Before designing the USB interface, a detailed analysis of the device's functions and indicators must be performed.
Devices connected to the USB interface share the entire USB bandwidth through a token-based and host-controlled protocol. USB allows a device to connect, set up, function, and disconnect as long as other devices are functioning normally.
Insert image description here
USB connection:
USB connection refers to the communication methods and methods between USB host and USB device, including: bus topology (connection method between USB host and device); intra-layer relationship (tasks in each layer of USB bus); data flow Mode (how data flows on the USB bus); USB schedule (USB provides a shared interconnect that obeys schedules)
The USB device is connected to the USB host through the USB bus. The physical connection on the USB bus is a hierarchical star topology. At the center of each star topology is the hub (USB hub). There is a point-to-point connection between the host and a hub or application and between the hub and other hubs or applications.

1.8 USB transmission method

There are four types of USB endpoints, which correspond to different data transmission methods, namely control transfers, interrupt transfers, Bluk Data transfers, and Isochronous Data Transfers.

  • Control transmission: Control transmission is a two-way transmission. The amount of data is usually relatively small. It is usually used to configure the device, obtain device information, and send commands to the device.
  • Batch transmission: Mainly used for reliable transmission when large amounts of data are transmitted without bandwidth and interval requirements. For example: copy data to U disk.
  • Interrupt transmission: Interrupt transmission is mainly used to regularly query whether the device has interrupt data to be transmitted. The structure of the device's endpoint moder determines its query frequency, from 1 to 255ms. This transmission method is typically used in the transmission of small amounts of scattered, unpredictable data. For example, keyboards and mice fall into this category. Interrupt transmission is one-way and only input to the host.
  • Isochronous transmission: Isochronous transmission provides a certain bandwidth and interval. It is used for streaming data transmission with strict time and strong fault tolerance, or for real-time applications that require a constant data transmission rate. For example: USB camera.

1.9 USB data transmission method

The USB bus is a serial bus (same as a serial port), and data is transmitted bit by bit on the data line. LSB comes first, the lowest bit is sent first, followed by the next lowest bit, and finally the highest bit (MSB).

1.10 USB device development process

  1. First determine the development type of the USB system, whether it is USB host, USB slave or USB OTG.
  2. If it is determined to be a USB device, the device type must be determined: HID, UDIO, CDC, HUB, IMAGE, etc.
  3. Find the relevant device manual to determine its descriptor.
  4. After completing the descriptor, write a USB enumeration program and observe whether the enumeration is successful. If the enumeration is successful, most of the development of this device has been completed.
  5. Write the application program. After the enumeration is successful, the main task is to process the data and write the application program.
    Insert image description here

2. How to distinguish USB2.0 and USB3.0

Whether the USB interface on the computer is 3.0 or 2.0 can be distinguished by three methods: color identification, contact plate identification, and logo identification.
1. The appearance difference between USB3.0 and USB2.0. Observe the USB (itself) socket and the USB socket on the computer. The color of the plastic piece in the middle is: USB3.0 - blue; USB2.0 - black or white.
Insert image description here
2. You cannot distinguish them by color, you can also look at the number of interface pins. Compared with USB2.0, USB3.0 has several more pins. On the Type-A interface, there are 5 more pins inside the interface, and the Type-B interface has an extra pin above the interface.
Insert image description here
Insert image description here
USB3.0 uses a two-row design with a total of 9 pins.
Insert image description here
3. Identification method: distinguish according to the symbol next to the socket, as shown below. The "SS" of USB3.0 stands for "SuperSpeed".
Insert image description here

3. Introduction to USB2.0 communication protocol (packet-transaction-transmission)

3.1 Package

Packet is the basic unit of USB transmission. The USB protocol specifies three types of packets: token (Token) packet, data (DATAx) packet, and handshake (Ack) packet. Among them, the token packet can only be sent from the host. Data packets and handshake packets can be sent by the host or the device.
Packets are composed of different fields. All packets start with the synchronization field SYNC and end with the end of packet signal EOP. The structure of the packet is as follows:
Insert image description here
different types of packets have different component bit fields, mainly including the following fields:
(1) Packet Identifier (PID)
The packet identifier mainly indicates what type of packet the current packet belongs to. The packet identifier has a total of 8 bits, of which the USB protocol uses only 4 bits [3:0], and the other 4 bits [7:4] are the inversion of the corresponding previous 4 bits. The PID bit fields and descriptions of various types of packets are as follows:
Insert image description here
Description:
4. USB is a serial bus (transmission using serial mode), data is transmitted on the bus one by one, and LSB is used The first transmission method means that the lowest bit is transmitted first, then the next lowest bit, and finally the highest bit. Taking the PID that transmits the SETUP packet as an example, the data on the bus should be: 1011 0010, so the data captured using the protocol analyzer is 0xB4.
5. Listed in the table are common data packages, as well as some special-purpose packages, without much study. And with the USB protocol update, there may be some new package types. Please refer to the official documentation for specific conditions.
( 2) The packet destination address (ADDR)
is a non-0 address assigned by the host to the device. The packets sent by the USB host will be broadcast on the bus, and all devices connected to the bus will filter the token packets and the data packets following the token packets according to their own device addresses. The packet destination address has only 7 bits (a total of 2 to the 7th power = 128 addresses), that is, the address range is: 0 127. The 0 address is the default address of the device before the host assigns an address to the device. The device does not count the 0 address except the 0 address. The mailing address is 1127. In other words, a USB host can only manage up to 127 USB devices (theoretically, a Host can connect to up to 127 Devices, and if one more is connected, the addresses will overlap).
(3) Package target endpoint (ENDP)
Data is sent between the USB device and the USB host through the endpoint. Therefore, each data packet transmitted on the bus must specify the target endpoint to which the data packet is sent (which endpoint of which device the data packet is to be sent to).
Insert image description here
(4) Data field
Data length range: 0~1024 bytes. Different transmission types have different data field lengths in different modes. This value is related to the transmission type and the maximum packet length and data object supported by the endpoint (for example, the host requests to obtain different device descriptors, and the specified length is different).
(5) Cyclic redundancy check code (CRC)
The USB protocol stipulates that only token packets and data packets have cyclic redundancy check codes. The token packet uses a 5-bit CRC5, and the data packet uses a 16-bit CRC16 check code. test.

3.1.1 Token Package

The token packet is the first data packet indicating that a transfer is about to begin. There are 4 types of token packages: SETUP token package, IN token package, OUT token package, and SOF token package. The structures of SETUP, IN, and OUT token packets are the same, with a total of 6 fields. The format is as follows:
Insert image description here
SETUP token packet
Insert image description here
IN token packet
Insert image description here
OUT token packet
Insert image description here
SOF token packet
Frame start packet (Start Of Frame), in In the USB topology, the host broadcasts SOF packets to the bus at regular intervals, and all full-speed devices and high-speed devices connected to the bus can receive SOF packets.
Compared with the other three token packages, the SOF token package structure is a bit special, with only 5 fields.
Insert image description here
illustrate:

  1. The device enumeration phase uses control transmission. The establishment transaction (establishment process) of the control transmission uses the SETUP token packet (only the control transmission will have the SETUP token packet). The endpoint 0 of the device is specifically used for control transmission. , so the packet target endpoint ENDP is 0, and the packet target address ADDR can be 0 or a non-zero value [1~127].
  2. When the device uses address 0, the CRC5 value is 0x08; after the address setting stage, the CRC5 value is calculated based on the specific address value, and the CRC5 calculation is automatically completed by the USB controller.
  3. The SETUP token packet contains the destination address and packet destination endpoint information of the data packet immediately following it.
  4. Among the 4 token packets, only the SOF token packet is not followed by data transmission.
  5. For low-speed/full-speed devices, SYNC is: 0000 0001; for high-speed devices, there are 31 0s in SYNC, and the last bit is 1.

3.1.2 Data Packet (DATAx)

In the USB1.1 protocol, there are only two types of data packets: DATA0 and DATA1. USB2.0 adds two new data packets: DATA2 and MDATA are respectively used for high-speed split transactions and high-speed bandwidth synchronous transmission. DATA0 and DATA1 are the focus of attention.
DATA0 data packet structure (PID of DATA0 data packet is 0xc3):
Insert image description here
DATA1 data packet structure (PID of DATA1 data packet is 0xd2):
Insert image description here
Description:
For control transmission

  1. The data process of establishing a transaction uses the DATA0 data packet (the data process of the SETUP transaction can only use DATA0). The DATA0 data packet is 8 bytes of request data.
  2. The data process of the IN transaction or OUT transaction uses the DATA1 data packet, which is the actual data to be interacted.
  3. The data packet transmitted by endpoint 0 is switched between DATA0 and DATA1. This is the reason why the toggle function of the USB control chip endpoint is configured when implementing the device stack.
  4. The data packet follows the token packet, and the destination address and endpoint information of the data packet transmission have been specified in the token.

3.1.3 Handshake packet (ACK)

The handshake packet has only three fields, and its structure is as follows:
Insert image description here
Description:

  1. The handshake packet follows the token packet or data packet to form a complete transaction.
  2. Handshake packets can be sent by the device or the host.
  3. The direction of the handshake packet is opposite to the direction of the data packet.

3.2 Transaction

A transaction usually consists of two or three packets: token packet + data packet + handshake packet
1) Token packet: starting a transaction is always sent by the host.
2) Data packet: used to transmit data, which can be sent from the host to the device or from the device to the host. The direction of the data packet is specified by the token packet.
3) Handshake packet: The sender of the handshake packet is the receiver of the data packet. Therefore, the direction of the handshake packet and the direction of the data packet are opposite. When the data is received correctly, the receiver sends a handshake packet.

3.2.1 Classification of transactions

A transaction always starts with a token package, so the type of token package determines the type of transaction. Therefore, transactions can be divided into: SETUP transactions, IN transactions, and OUT transactions.

3.3 Transfer

USB has a total of four transmission types: bulk transmission (bulk), interrupt transmission (interrupt), isochronous transmission (isochronous), and control transmission (control). Different transmission types have their own data flow models.
In the device enumeration stage, the data interaction between the device's hosts uses control transmission. First, understand the control transmission. For other types of transmission and their data flow models, we will study them one by one when we introduce the development of different types of devices later.

3.3.1 Control transmission

Control transfer is the most complex of the four transfer types. The control transfer process is divided into three phases: the first phase is the establishment phase; the second phase is the optional data phase; and the third process is the status phase. These three stages are also called the establishment process, data process, and status process.
The establishment process is a establishment transaction. The first packet in the transaction is the SETUP token packet, followed by the DATA0 data packet (8 bytes of request data), and finally the handshake packet (can only be the ACK handshake packet). The data phase is optional, which means that the control transfer does not need a data process (the data field of the SETUP package will be designated as 0). The data process is divided into: control read transfer, data transactions are input; control write transfer, data transactions It is output (reading/writing is relative to the host). And the first data packet of the data process must be the DATA1 data packet.

3.4 The relationship between packages, transactions and transmission

Packages constitute transactions, and transactions constitute transmissions. That is, a transmission consists of multiple transactions, and a transaction consists of multiple packages.
The type of transaction is determined by the type of package, and the type of transmission depends on the transmission used to implement the function.

3.4.1 Relationship diagram

Taking control transmission and the host requesting the descriptor data of the device as an example, the relationship diagram is as follows:
Insert image description here
Description:

  1. Control transfer is generally used to implement a request defined by the USB protocol
  2. The SETUP transaction is used to send a request command to the device
  3. IN transactions are used by the USB device to return data requested by the host to the host.
  4. The OUT transaction uses a handshake mechanism to confirm the normal end of the previous data transaction.

4. USB chip

The USB chip is divided into the Controller part and the PHY (Physical Layer, PHY is responsible for the underlying signal conversion) part. The Controller part mainly implements USB protocol and control. The internal logic mainly includes the MAC layer, CSR layer and FIFO control layer, as well as other low-power management and other layers. The MAC implements packet packaging and unpacking according to the USB protocol, and sends the data to the PHY according to the UTMI bus format (USB3.0 is PIPE). The CSR layer performs register control. The software controls the USB chip through the CSR register. This part mainly acts as a Slave to interact with the CPU through AXI or AHB. The FIFO control layer mainly interacts with DDR for data, controls the channel through which USB transfers data from DDR, and mainly interacts as a Master through AXI/AHB. Part of the PHY function mainly implements the parallel-to-serial function, converting parallel data from the UTMI or PIPE port into serial data, and then outputs it to the outside of the chip through the differential data line.
The function implemented inside the USB chip is to accept software control, then transfer data from the memory and package the data according to the USB protocol, and then output the data to the outside of the chip after parallel-to-serial conversion. Or receive differential data signals from outside the chip, unpack the data after serial-to-parallel conversion and write it to the memory.
Insert image description here
The USB controller accepts digital signals, and the USB PHY is responsible for converting digital signals and analog signals between the USB controller and the USB interface.

5. USB device insertion detection mechanism

First, connect a 15K ohm pull-down resistor to ground on D+ and D- of each downstream port of the USB hub (Hub). In this way, when the hub port is floating, it is pulled to a low level by these two pull-down resistors. On the USB device side, a 1.5K ohm pull-up resistor is connected to D+ or D-. For full-speed and high-speed devices, the pull-up resistor is connected to D+; while for low-speed devices, the pull-up resistor is connected to D-. In this way, when the device is plugged into the hub, the voltage is divided by the 1.5K pull-up resistor and the 15K pull-down resistor, resulting in one of the differential data lines being pulled high. After the hub detects this status, it reports to the USB host controller (or reports to the USB host controller through its upper-level hub), thus detecting the insertion of the device. The USB high-speed device is first recognized as a full-speed device, and then switches to high-speed mode through confirmation between HOST and DEVICE. In high-speed mode, it is the current transfer mode. At this time, the pull-up resistor on D+ is disconnected.

5.1 Identify low-speed and full-speed devices

Both D+ and D- of the host are connected to a 15K pull-down resistor; the data line D+ of the full-speed USB device is connected to a 1.5K pull-up resistor. Once connected to the host, the D+ of the host is pulled high; the data line D- of the low-speed USB device is connected There is a 1.5K pull-up resistor. Once connected to the host, the D- of the host will be pulled high. Therefore, the host can determine whether the connected device is a full-speed or low-speed device based on whether its D+ is high or D- is high. The hardware structure diagram is as follows:
Insert image description here
Insert image description here

5.2 Identify high-speed equipment

Initially, the high-speed device is connected to the host in full-speed mode, and D+ has a pull-up resistor. After the host detects that the full-speed device is connected, it resets the device. The USB device receives the reset signal and actively initiates a high-speed mode handshake protocol for speed recognition. Next, it depends on the host. If the host's USB controller supports high-speed mode transmission, the host will interact with the high-speed device to complete the high-speed mode handshake protocol. After that, both work in high-speed mode; if the host does not support High-speed mode transmission (if there is no EHCI type controller on the host, such as an older computer, and it is an XP system), then the handshake protocol will fail and the device will not switch to high-speed mode. After that, both will work. In full speed mode.
If a full-speed device is connected to a high-speed host, the device cannot initiate a high-speed handshake protocol, so eventually the device and the host will work in full-speed mode; the host has multiple controller types and can work in full-speed/high-speed mode.
Both full-speed and high-speed controllers have DP pull-ups, while low-speed controllers have DM pull-ups (something to note when configuring registers).

6. Request

Requests are the same as descriptors. There are standard device requests, and different types of devices have their own specific requests. The standard device requests will be introduced first. For special requests, each different type of device will be introduced and then analyzed.

6.1 Data structure of standard device request

The USB protocol stipulates that the length of the standard request is 8 bytes. During the device enumeration process, the Host will issue a series of standard requests. The device needs to parse these standard requests (SETUP transactions) and respond correctly so that the device can be successfully enumerated. Only after successful enumeration can the relevant interfaces be called for data communication.
The 8-byte standard request structure is as follows:
Insert image description here
The analysis of each field is as follows:
Insert image description here
For standard requests, D6~D5 = 00, the USB protocol specifies 11 standard requests, and the request code (bRequest) is as shown in the table below.
Insert image description here
The following focuses on three standard requests.

6.1.1 GET_DESCRIPTOR request

Get descriptor request. This request is issued by the Host. The device parses the request and returns the specified descriptor data to the Host. It is the most commonly used request in the device enumeration process.
The request structure for obtaining the device descriptor is as follows:
Insert image description here
The analysis of each field is as follows
1) wValue has two bytes, and the low byte represents: index number. That is, which descriptor within the same type of descriptor.
Example 1: String descriptors can be divided into manufacturer strings, product strings, and product serial number strings. The corresponding index values ​​are 1, 2, and 3 respectively. If the index value is 0, it means that the voice ID string is obtained.
Example 2: If a device has multiple configurations (multiple configuration descriptors are defined), then the index number is the ID number of the configuration descriptor (bConfigurationValue).
The high byte of wValue indicates the type of descriptor. The USB protocol stipulates that different types are represented by a unique code value, as shown in the following table: 2) The
Insert image description here
wIndex
field is specially set for obtaining string descriptors. When the host requests to obtain other descriptors, the value of this field must be 0; when the host requests to obtain those three string descriptors, this field is the language ID number of the string, usually American English, and the value is fixed to 0x0409, this value is the value returned by the device when the host obtains the string descriptor language ID. Therefore, when the host obtains the device string descriptor, the order is generally (if the index value of the string descriptor is set to non-0 in the device descriptor): A. The first request
is the language ID of the string descriptor.
B. Request manufacturer string
C. Request product string
D. Request product serial number string
3) The wLength
data process requires the data length returned by the device. The data returned by the device can be less than this value. For example, during the enumeration process, Host (win7) will request a configuration descriptor with a length of 0xFF at one time.
Note:
(1) If the device is working in Full-Speed ​​Mode, then the host side obtains the standard descriptor and there are only three standard requests: A. Request to obtain the device descriptor; B. Request to obtain the configuration descriptor; C .Request to get a string descriptor. For interface descriptors and endpoint descriptors, they are returned together when the host requests the configuration descriptor set.
(2) The USB protocol stipulates that the bus transmission method is serial, with the LSB first,..., and the MSB last. This is what you need to pay attention to when analyzing data on BusHound or protocol analyzer.

6.1.2 SET_ADDRESS request

After the host receives the first data packet (device descriptor data packet) and parses it correctly, it then enters the address setting phase.
The set address request is also a USB standard device request. This is a process of assigning an address (the host assigns an address to the device). Since it is a standard request, it also has 8 bytes of data. The specified address is contained in the wValue field.
The host obtains the device descriptor from the device with address 0. Once the device descriptor is successfully obtained for the first time, the host will immediately send a request to set the address, reducing the time the device uses public address 0. (Every device is reset first when it is inserted. The default address is 0, that is, the 0 address is the initialization address of all USB devices, which can be understood as the public address).
The structure of the address setting request is as follows:
Insert image description hereDescription:
(1) There is no data in the request process of setting the address, so the data length is 0.
(2) The index is not needed (the index is only used when the string descriptor is requested), so it is also 0.
Example:
The data packet sent by the host is: 00 05 1D 00 00 00 00 00
Analysis: Data direction [Host -> Device] Set the address request address data to 0x001D=29

6.1.3 SET_CONFIGURATION request

After the configuration request is issued, the USB controller on the device enters the configuration state. The device side must use the corresponding configuration according to the configuration value so that the USB device can work normally.
The structure of the setting configuration is as follows:
Insert image description here
Description:
(1) In the setting configuration request, the first byte (low byte) of wValue is the configured value. When this value is consistent with the configuration number in a configuration descriptor (equal), it means that the configuration is selected, and the device will use this configuration.
Example
A USB device can have many configurations. bConfigurationValue is the identity of each configuration! When the host requests to set the configuration, it will issue a configuration value. If the bConfigurationValue of a certain configuration matches the configuration value requested by the host, it means that the configuration is activated, and the USB device uses this configuration (it is decided by the host which device uses which configuration).
A set configuration request is an output request that enables the corresponding endpoint based on the requested configuration value. After receiving it, the device returns a 0-length status packet. The device can enable the non-0 endpoint only after receiving the non-0 configuration value. Otherwise non-zero endpoints are disabled.

7. USB device enumeration process

After the USB host detects that the USB device is plugged in, it must enumerate the device. Why enumerate? Enumeration is to read some information from the device, including USB device transmission type, ID number, Product, USB speed and other information, so that the host can load the appropriate driver based on this information. When debugging USB devices, a very important point is the USB enumeration process. As long as the enumeration is successful, it is mostly successful.
Before talking about enumeration, let's briefly talk about a USB transmission mode-control transmission. This kind of transmission is very important in USB. It must ensure the correctness of the data. Control transmission is used during the enumeration process of the device. Control transmission is divided into three processes: ① Setup process (Setup). ② Optional data process. ③State process. The setup process is initiated by the USB host. It starts with a Setup token packet, followed by a DATA0 packet. If it is control input transmission, then the data process is the input data; if it is control output transmission, then the data process is the output data. If during the setting process, the data length is specified as 0, there will be no data process. The data process is followed by the state process. The data transmission direction of the status process is just opposite to that of the data process: if it is a control input transmission, the status process is an output data packet; if it is a control output transmission, the status process is an input data packet. The status phase is used to confirm that all data has been transmitted correctly.

7.1 Detailed process of enumeration

First, after the USB host detects that the USB device is inserted, it will reset the device first. After the device is reset, the device is in the default/idle state and is ready to receive requests from the host. The USB host will send a standard request to obtain the device descriptor for the device with address 0. All USB devices have addresses of 0 after the bus is reset, so that the host can communicate with the devices that have just been plugged in through address 0 (the request sent by the host at this time is the default address 0, endpoint 0, although all devices with no assigned addresses The requests from the host are obtained through address 0. However, since the enumeration process is not processed by multiple devices in parallel, but by enumerating one device at a time, multiple devices will not respond to requests from the host at the same time. ). The host issues an input request to obtain a device descriptor during the establishment phase. After the device receives the request, it returns the device descriptor to the host during the data process. After the host successfully obtains the device descriptor of a data packet and confirms that there are no errors, (note: the endpoint 0 size of some USB devices is less than 18 bytes (but has at least 8 bytes), while the standard device description has 18 Bytes, in this case, the USB device can only temporarily return part of the device descriptor according to the maximum package, and the host will not request the remaining device descriptor part after successfully obtaining the previous part of the descriptor, but is entering the address setting stage), a 0-length status packet will be returned to the device.
Then the host resets the device (the purpose of resetting is to make the device enter a certain state), and then it will enter the address setting stage. At this time, the USB host controller sends a request to set the address (set_address) (establishment process, setting address without data process). The address is included in the establishment package. The specific address will be managed by the USB host and it will assign a unique address. Give new equipment. After receiving the address, the USB device returns a 0-length status packet. After the host receives the 0-length status packet, it returns an ACK to the device. After the device receives this ACK, it can enable the new address. This address is lifelong for the device. When the device is there, the address is there; when the device disappears (unplugged, reset, system restarts), the address is reclaimed. When the same device is enumerated again, the address obtained is not necessarily the same as the last time.
Insert image description here

Next, the host sends a Get_Descriptor request to the new address to read the device descriptor. This time the host sends a Get_Descriptor request, which can be considered sincere. It will carefully parse the content of the device descriptor. The information in the device descriptor includes the maximum packet length of endpoint 0, the number of configurations supported by the device, device type, VID (Vendor ID, assigned by USB-IF), PID (Product ID, customized by the manufacturer), etc. information.

  • Carefully analyze each standard request made by the host and who is the recipient (device, interface, endpoint).
  • During the enumeration process, the transmission methods used are all control transmission methods.
  • During the enumeration process, there will definitely be some necessary requests, such as: Get device descriptor---->Set address---->USB reset bus----->Get configuration descriptor------> Get the configuration descriptor collection---->Get various types of string descriptors->Set configuration.
  • It is necessary to clarify the meaning of various descriptors and their definitions in the program, as well as how data is returned to the host. The entire data circulation process of clearly receiving the request and responding.

7.2 The host mounts the driver for the device (except composite devices)

After parsing the descriptor, the host has enough understanding of the device and will select the most appropriate driver for the device. Then tell the world (announce_device) indicates that the device has been found, and finally calls the interface device_add provided by the device model to add the device to the device list of the USB bus. Then the USB bus will traverse each driver in the driver list and call its own match( usb_device_match) function to see if they match your device or interface. If they match, call the device_bind_driver function. Now control is handed over to the device driver.
For composite devices, different interfaces (Interfaces) should usually be configured for different drivers. Therefore, you need to wait until the device is configured and the interface is enabled before the driver can be mounted.
The device-configuration-interface-endpoint relationship is shown in the figure below:
Insert image description here
The specific process of USB enumeration:
Insert image description here
When the daemon is run for the first time or the status of the USB port changes, and the daemon is awakened, the hub_events function will be run. This function is in the USB system. At the core, the USB enumeration process is completed by it.

static void hub_events(void)
{
    
    
    struct list_head *tmp;
    struct usb_device *hdev;
    struct usb_interface *intf;
    struct usb_hub *hub;
    struct device *hub_dev;
    u16 hubstatus;
    u16 hubchange;
    u16 portstatus;
    u16 portchange;
    int i, ret;
    int connect_change, wakeup_change;

    /*
     *  We restart the list every time to avoid a deadlock with
     * deleting hubs downstream from this one. This should be
     * safe since we delete the hub from the event list.
     * Not the most efficient, but avoids deadlocks.
     */
    while (1) {
    
    

        /* Grab the first entry at the beginning of the list */
        spin_lock_irq(&hub_event_lock);
        if (list_empty(&hub_event_list)) {
    
    
            spin_unlock_irq(&hub_event_lock);
            break;
        }

        tmp = hub_event_list.next;
        list_del_init(tmp);

        hub = list_entry(tmp, struct usb_hub, event_list);
        kref_get(&hub->kref);

        /* make sure hdev is not freed before accessing it */
        if (hub->disconnected) {
    
      //判断hub是否连接
            spin_unlock_irq(&hub_event_lock);
            goto hub_disconnected;
        } else {
    
    
            usb_get_dev(hub->hdev);
        }
        spin_unlock_irq(&hub_event_lock);

        hdev = hub->hdev;
        hub_dev = hub->intfdev;
        intf = to_usb_interface(hub_dev);
        dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
                hdev->state, hub->descriptor
                    ? hub->descriptor->bNbrPorts
                    : 0,
                /* NOTE: expects max 15 ports... */
                (u16) hub->change_bits[0],
                (u16) hub->event_bits[0]);

        /* Lock the device, then check to see if we were
         * disconnected while waiting for the lock to succeed. */
        usb_lock_device(hdev);
        if (unlikely(hub->disconnected))
            goto loop_disconnected;

        /* If the hub has died, clean up after it */
        if (hdev->state == USB_STATE_NOTATTACHED) {
    
    
            hub->error = -ENODEV;
            hub_quiesce(hub, HUB_DISCONNECT);
            goto loop;
        }

        /* Autoresume */
        ret = usb_autopm_get_interface(intf);
        if (ret) {
    
    
            dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
            goto loop;
        }

        /* If this is an inactive hub, do nothing */
        if (hub->quiescing)
            goto loop_autopm;

        if (hub->error) {
    
    
            dev_dbg (hub_dev, "resetting for error %d\n",
                hub->error);

            ret = usb_reset_device(hdev);
            if (ret) {
    
    
                dev_dbg (hub_dev,
                    "error resetting hub: %d\n", ret);
                goto loop_autopm;
            }

            hub->nerrors = 0;
            hub->error = 0;
        }

        /* deal with port status changes */
        //遍历hub中的所有port,对各个port的状态进行查看,判断port是否发生了状态变化
        for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
    
    
            if (test_bit(i, hub->busy_bits))
                continue;
            connect_change = test_bit(i, hub->change_bits);
            wakeup_change = test_and_clear_bit(i, hub->wakeup_bits);
            if (!test_and_clear_bit(i, hub->event_bits) &&
                    !connect_change && !wakeup_change)
                continue;

            ret = hub_port_status(hub, i,
                    &portstatus, &portchange);
            if (ret < 0)
                continue;

            if (portchange & USB_PORT_STAT_C_CONNECTION) {
    
    
                usb_clear_port_feature(hdev, i,
                    USB_PORT_FEAT_C_CONNECTION);
                connect_change = 1;
            }

            if (portchange & USB_PORT_STAT_C_ENABLE) {
    
    
                if (!connect_change)
                    dev_dbg (hub_dev,
                        "port %d enable change, "
                        "status %08x\n",
                        i, portstatus);
                usb_clear_port_feature(hdev, i,
                    USB_PORT_FEAT_C_ENABLE);

                /*
                 * EM interference sometimes causes badly
                 * shielded USB devices to be shutdown by
                 * the hub, this hack enables them again.
                 * Works at least with mouse driver. 
                 */
                if (!(portstatus & USB_PORT_STAT_ENABLE)
                    && !connect_change
                    && hub->ports[i - 1]->child) {
    
    
                    dev_err (hub_dev,
                        "port %i "
                        "disabled by hub (EMI?), "
                        "re-enabling...\n",
                        i);
                    connect_change = 1;
                }
            }

            if (hub_handle_remote_wakeup(hub, i,
                        portstatus, portchange))
                connect_change = 1;

            if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
    
    
                u16 status = 0;
                u16 unused;

                dev_dbg(hub_dev, "over-current change on port "
                    "%d\n", i);
                usb_clear_port_feature(hdev, i,
                    USB_PORT_FEAT_C_OVER_CURRENT);
                msleep(100);    /* Cool down */
                hub_power_on(hub, true);
                hub_port_status(hub, i, &status, &unused);
                if (status & USB_PORT_STAT_OVERCURRENT)
                    dev_err(hub_dev, "over-current "
                        "condition on port %d\n", i);
            }

            if (portchange & USB_PORT_STAT_C_RESET) {
    
    
                dev_dbg (hub_dev,
                    "reset change on port %d\n",
                    i);
                usb_clear_port_feature(hdev, i,
                    USB_PORT_FEAT_C_RESET);
            }
            if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
                    hub_is_superspeed(hub->hdev)) {
    
    
                dev_dbg(hub_dev,
                    "warm reset change on port %d\n",
                    i);
                usb_clear_port_feature(hdev, i,
                    USB_PORT_FEAT_C_BH_PORT_RESET);
            }
            if (portchange & USB_PORT_STAT_C_LINK_STATE) {
    
    
                usb_clear_port_feature(hub->hdev, i,
                        USB_PORT_FEAT_C_PORT_LINK_STATE);
            }
            if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
    
    
                dev_warn(hub_dev,
                    "config error on port %d\n",
                    i);
                usb_clear_port_feature(hub->hdev, i,
                        USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
            }

            /* Warm reset a USB3 protocol port if it's in
             * SS.Inactive state.
             */
            if (hub_port_warm_reset_required(hub, portstatus)) {
    
    
                int status;
                struct usb_device *udev =
                    hub->ports[i - 1]->child;

                dev_dbg(hub_dev, "warm reset port %d\n", i);
                if (!udev ||
                    !(portstatus & USB_PORT_STAT_CONNECTION) ||
                    udev->state == USB_STATE_NOTATTACHED) {
    
    
                    status = hub_port_reset(hub, i,
                            NULL, HUB_BH_RESET_TIME,
                            true);
                    if (status < 0)
                        hub_port_disable(hub, i, 1);
                } else {
    
    
                    usb_lock_device(udev);
                    status = usb_reset_device(udev);
                    usb_unlock_device(udev);
                    connect_change = 0;
                }
            }

            if (connect_change)//如果port状态发生变化,则调用hub_port_connect_change
                hub_port_connect_change(hub, i,
                        portstatus, portchange);
        } /* end for i */

        /* deal with hub status changes */
        if (test_and_clear_bit(0, hub->event_bits) == 0)
            ;   /* do nothing */
        else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
            dev_err (hub_dev, "get_hub_status failed\n");
        else {
    
    
            if (hubchange & HUB_CHANGE_LOCAL_POWER) {
    
    
                dev_dbg (hub_dev, "power change\n");
                clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
                if (hubstatus & HUB_STATUS_LOCAL_POWER)
                    /* FIXME: Is this always true? */
                    hub->limited_power = 1;
                else
                    hub->limited_power = 0;
            }
            if (hubchange & HUB_CHANGE_OVERCURRENT) {
    
    
                u16 status = 0;
                u16 unused;

                dev_dbg(hub_dev, "over-current change\n");
                clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
                msleep(500);    /* Cool down */
                            hub_power_on(hub, true);
                hub_hub_status(hub, &status, &unused);
                if (status & HUB_STATUS_OVERCURRENT)
                    dev_err(hub_dev, "over-current "
                        "condition\n");
            }
        }

 loop_autopm:
        /* Balance the usb_autopm_get_interface() above */
        usb_autopm_put_interface_no_suspend(intf);
 loop:
        /* Balance the usb_autopm_get_interface_no_resume() in
         * kick_khubd() and allow autosuspend.
         */
        usb_autopm_put_interface(intf);
 loop_disconnected:
        usb_unlock_device(hdev);
        usb_put_dev(hdev);
 hub_disconnected:
        kref_put(&hub->kref, hub_release);

        } /* end while (1) */
}

hub_events itself is an infinite loop, which will continue to execute as long as the conditions are met.
First, determine whether hub_event_list is empty. If it is empty, jump out of the loop. Hub_events will end and enter sleep state. If it is not empty, remove an item from the hub_event_list list and delete it from hub_event_list through list_entry. Obtain the hub corresponding to the event_list and increase the hub usage count; then make some logical judgments to determine whether the hub is connected and whether there are errors on the hub. If there are errors, restart the hub. If there are no errors, then check each hub on the hub. Scan each port to determine whether the status of each port has changed;
then traverse all ports in the hub, check the status of each port, and determine whether the status of the port has changed. If there is a change, call hub_port_connect_change for processing; in the hub
structure There are multiple flag bits used to indicate some status of the port:

  1. If the i-th port of the USB is in the resume or reset state, the i-th position in busy_bits in the hub is 1; if the i-th position in busy_bits is 1, then the current port is exited;
  2. The 0th bit in event_bits is used to indicate whether the hub itself generates local power status change and whether overcurrent occurs. The other bits are used to indicate whether the status of each port under the hub changes. These statuses include: ConnectStatusChange The connection status changes, and the PortEnableStatusChange port uses The function status changes, the PortSuspendStatusChange port is completed from suspension to recovery, the PortOverCurrentIndicatorChange port overcurrent status changes, and the PortResetStatusChange port reset10ms is set;
  3. remove_bits is used to indicate whether there are devices connected to each port under the hub. If set, it means there is no device connection or the device has been disconnected;
  4. change_bits is used to indicate whether the logical status of each port under the hub has changed. It is assigned when the hub is initialized. These statuses mainly include: 1. There was no device on the original port, but now a device connection is detected; 2. There was a device on the original port. If the device is in the NOATTACHED state due to the abnormal shutdown of the controller or the prohibition of the USB port, the device needs to be disconnected;

When traversing the port, first judge these flags. If there is no change, skip the current port. Otherwise, read the status of the current port and determine the cause of the status change; if the connection status of the port changes, clear the
corresponding Port status, set the connect_change variable to 1;
if the port's enable status changes, clear the corresponding status flag, if the status change is caused by EMI electromagnetic interference, set the connect_change variable to 1; ...After a series
of
ports Judgment, if it is found that the connection status of the port changes or the connection enable changes due to EMI, that is, connect_change=1, then call hub_port_connect_change.

/* Handle physical or logical connection change events.
 * This routine is called when:
 *  a port connection-change occurs;
 *  a port enable-change occurs (often caused by EMI);
 *  usb_reset_and_verify_device() encounters changed descriptors (as from
 *      a firmware download)
 * caller already locked the hub
 */
static void hub_port_connect_change(struct usb_hub *hub, int port1,
                    u16 portstatus, u16 portchange)
{
    
    
    struct usb_device *hdev = hub->hdev;
    struct device *hub_dev = hub->intfdev;
    struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
    unsigned wHubCharacteristics =
            le16_to_cpu(hub->descriptor->wHubCharacteristics);
    struct usb_device *udev;
    int status, i;
    unsigned unit_load;

    dev_dbg (hub_dev,
        "port %d, status %04x, change %04x, %s\n",
        port1, portstatus, portchange, portspeed(hub, portstatus));

    if (hub->has_indicators) {
    
    //如果当前的hub支持indicator指示灯,则设备指示灯为HUB_LED_AUTO
        set_port_led(hub, port1, HUB_LED_AUTO);
        hub->indicator[port1-1] = INDICATOR_AUTO;
    }

#ifdef  CONFIG_USB_OTG
    /* during HNP, don't repeat the debounce */
    if (hdev->bus->is_b_host)
        portchange &= ~(USB_PORT_STAT_C_CONNECTION |
                USB_PORT_STAT_C_ENABLE);
#endif

    /* Try to resuscitate an existing device */
    udev = hub->ports[port1 - 1]->child;
    if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
            udev->state != USB_STATE_NOTATTACHED) {
    
    
        usb_lock_device(udev);
        if (portstatus & USB_PORT_STAT_ENABLE) {
    
    
            status = 0;     /* Nothing to do */

#ifdef CONFIG_PM_RUNTIME
        } else if (udev->state == USB_STATE_SUSPENDED &&
                udev->persist_enabled) {
    
    
            /* For a suspended device, treat this as a
             * remote wakeup event.
             */
            status = usb_remote_wakeup(udev);
#endif

        } else {
    
    
            status = -ENODEV;   /* Don't resuscitate */
        }
        usb_unlock_device(udev);

        if (status == 0) {
    
      //清除当前port的change_bits标志
            clear_bit(port1, hub->change_bits);
            return;
        }
    }

    /* Disconnect any existing devices under this port */
    if (udev) {
    
    
        if (hcd->phy && !hdev->parent &&
                !(portstatus & USB_PORT_STAT_CONNECTION))
            usb_phy_notify_disconnect(hcd->phy, udev->speed);
        usb_disconnect(&hub->ports[port1 - 1]->child);
    }
    clear_bit(port1, hub->change_bits);

    /* We can forget about a "removed" device when there's a physical
     * disconnect or the connect status changes.
     */
    if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
            (portchange & USB_PORT_STAT_C_CONNECTION))
        clear_bit(port1, hub->removed_bits);

    if (portchange & (USB_PORT_STAT_C_CONNECTION |
                USB_PORT_STAT_C_ENABLE)) {
    
    
        status = hub_port_debounce_be_stable(hub, port1);
        if (status < 0) {
    
    
            if (status != -ENODEV && printk_ratelimit())
                dev_err(hub_dev, "connect-debounce failed, "
                        "port %d disabled\n", port1);
            portstatus &= ~USB_PORT_STAT_CONNECTION;
        } else {
    
    
            portstatus = status;
        }
    }

    /* Return now if debouncing failed or nothing is connected or
     * the device was "removed".
     */
    if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
            test_bit(port1, hub->removed_bits)) {
    
    

        /* maybe switch power back on (e.g. root hub was reset) */
        if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
                && !port_is_power_on(hub, portstatus))
            set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);

        if (portstatus & USB_PORT_STAT_ENABLE)
            goto done;
        return;
    }
    if (hub_is_superspeed(hub->hdev))
        unit_load = 150;
    else
        unit_load = 100;

    status = 0;
    for (i = 0; i < SET_CONFIG_TRIES; i++) {
    
    

        /* reallocate for each attempt, since references
         * to the previous one can escape in various ways
         * 通过usb_alloc_dev为新的USB设备申请资源,并进行一些初始化,
         * 将usb设置为USB_STATE_ATTACHED,表示设备已经连接
         */
        udev = usb_alloc_dev(hdev, hdev->bus, port1);
        if (!udev) {
    
    
            dev_err (hub_dev,
                "couldn't allocate port %d usb_device\n",
                port1);
            goto done;
        }

        usb_set_device_state(udev, USB_STATE_POWERED);//设置usb设备为USB_STATE_POWERED态
        udev->bus_mA = hub->mA_per_port;//设置usb设备可以从port上获取的电流量
        udev->level = hdev->level + 1;//设置usb设备的拓扑层级
        udev->wusb = hub_is_wusb(hub);

        /* Only USB 3.0 devices are connected to SuperSpeed hubs. */
        //如果hub支持超速,则设置usb设备的速度为超速,否则设置usb设备速度 为unknow
        if (hub_is_superspeed(hub->hdev))
            udev->speed = USB_SPEED_SUPER;
        else
            udev->speed = USB_SPEED_UNKNOWN;

        choose_devnum(udev);  //从usb总线中找到一个usb地址
        if (udev->devnum <= 0) {
    
    
            status = -ENOTCONN; /* Don't retry */
            goto loop;
        }

        /* reset (non-USB 3.0 devices) and get descriptor */
        status = hub_port_init(hub, udev, port1, i);
        if (status < 0)
            goto loop;

        usb_detect_quirks(udev);
        if (udev->quirks & USB_QUIRK_DELAY_INIT)
            msleep(1000);

        /* consecutive bus-powered hubs aren't reliable; they can
         * violate the voltage drop budget.  if the new child has
         * a "powered" LED, users should notice we didn't enable it
         * (without reading syslog), even without per-port LEDs
         * on the parent.
         */ 
         //如果当前的usb设备是一个hub,它由hub供电
        if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
                && udev->bus_mA <= unit_load) {
    
    
            u16 devstat;

            status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
                    &devstat);
            if (status < 2) {
    
    
                dev_dbg(&udev->dev, "get status %d ?\n", status);
                goto loop_disable;
            }
            le16_to_cpus(&devstat);
            if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
    
    
                dev_err(&udev->dev,
                    "can't connect bus-powered hub "
                    "to this port\n");
                if (hub->has_indicators) {
    
    
                    hub->indicator[port1-1] =
                        INDICATOR_AMBER_BLINK;
                    schedule_delayed_work (&hub->leds, 0);
                }
                status = -ENOTCONN; /* Don't retry */
                goto loop_disable;
            }
        }

        /* check for devices running slower than they could */ 
        //如果usb设备支持高速运行,而现在却工作在全速,它就会通过点亮绿色指示灯来指示这种错误
        if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
                && udev->speed == USB_SPEED_FULL
                && highspeed_hubs != 0)
            check_highspeed (hub, udev, port1);

        /* Store the parent's children[] pointer.  At this point
         * udev becomes globally accessible, although presumably
         * no one will look at it until hdev is unlocked.
         */
        status = 0;

        /* We mustn't add new devices if the parent hub has
         * been disconnected; we would race with the
         * recursively_mark_NOTATTACHED() routine.
         */
        spin_lock_irq(&device_state_lock);
        if (hdev->state == USB_STATE_NOTATTACHED)
            status = -ENOTCONN;
        else
            hub->ports[port1 - 1]->child = udev;
        spin_unlock_irq(&device_state_lock);

        /* Run it through the hoops (find a driver, etc) */
        if (!status) {
    
    
            status = usb_new_device(udev);//通过usb_new_device去获取usb设备的配置信息,将它注册到系统中
            if (status) {
    
    
                spin_lock_irq(&device_state_lock);
                hub->ports[port1 - 1]->child = NULL;
                spin_unlock_irq(&device_state_lock);
            }
        }

        if (status)
            goto loop_disable;

        status = hub_power_remaining(hub);
        if (status)
            dev_dbg(hub_dev, "%dmA power budget left\n", status);

        return;

loop_disable:
        hub_port_disable(hub, port1, 1);
loop:
        usb_ep0_reinit(udev);
        release_devnum(udev);
        hub_free_dev(udev);
        usb_put_dev(udev);
        if ((status == -ENOTCONN) || (status == -ENOTSUPP))
            break;
    }
    if (hub->hdev->parent ||
            !hcd->driver->port_handed_over ||
            !(hcd->driver->port_handed_over)(hcd, port1)) {
    
    
        if (status != -ENOTCONN && status != -ENODEV)
            dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
                    port1);
    }

done:
    hub_port_disable(hub, port1, 1);
    if (hcd->driver->relinquish_port && !hub->hdev->parent)
        hcd->driver->relinquish_port(hcd, port1);
}

hub_port_connect_change is divided into two parts:
the first part is mainly to confirm whether a new device is inserted; the
second part is mainly to: after confirming that a new device is inserted on the port, allocate device resources and perform enumeration operations;
for some connected devices, Perform some anti-shake processing. The processing mechanism is: read the status and change status of the port every 25ms to check whether the port connection status is stable. If the port is in a stable state for more than 100ms, it is considered that the device on the current port is stable. This The maximum time of this processing mechanism is 1.5s; if the time for the port to be in a stable state is less than 100ms, the connection is considered unstable, and the current port will be jumped out to execute the next port; for a newly inserted device, it has been
determined existence, then the USB device will be enumerated next.
There can be a total of 128 devices under a USB bus. The USB bus manages the addresses of USB devices in the form of bitmaps. choose_devnum is to find a USB address from the USB bus. The USB address is between 1 and 128; through hub_port_init, the USB device
is Perform reset, set the address of the USB device, and obtain the device descriptor of the USB device. This function is relatively important, so we further analyzed it:

/* Reset device, (re)assign address, get device descriptor.
 * Device connection must be stable, no more debouncing needed.
 * Returns device in USB_STATE_ADDRESS, except on error.
 *
 * If this is called for an already-existing device (as part of
 * usb_reset_and_verify_device), the caller must own the device lock.  For a
 * newly detected device that is not accessible through any global
 * pointers, it's not necessary to lock the device.
 */
static int
hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        int retry_counter)
{
    
    
    static DEFINE_MUTEX(usb_address0_mutex);

    struct usb_device   *hdev = hub->hdev;
    struct usb_hcd      *hcd = bus_to_hcd(hdev->bus);
    int         i, j, retval;
    unsigned        delay = HUB_SHORT_RESET_TIME;
    enum usb_device_speed   oldspeed = udev->speed;
    const char      *speed;
    int         devnum = udev->devnum;

    /* root hub ports have a slightly longer reset period
     * (from USB 2.0 spec, section 7.1.7.5)
     * 设置用于读取port状态的时间间隔,root hub需要50ms,普通的hub需要10ms,
     * 对于低速的usb设备需要200ms
     */
    if (!hdev->parent) {
    
    
        delay = HUB_ROOT_RESET_TIME;
        if (port1 == hdev->bus->otg_port)
            hdev->bus->b_hnp_enable = 0;
    }

    /* Some low speed devices have problems with the quick delay, so */
    /*  be a bit pessimistic with those devices. RHbug #23670 */
    if (oldspeed == USB_SPEED_LOW)
        delay = HUB_LONG_RESET_TIME;

    mutex_lock(&usb_address0_mutex);

    /* Reset the device; full speed may morph to high speed */
    /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ 
    //通过hub_port_reset来reset设备
    retval = hub_port_reset(hub, port1, udev, delay, false);
    if (retval < 0)     /* error or disconnect */
        goto fail;
    /* success, speed is known */

    retval = -ENODEV;

    if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
    
    
        dev_dbg(&udev->dev, "device reset changed speed!\n");
        goto fail;
    }
    oldspeed = udev->speed;

    /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
     * it's fixed size except for full speed devices.
     * For Wireless USB devices, ep0 max packet is always 512 (tho
     * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
     * 根据usb设备的速度来初始化endpont0的最大发送数据长度,
     * 对于无线usb其maxpacketsize为512字节,对于高速和全速为64字节,而低速为8个字节
     */
    switch (udev->speed) {
    
    
    case USB_SPEED_SUPER:
    case USB_SPEED_WIRELESS:    /* fixed at 512 */
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
        break;
    case USB_SPEED_HIGH:        /* fixed at 64 */
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
        break;
    case USB_SPEED_FULL:        /* 8, 16, 32, or 64 */
        /* to determine the ep0 maxpacket size, try to read
         * the device descriptor to get bMaxPacketSize0 and
         * then correct our initial guess.
         */
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
        break;
    case USB_SPEED_LOW:     /* fixed at 8 */
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
        break;
    default:
        goto fail;
    }

    if (udev->speed == USB_SPEED_WIRELESS)
        speed = "variable speed Wireless";
    else
        speed = usb_speed_string(udev->speed);

    if (udev->speed != USB_SPEED_SUPER)
        dev_info(&udev->dev,
                "%s %s USB device number %d using %s\n",
                (udev->config) ? "reset" : "new", speed,
                devnum, udev->bus->controller->driver->name);

    /* Set up TT records, if needed  */ 
    //如果hub为高速,而usb设备为低速或全速,则在它们之间需要有一个速度转换设备tt
    if (hdev->tt) {
    
    
        udev->tt = hdev->tt;
        udev->ttport = hdev->ttport;
    } else if (udev->speed != USB_SPEED_HIGH
            && hdev->speed == USB_SPEED_HIGH) {
    
    
        if (!hub->tt.hub) {
    
    
            dev_err(&udev->dev, "parent hub has no TT\n");
            retval = -EINVAL;
            goto fail;
        }
        udev->tt = &hub->tt;
        udev->ttport = port1;
    }

    /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
     * Because device hardware and firmware is sometimes buggy in
     * this area, and this is how Linux has done it for ages.
     * Change it cautiously.
     *
     * NOTE:  If USE_NEW_SCHEME() is true we will start by issuing
     * a 64-byte GET_DESCRIPTOR request.  This is what Windows does,
     * so it may help with some non-standards-compliant devices.
     * Otherwise we start with SET_ADDRESS and then try to read the
     * first 8 bytes of the device descriptor to get the ep0 maxpacket
     * value.
     */
    for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
    
    
        if (USE_NEW_SCHEME(retry_counter) &&
            !(hcd->driver->flags & HCD_USB3) &&
            !((hcd->driver->flags & HCD_RT_OLD_ENUM) &&
                !hdev->parent)) {
    
    
            struct usb_device_descriptor *buf;
            ushort idvendor;
            int r = 0;

#define GET_DESCRIPTOR_BUFSIZE  64
            buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
            if (!buf) {
    
    
                retval = -ENOMEM;
                continue;
            }

            /* Retry on all errors; some devices are flakey.
             * 255 is for WUSB devices, we actually need to use
             * 512 (WUSB1.0[4.8.1]).
             */
            for (j = 0; j < 3; ++j) {
    
    
                buf->bMaxPacketSize0 = 0;
                r = usb_control_msg(udev, usb_rcvaddr0pipe(),
                    USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
                    USB_DT_DEVICE << 8, 0,
                    buf, GET_DESCRIPTOR_BUFSIZE,
                    initial_descriptor_timeout);
                switch (buf->bMaxPacketSize0) {
    
    
                case 8: case 16: case 32: case 64: case 255:
                    if (buf->bDescriptorType ==
                            USB_DT_DEVICE) {
    
    
                        r = 0;
                        break;
                    }
                    /* FALL THROUGH */
                default:
                    if (r == 0)
                        r = -EPROTO;
                    break;
                }
                if (r == 0)
                    break;
            }
            udev->descriptor.bMaxPacketSize0 =
                    buf->bMaxPacketSize0;
            idvendor = le16_to_cpu(buf->idVendor);
            kfree(buf);

            /*
             * If it is a HSET Test device, we don't issue a
             * second reset which results in failure due to
             * speed change.
             */
            if (idvendor != 0x1a0a) {
    
    
                retval = hub_port_reset(hub, port1, udev,
                             delay, false);
                if (retval < 0) /* error or disconnect */
                    goto fail;
                if (oldspeed != udev->speed) {
    
    
                    dev_dbg(&udev->dev,
                           "device reset changed speed!\n");
                    retval = -ENODEV;
                    goto fail;
                }
            }
            if (r) {
    
    
                if (r != -ENODEV)
                    dev_err(&udev->dev, "device descriptor read/64, error %d\n",
                            r);
                retval = -EMSGSIZE;
                continue;
            }
#undef GET_DESCRIPTOR_BUFSIZE
        }

        /*
         * If device is WUSB, we already assigned an
         * unauthorized address in the Connect Ack sequence;
         * authorization will assign the final address.
         */
        if (udev->wusb == 0) {
    
    
            for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
    
    
                retval = hub_set_address(udev, devnum);
                if (retval >= 0)
                    break;
                msleep(200);
            }
            if (retval < 0) {
    
    
                if (retval != -ENODEV)
                    dev_err(&udev->dev, "device not accepting address %d, error %d\n",
                            devnum, retval);
                goto fail;
            }
            if (udev->speed == USB_SPEED_SUPER) {
    
    
                devnum = udev->devnum;
                dev_info(&udev->dev,
                        "%s SuperSpeed USB device number %d using %s\n",
                        (udev->config) ? "reset" : "new",
                        devnum, udev->bus->controller->driver->name);
            }

            /* cope with hardware quirkiness:
             *  - let SET_ADDRESS settle, some device hardware wants it
             *  - read ep0 maxpacket even for high and low speed,
             */
            msleep(10);
            if (USE_NEW_SCHEME(retry_counter) &&
                !(hcd->driver->flags & HCD_USB3) &&
                !((hcd->driver->flags & HCD_RT_OLD_ENUM) &&
                    !hdev->parent))
                break;
        }

        retval = usb_get_device_descriptor(udev, 8);
        if (retval < 8) {
    
    
            if (retval != -ENODEV)
                dev_err(&udev->dev,
                    "device descriptor read/8, error %d\n",
                    retval);
            if (retval >= 0)
                retval = -EMSGSIZE;
        } else {
    
    
            retval = 0;
            break;
        }
    }
    if (retval)
        goto fail;

    if (hcd->phy && !hdev->parent)
        usb_phy_notify_connect(hcd->phy, udev->speed);

    /*
     * Some superspeed devices have finished the link training process
     * and attached to a superspeed hub port, but the device descriptor
     * got from those devices show they aren't superspeed devices. Warm
     * reset the port attached by the devices can fix them.
     */
    if ((udev->speed == USB_SPEED_SUPER) &&
            (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
    
    
        dev_err(&udev->dev, "got a wrong device descriptor, "
                "warm reset device\n");
        hub_port_reset(hub, port1, udev,
                HUB_BH_RESET_TIME, true);
        retval = -EINVAL;
        goto fail;
    }

    if (udev->descriptor.bMaxPacketSize0 == 0xff ||
            udev->speed == USB_SPEED_SUPER)
        i = 512;
    else
        i = udev->descriptor.bMaxPacketSize0;
    if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
    
    
        if (udev->speed == USB_SPEED_LOW ||
                !(i == 8 || i == 16 || i == 32 || i == 64)) {
    
    
            dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
            retval = -EMSGSIZE;
            goto fail;
        }
        if (udev->speed == USB_SPEED_FULL)
            dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
        else
            dev_warn(&udev->dev, "Using ep0 maxpacket: %d\n", i);
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
        usb_ep0_reinit(udev);
    }

      //根据maxpacketsize去获取完整的设备描述符
    retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
    if (retval < (signed)sizeof(udev->descriptor)) {
    
    
        if (retval != -ENODEV)
            dev_err(&udev->dev, "device descriptor read/all, error %d\n",
                    retval);
        if (retval >= 0)
            retval = -ENOMSG;
        goto fail;
    }

    if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
    
    
        retval = usb_get_bos_descriptor(udev);
        if (!retval) {
    
    
            udev->lpm_capable = usb_device_supports_lpm(udev);
            usb_set_lpm_parameters(udev);
        }
    }

    retval = 0;
    /* notify HCD that we have a device connected and addressed */
    if (hcd->driver->update_device)
        hcd->driver->update_device(hcd, udev);
fail:
    if (retval) {
    
    
        hub_port_disable(hub, port1, 0);
        update_devnum(udev, devnum);    /* for disconnect processing */
    }
    mutex_unlock(&usb_address0_mutex);
    return retval;
}

hub_port_init first resets the port. After the reset is successful, it sets the speed of the USB device and the maximum sending data length of port 0, sets the address of the USB device, and obtains the device descriptor of the USB device; resets the device through hub_port_reset. The reset mechanism is: through
set_port_feature To transmit the USB_PORT_FEAT_RESET command, after the setting is successful, the loop delay is determined by the previously determined time interval to read the status and change status of the port. Make sure that the USB device can still exist normally after reset. If the reset can still be normal, pass the port's status bit to determine the speed of the USB device, the total cycle delay time is 500ms, and the number of USB device resets is 5 times.
Initialize the maximum sending data length of endpont0 according to the speed of the USB device. For wireless USB, its maxpacketsize is 512 bytes, for high speed and full speed, it is 64 bytes, and for low speed, it is 8 bytes; if the hub is high speed, and the USB device
is Low speed or full speed, a speed conversion device tt is required between them;
Get the maximum sent data length of endpoint0 of the usb device by getting the device descriptor of the usb device. The device descriptor actually has 18 bytes, and the maxpacketsize of endpoint0 is the 8th byte in the device descriptor, so as long as It is enough to obtain the first 8 bytes of data of the device descriptor, but some USB devices do not support incomplete USB requests such as obtaining only 8 bytes. To solve this problem, the USB driver adopts two strategies. Get maxpacketsize, one is the windows method, it directly requests 64-byte data from the usb device. For some maxpacketsize is 32 or 64, it can directly transfer the 18-byte device descriptor back at once, and for low The maxpacketsize of the speed device is only 8 bytes, so it needs to be sent multiple times; the Linux method is to first set the address of the USB device, then send an 8-byte data request, and obtain endpoint0 from the returned 8 bytes. maxpacketsize;

/**
 * usb_new_device - perform initial device setup (usbcore-internal)
 * @udev: newly addressed device (in ADDRESS state)
 *
 * This is called with devices which have been detected but not fully
 * enumerated.  The device descriptor is available, but not descriptors
 * for any device configuration.  The caller must have locked either
 * the parent hub (if udev is a normal device) or else the
 * usb_bus_list_lock (if udev is a root hub).  The parent's pointer to
 * udev has already been installed, but udev is not yet visible through
 * sysfs or other filesystem code.
 *
 * It will return if the device is configured properly or not.  Zero if
 * the interface was registered with the driver core; else a negative
 * errno value.
 *
 * This call is synchronous, and may not be used in an interrupt context.
 *
 * Only the hub driver or root-hub registrar should ever call this.
 */
int usb_new_device(struct usb_device *udev)
{
    
    
    int err;

    if (udev->parent) {
    
    
        /* Initialize non-root-hub device wakeup to disabled;
         * device (un)configuration controls wakeup capable
         * sysfs power/wakeup controls wakeup enabled/disabled
         */
        device_init_wakeup(&udev->dev, 0);
    }

    /* Tell the runtime-PM framework the device is active */
    pm_runtime_set_active(&udev->dev);
    pm_runtime_get_noresume(&udev->dev);
    pm_runtime_use_autosuspend(&udev->dev);
    pm_runtime_enable(&udev->dev);

    /* By default, forbid autosuspend for all devices.  It will be
     * allowed for hubs during binding.
     */
    usb_disable_autosuspend(udev);

    err = usb_enumerate_device(udev);   /* Read descriptors */
    if (err < 0)
        goto fail;
    dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",
            udev->devnum, udev->bus->busnum,
            (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
    /* export the usbdev device-node for libusb */
    udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
            (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));

    /* Tell the world! */
    announce_device(udev);

    if (udev->serial)
        add_device_randomness(udev->serial, strlen(udev->serial));
    if (udev->product)
        add_device_randomness(udev->product, strlen(udev->product));
    if (udev->manufacturer)
        add_device_randomness(udev->manufacturer,
                      strlen(udev->manufacturer));

    device_enable_async_suspend(&udev->dev);

    /*
     * check whether the hub marks this port as non-removable. Do it
     * now so that platform-specific data can override it in
     * device_add()
     */
    if (udev->parent)
        set_usb_port_removable(udev);

    /* Register the device.  The device driver is responsible
     * for configuring the device and invoking the add-device
     * notifier chain (used by usbfs and possibly others).
     */
    err = device_add(&udev->dev);
    if (err) {
    
    
        dev_err(&udev->dev, "can't device_add, error %d\n", err);
        goto fail;
    }

    /* Create link files between child device and usb port device. */
    if (udev->parent) {
    
    
        struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
        struct usb_port *port_dev;

        if (!hub)
            goto fail;

        port_dev = hub->ports[udev->portnum - 1];
        if (!port_dev)
            goto fail;

        err = sysfs_create_link(&udev->dev.kobj,
                &port_dev->dev.kobj, "port");
        if (err)
            goto fail;

        err = sysfs_create_link(&port_dev->dev.kobj,
                &udev->dev.kobj, "device");
        if (err) {
    
    
            sysfs_remove_link(&udev->dev.kobj, "port");
            goto fail;
        }

        pm_runtime_get_sync(&port_dev->dev);
    }

    (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
    usb_mark_last_busy(udev);
    pm_runtime_put_sync_autosuspend(&udev->dev);
    return err;

fail:
    usb_set_device_state(udev, USB_STATE_NOTATTACHED);
    pm_runtime_disable(&udev->dev);
    pm_runtime_set_suspended(&udev->dev);
    return err;
}

usb_new_device is mainly to obtain the configuration information of the usb device, and then add the usb device to the system. usb_enumerate_device is relatively important here, that is, it completes the acquisition and allocation of configuration information;

/**
 * usb_enumerate_device - Read device configs/intfs/otg (usbcore-internal)
 * @udev: newly addressed device (in ADDRESS state)
 *
 * This is only called by usb_new_device() and usb_authorize_device()
 * and FIXME -- all comments that apply to them apply here wrt to
 * environment.
 *
 * If the device is WUSB and not authorized, we don't attempt to read
 * the string descriptors, as they will be errored out by the device
 * until it has been authorized.
 */
static int usb_enumerate_device(struct usb_device *udev)
{
    
    
    int err;

    if (udev->config == NULL) {
    
    
        err = usb_get_configuration(udev);
        if (err < 0) {
    
    
            if (err != -ENODEV)
                dev_err(&udev->dev, "can't read configurations, error %d\n",
                        err);
            return err;
        }
    }

    /* read the standard strings and cache them if present */
    udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
    udev->manufacturer = usb_cache_string(udev,
                          udev->descriptor.iManufacturer);
    udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);

    err = usb_enumerate_device_otg(udev);
    if (err < 0)
        return err;

    usb_detect_interface_quirks(udev);

    return 0;
}

Obtain and allocate the configuration, interface, and port information of the USB device through usb_get_configuration.

/*
 * Get the USB config descriptors, cache and parse'em
 *
 * hub-only!! ... and only in reset path, or usb_new_device()
 * (used by real hubs and virtual root hubs)
 */
int usb_get_configuration(struct usb_device *dev)
{
    
    
    struct device *ddev = &dev->dev;
    int ncfg = dev->descriptor.bNumConfigurations;
    int result = 0;
    unsigned int cfgno, length;
    unsigned char *bigbuffer;
    struct usb_config_descriptor *desc;

    cfgno = 0;
    result = -ENOMEM; 
    //如果usb设备的配置个数大于最大允许配置数8,则发出警告,并把设备的配置个数改成最大允许配置数8
    if (ncfg > USB_MAXCONFIG) {
    
    
        dev_warn(ddev, "too many configurations: %d, "
            "using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
        dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
    }

    //如果usb设备没有配置,则不允许,直接返回错误信息
    if (ncfg < 1) {
    
    
        dev_err(ddev, "no configurations\n");
        return -EINVAL;
    }

    //根据配置数,申请usb配置结构usb_host_config,并为用于存放配置结构的指针数据申请空间
    length = ncfg * sizeof(struct usb_host_config);
    dev->config = kzalloc(length, GFP_KERNEL);
    if (!dev->config)
        goto err2;

    length = ncfg * sizeof(char *);
    dev->rawdescriptors = kzalloc(length, GFP_KERNEL);
    if (!dev->rawdescriptors)
        goto err2;

    //申请用于暂时存放配置描述符的结构空间
    desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
    if (!desc)
        goto err2;

    result = 0;
    //依次获取usb设备的配置信息
    for (; cfgno < ncfg; cfgno++) {
    
    
        /* We grab just the first descriptor so we know how long
         * the whole configuration is */
        result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
            desc, USB_DT_CONFIG_SIZE);
        if (result < 0) {
    
    
            dev_err(ddev, "unable to read config index %d "
                "descriptor/%s: %d\n", cfgno, "start", result);
            if (result != -EPIPE)
                goto err;
            dev_err(ddev, "chopping to %d config(s)\n", cfgno);
            dev->descriptor.bNumConfigurations = cfgno;
            break;
        } else if (result < 4) {
    
    
            dev_err(ddev, "config index %d descriptor too short "
                "(expected %i, got %i)\n", cfgno,
                USB_DT_CONFIG_SIZE, result);
            result = -EINVAL;
            goto err;
        }
        length = max((int) le16_to_cpu(desc->wTotalLength),
            USB_DT_CONFIG_SIZE);//得到wTotallLength

        /* Now that we know the length, get the whole thing */ 
        //根据得到的wTotallLength,申请用于存放这些信息的内存空间
        bigbuffer = kmalloc(length, GFP_KERNEL);
        if (!bigbuffer) {
    
    
            result = -ENOMEM;
            goto err;
        }

        if (dev->quirks & USB_QUIRK_DELAY_INIT)
            msleep(100);

        //根据得到的wTotallLength,去获取完整的配置,接口,端口等描述符信息,把这些信息存放到bigbuffer里
        result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
            bigbuffer, length);
        if (result < 0) {
    
    
            dev_err(ddev, "unable to read config index %d "
                "descriptor/%s\n", cfgno, "all");
            kfree(bigbuffer);
            goto err;
        }
        if (result < length) {
    
    
            dev_warn(ddev, "config index %d descriptor too short "
                "(expected %i, got %i)\n", cfgno, length, result);
            length = result;
        }

    //把用于存放配置等信息的地址保存在之前申请的rawdescriptors里
        dev->rawdescriptors[cfgno] = bigbuffer;

    //通过usb_parse_configuration函数,把获得的配置等信息按照类型进行分类 
        result = usb_parse_configuration(dev, cfgno,
            &dev->config[cfgno], bigbuffer, length);
        if (result < 0) {
    
    
            ++cfgno;
            goto err;
        }
    }
    result = 0;

err:
    kfree(desc);
    dev->descriptor.bNumConfigurations = cfgno;
err2:
    if (result == -ENOMEM)
        dev_err(ddev, "out of memory\n");
    return result;
}

This function is mainly divided into two parts. The first part is to obtain the device configuration, interface, and port information from the USB device side. First, it applies for storage space for this information. Then, like obtaining the device descriptor before, it first sends a 9-byte request to obtain the total length of the configuration, interface, port and other descriptors. Finally, based on the total length obtained To obtain the completed device configuration, interface, and port information; the second part is to classify the obtained information according to categories and store it in the corresponding structure;

Through the usb_parse_configuration function, the obtained configuration and other information is classified according to type.

static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
    struct usb_host_config *config, unsigned char *buffer, int size)
{
    
    
    struct device *ddev = &dev->dev;
    unsigned char *buffer0 = buffer;
    int cfgno;
    int nintf, nintf_orig;
    int i, j, n;
    struct usb_interface_cache *intfc;
    unsigned char *buffer2;
    int size2;
    struct usb_descriptor_header *header;
    int len, retval;
    u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
    unsigned iad_num = 0;

    memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
    if (config->desc.bDescriptorType != USB_DT_CONFIG ||
        config->desc.bLength < USB_DT_CONFIG_SIZE ||
        config->desc.bLength > size) {
    
    
        dev_err(ddev, "invalid descriptor for config index %d: "
            "type = 0x%X, length = %d\n", cfgidx,
            config->desc.bDescriptorType, config->desc.bLength);
        return -EINVAL;
    }
    cfgno = config->desc.bConfigurationValue;

    buffer += config->desc.bLength;
    size -= config->desc.bLength;

    nintf = nintf_orig = config->desc.bNumInterfaces;
    if (nintf > USB_MAXINTERFACES) {
    
    
        dev_warn(ddev, "config %d has too many interfaces: %d, "
            "using maximum allowed: %d\n",
            cfgno, nintf, USB_MAXINTERFACES);
        nintf = USB_MAXINTERFACES;
    }

    /* Go through the descriptors, checking their length and counting the
     * number of altsettings for each interface */
    n = 0;
    for ((buffer2 = buffer, size2 = size);
          size2 > 0;
         (buffer2 += header->bLength, size2 -= header->bLength)) {
    
    

        if (size2 < sizeof(struct usb_descriptor_header)) {
    
    
            dev_warn(ddev, "config %d descriptor has %d excess "
                "byte%s, ignoring\n",
                cfgno, size2, plural(size2));
            break;
        }

        header = (struct usb_descriptor_header *) buffer2;
        if ((header->bLength > size2) || (header->bLength < 2)) {
    
    
            dev_warn(ddev, "config %d has an invalid descriptor "
                "of length %d, skipping remainder of the config\n",
                cfgno, header->bLength);
            break;
        }

        if (header->bDescriptorType == USB_DT_INTERFACE) {
    
    
            struct usb_interface_descriptor *d;
            int inum;

            d = (struct usb_interface_descriptor *) header;
            if (d->bLength < USB_DT_INTERFACE_SIZE) {
    
    
                dev_warn(ddev, "config %d has an invalid "
                    "interface descriptor of length %d, "
                    "skipping\n", cfgno, d->bLength);
                continue;
            }

            inum = d->bInterfaceNumber;

            if ((dev->quirks & USB_QUIRK_HONOR_BNUMINTERFACES) &&
                n >= nintf_orig) {
    
    
                dev_warn(ddev, "config %d has more interface "
                    "descriptors, than it declares in "
                    "bNumInterfaces, ignoring interface "
                    "number: %d\n", cfgno, inum);
                continue;
            }

            if (inum >= nintf_orig)
                dev_warn(ddev, "config %d has an invalid "
                    "interface number: %d but max is %d\n",
                    cfgno, inum, nintf_orig - 1);

            /* Have we already encountered this interface?
             * Count its altsettings */
            for (i = 0; i < n; ++i) {
    
    
                if (inums[i] == inum)
                    break;
            }
            if (i < n) {
    
    
                if (nalts[i] < 255)
                    ++nalts[i];
            } else if (n < USB_MAXINTERFACES) {
    
    
                inums[n] = inum;
                nalts[n] = 1;
                ++n;
            }

        } else if (header->bDescriptorType ==
                USB_DT_INTERFACE_ASSOCIATION) {
    
    
            if (iad_num == USB_MAXIADS) {
    
    
                dev_warn(ddev, "found more Interface "
                           "Association Descriptors "
                           "than allocated for in "
                           "configuration %d\n", cfgno);
            } else {
    
    
                config->intf_assoc[iad_num] =
                    (struct usb_interface_assoc_descriptor
                    *)header;
                iad_num++;
            }

        } else if (header->bDescriptorType == USB_DT_DEVICE ||
                header->bDescriptorType == USB_DT_CONFIG)
            dev_warn(ddev, "config %d contains an unexpected "
                "descriptor of type 0x%X, skipping\n",
                cfgno, header->bDescriptorType);

    }   /* for ((buffer2 = buffer, size2 = size); ...) */
    size = buffer2 - buffer;
    config->desc.wTotalLength = cpu_to_le16(buffer2 - buffer0);

    if (n != nintf)
        dev_warn(ddev, "config %d has %d interface%s, different from "
            "the descriptor's value: %d\n",
            cfgno, n, plural(n), nintf_orig);
    else if (n == 0)
        dev_warn(ddev, "config %d has no interfaces?\n", cfgno);
    config->desc.bNumInterfaces = nintf = n;

    /* Check for missing interface numbers */
    for (i = 0; i < nintf; ++i) {
    
    
        for (j = 0; j < nintf; ++j) {
    
    
            if (inums[j] == i)
                break;
        }
        if (j >= nintf)
            dev_warn(ddev, "config %d has no interface number "
                "%d\n", cfgno, i);
    }

    /* Allocate the usb_interface_caches and altsetting arrays */
    for (i = 0; i < nintf; ++i) {
    
    
        j = nalts[i];
        if (j > USB_MAXALTSETTING) {
    
    
            dev_warn(ddev, "too many alternate settings for "
                "config %d interface %d: %d, "
                "using maximum allowed: %d\n",
                cfgno, inums[i], j, USB_MAXALTSETTING);
            nalts[i] = j = USB_MAXALTSETTING;
        }

        len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j;
        config->intf_cache[i] = intfc = kzalloc(len, GFP_KERNEL);
        if (!intfc)
            return -ENOMEM;
        kref_init(&intfc->ref);
    }

    /* FIXME: parse the BOS descriptor */

    /* Skip over any Class Specific or Vendor Specific descriptors;
     * find the first interface descriptor */
    config->extra = buffer;
    i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,
        USB_DT_INTERFACE, &n);
    config->extralen = i;
    if (n > 0)
        dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
            n, plural(n), "configuration");
    buffer += i;
    size -= i;

    /* Parse all the interface/altsetting descriptors */
    while (size > 0) {
    
    
        retval = usb_parse_interface(ddev, cfgno, config,
            buffer, size, inums, nalts);
        if (retval < 0)
            return retval;

        buffer += retval;
        size -= retval;
    }

    /* Check for missing altsettings */
    for (i = 0; i < nintf; ++i) {
    
    
        intfc = config->intf_cache[i];
        for (j = 0; j < intfc->num_altsetting; ++j) {
    
    
            for (n = 0; n < intfc->num_altsetting; ++n) {
    
    
                if (intfc->altsetting[n].desc.
                    bAlternateSetting == j)
                    break;
            }
            if (n >= intfc->num_altsetting)
                dev_warn(ddev, "config %d interface %d has no "
                    "altsetting %d\n", cfgno, inums[i], j);
        }
    }

    return 0;
}

Insert image description here
So far, the USB device enumeration has basically been completed. Next, the system will allocate interface drivers to each interface according to its type. The interface driver mentioned here is the program that the driver engineer needs to complete regarding the use of USB device functions. Of course, if the current port is connected to a hub, it will call hub_probe to configure the hub.

8. Linux USB driver architecture

The Linux kernel supports two main types of USB drivers: drivers on the host system and drivers on the device. The host's USB controller driver controls the USB device plugged into it, and the host's USB device driver controls how the device communicates with the host as a USB slave device. Since "USB device drivers" are easily confused, USB development The author created the term "USB gadget drivers" to describe the functionality of controlling USB devices connected to a computer.
Next, we will explain the USB host controller driver and device driver seen from the host side, and the device controller and gadget driver seen from the device side.

8.1 Linux USB driver hierarchy

8.1.1 Host-side and device-side USB drivers

The USB controllers on the host side and device side are called host controller (Host Controller) and USB device controller (UDC) respectively. There is only one host controller on each bus, which is responsible for coordinating the communication between the host and the device. The device cannot actively communicate with the host. Send any message. As shown in the figure below, in the Linux system, the USB driver can be viewed from two angles: the host side and the device side. From the host side, in the Linux system, the bottom layer of the USB driver is the USB host controller hardware, the USB host controller driver runs on it, and the USB core layer on the host controller (as long as it is dependent on The subsystems of the bus have several levels: host, core, device. Take the USB subsystem as an example, there are USB main controller, USB core, USB sub-device), and then the USB device driver layer (inserted in U disk, mouse, USB to serial port and other device drivers on the host machine). Therefore, the USB host controller driver and USB device driver must be implemented on the host side. The USB controller driver of the host controls the USB device inserted into it. The USB device driver of the host controls how the device communicates with the host as a USB slave device. USB The core provides programming interfaces for device drivers upwards and USB host controllers downwards by defining some data structures, macros and functions, maintains USB device information in the entire system, and completes device hot-plugging, bus data transmission control, etc.
![Insert image description here](https://img-blog.csdnimg.cn/3225a27af936429cb1a7e92c5038375d.pInsert image description here

The USB device side driver is divided into three levels: USB device controller (UDC), Gadget Function API and Gadget Function driver. The UDC driver directly accesses the hardware, controls the underlying communication between the USB device and the host, and provides callback functions for hardware-related operations to the upper layer. The Gadget Function API is a simple wrapper for the UDC driver callback function. The Gadget Function driver specifically controls USB device functions. The implementation enables the device to show characteristics such as "network connection", "printer" or "USB Mass Storage". It uses the Gadget Function API to control UDC to implement the above functions.
The key to writing gadgets is to understand the connections and architectural levels between UDC, gadget, and composite. In practical applications, gadgets do not need to be written by us. What we need to write ourselves are the composite layer and the ground-to-UDC layer. Modifications. Composite means composite in English. Writing USB gadget layer device drivers is integrated together and registered through the unified function usb_composite_register. They have different functions and are miscellaneous, so they are called composite layers.
USB driver overall framework:
Insert image description here

8.1.2 Devices, configurations, interfaces, endpoints

The logical organization of USB devices includes four levels: device, configuration, interface and endpoint. Each USB device provides different levels of configuration information and can contain one or more configurations. Different configurations make the device exhibit different behavior. Function combination and configuration are composed of multiple interfaces. The interface is composed of multiple endpoints, which represents a basic function and is the object controlled by the USB device driver. The following figure shows the relationship between USB devices, configurations, interfaces and endpoints.
Endpoints are the most basic form of USB. Each USB device interface appears to the host as a collection of endpoints. The host can communicate with the device only through endpoints to use the device's functionality. In the USB system, each endpoint has a unique address, and each endpoint has certain attributes, including transmission mode, bus access frequency, bandwidth, endpoint number, and maximum capacity of data packets. A USB endpoint can only carry data in one direction, from the host to the device (called an output endpoint) or from the device to the host (called an input endpoint), so the endpoint can be regarded as a one-way pipe. Endpoint 0 is usually the control endpoint and is used for device initialization parameters etc. As long as the device is connected to USB and powered on, endpoint 0 can be accessed. Endpoints 1, 2, etc. are generally used as data endpoints to store data between the host and the device.
Insert image description here

  1. Devices typically have one or more configurations;
  2. A configuration usually has one or more interfaces;
  3. The interface has 0 or more endpoints;

USB is a universal bus with unified ports. But there are various USB devices, such as USB mouse, USB keyboard, USB flash drive, etc. So how does the USB host recognize different devices? This depends on the descriptor. USB descriptors mainly include device descriptors, configuration descriptors, interface descriptors, endpoint descriptors, string descriptors, HID descriptors, report descriptors, etc.
A USB device has a device descriptor. The device descriptor determines how many configurations the device has. Each configuration description corresponds to a configuration descriptor; and the configuration descriptor defines how many interfaces there are in the configuration. Each configuration descriptor defines how many interfaces there are in the configuration. Each interface has a corresponding interface descriptor; the interface descriptor defines how many endpoints the interface has, and each endpoint corresponds to an endpoint descriptor; the endpoint descriptor defines the size, type, etc. of the endpoint. From this we can see that the relationship between USB descriptors is layer by layer. The top layer is the device descriptor, the following is the configuration descriptor, the following is the interface descriptor, and the following is the endpoint descriptor. When obtaining the descriptor, first obtain the device descriptor, and then obtain the configuration descriptor. According to the length of the configuration set in the configuration descriptor, the configuration descriptor, interface descriptor, and endpoint descriptor are read back together in sequence. It may also include obtaining the device serial number, manufacturer string, product string, etc.

8.1.2.1 Device descriptor

Each USB device must have only one device descriptor (define the device descriptor in the program). The USB protocol defines the device descriptor as follows:
Insert image description here
Description:
1) bcdUSB is the USB protocol version number used by the device, 2 bytes in length. For example, you can take version numbers such as 2.0 or 1.1. It is important to note that the protocol stipulates the use of BCD codes to represent version numbers. For example, the USB2.0 protocol is 0x0200, and the USB1.1 protocol is 0x0110. When comparing it with the USB protocol analyzer, it should be noted that the USB protocol uses a little-endian structure, that is, the low byte comes first. For example, the USB2.0 protocol is split into two bytes, which is 0x00 0x02.

2) bDeviceClass is the class code used by the device (XX class interface descriptor code). Commonly used classes are as follows (C macro definition according to the protocol):
//HID device class interface descriptor code
#define HID_CLASS 0x03

//Audio class interface descriptor code
#define Audio_CLASS 0x01

//Video class interface descriptor code
#define Vedio_CLASS 0x0E

//Large-capacity device class interface descriptor code
MASS_STORAGE_CLASS 0x08

//Miscellaneous class or mixed class interface descriptor code
#define MISC_CLASS 0xEF

//Manufacturer-defined device class interface descriptor code
#define CUSTOM_CLASS 0xFF

//Specific application class interface descriptor code
#define DFU_DEVICE_CLASS 0xFE

3) The subclass code used by bDeviceSubClass device. When the subclass code is neither 0 nor 0xFF, the subclass code must be assigned according to the protocol. When the class code is 0, the subclass code must also be 0.

4) bDeviceProtocol is the protocol used by the device. The protocol code is specified by the USB protocol.

5) The maximum packet length of endpoint 0, the value can be: 8, 16, 32, 64 bytes. Note that the corresponding hexadecimal numbers are: 0x08, 0x10, 0x20, 0x40 (analyze the data in the source code and protocol analyzer, pay attention to conversion).

6) Regarding the manufacturer ID (2Byte), you can set a value at will during development. To actually make products, you must use your company's ID (apply to the USB Association) to avoid infringement. For the inserted device, the host relies on the manufacturer ID number, product ID number, and product serial number to install the driver.

7) Product ID is defined by the manufacturer itself, which is relatively free.

8) bcdDevice device version number. For the same product, after upgrade (such as firmware modification, new functions), you can distinguish it by modifying the device version number.

9) iManufacturer is the index value describing the manufacturer string. If set to 0, it means that the USB device does not have a manufacturer string. When the host obtains the manufacturer string separately, in the standard request packet issued, the first byte [low byte] of the wValue field is the index value of the manufacturer string, and the high byte is the descriptor type (character String descriptor 0x03).
The manufacturer string is a string of ordinary strings. In the device descriptor, there are three non-zero index values: the index value of the manufacturer string is 1; the index value of the product string is 2; the index value of the product serial number string is 3. After receiving the string descriptor request from the host, the device returns the corresponding string data to the host based on the index value.
Therefore, if the packet requested by the host string descriptor is parsed, if wValue=0x0301, it means that the host requested to obtain the manufacturer string.

10) iProduct is the index value of the string describing the product. Likewise, if set to 0, it means that the USB device does not have a product string. When you plug in the device for the first time, it prompts that new hardware has been found and displays the name of the device. In fact, the information displayed here is obtained from the product string. Experiment: You can modify the product string, compile the firmware, burn it, and insert the device, you can see the prompt information.
Similarly, when the host requests a product string, the value of the wValue field should be: 0x0302

11) iSerialNumber is the index value of the device’s serial number string. Similarly, if set to 0, it means that the USB device does not have a device serial number. It is best to specify a unique serial number for each product, because it is possible that the host will combine the product serial number with VID and PID to distinguish the device and load the corresponding driver. Similarly, when the host requests a serial number string, the value of the wValue field should be: 0x0303.

8.1.2.2 Configuration descriptor

A USB setting has at least one configuration descriptor. The standard configuration descriptor structure is as follows:
Insert image description here
Description:
1) wTotalLength is the total length of the entire configuration descriptor set. The configuration descriptor set includes: the length of the configuration descriptor itself, the interface descriptor, Endpoint descriptors, class special descriptors, etc.
2) bNumInterfaces is the number of interfaces supported by the device. Generally speaking, devices with a single function have only one interface (such as mouse, keyboard), while composite devices have multiple interfaces (such as audio devices, usually HID + UAC).
3) bConfigurationValue: A USB device can have many configurations. bConfigurationValue is the identification (ID) of each configuration. When making a configuration request, the host will send a configuration value. If the bConfigurationValue of a configuration matches the configuration value requested by the host, it means that the configuration is activated, and the USB device Just use this configuration. (The host decides which configuration the device uses)
4) iConfiguration is 0, which means there is no string to describe the configuration descriptor.
5) bmAttributes: The size is one byte, and different bits represent different attributes.
6) bMaxPower: size is 1 byte. Indicates the maximum current the device draws from the bus, in unit 2mA. For example, if a current of 200mA is required, the value of this byte is 100 (100 x 2mA = 200mA).

In fact, in standard requests for configuration descriptors, sometimes the wLength item is larger than the actual configuration descriptor length (9 bytes), such as 255. The effect is that when the host sends a Get_Descriptor_Configuration request, the device will return the interface descriptor, endpoint descriptor and other subsequent descriptors to the host, and the host will judge what is sent based on the flag in the descriptor header. kind of descriptor.

8.1.2.3 Interface descriptor

The configuration descriptor contains one or more interface descriptors. The "interface" here does not refer to a physical interface. It is easier to understand here to call it a "function". For example, a device has both a recording function and a If it has the function of a speaker, the device has at least two "interfaces".
The interface descriptor cannot be returned alone and must be returned together with the configuration descriptor (after the Host requests the configuration descriptor set, the Device returns a bunch of data).
Insert image description here
Description:
1) bInterfaceNumber: When a configuration has multiple interfaces, each interface has a different number. A configured interface is numbered incrementally starting from 0. Note here, check how many interfaces are supported in the configuration descriptor (this field: bNumInterfaces).
2) bAlternateSetting: Alternate port number, also starting from 0. This field is generally rarely used (this field must be used when developing UAC), and can be set to 0. Alternate: Alternate.
3) bNumEndpoints: is the number of endpoints used by the interface, excluding the 0 endpoint. If this field is set to 0, it means there are no non-zero endpoints.
4) bInterfaceClass, bInterfaceSubClass, bInterfaceProtocol: respectively the class, subclass, and protocol used by the interface, and the corresponding code (number) is defined by the USB protocol. Very similar to the meaning in the device descriptor, the device descriptor also has three similar fields (if you want to develop your own host computer, these three fields are set to 0xFF).
5) iInterface: If set to 0, it means there is no string.

8.1.2.4 Endpoint descriptor

The endpoint is a logical interface for data transmission between the device and the host. Except for the configured endpoint 0 (control endpoint, generally a device has only one control endpoint), which is a bidirectional port, the others are all unidirectional. The endpoint descriptor describes the data transmission type, transmission direction, data packet size, endpoint number (also called endpoint address), etc. Endpoint 0 does not have a dedicated descriptor, but its maximum packet length is defined in the device descriptor. The host sends commands to the device through this endpoint, obtains information about various descriptors of the device, and configures the device through it.
The endpoint descriptor cannot be returned alone and must be returned together with the configuration descriptor (after the Host requests the configuration descriptor set, the Device returns a bunch of data).
Insert image description here
Description:
1) bEndpointAddress: size 1 byte, the address of the endpoint.
The highest bit, which is the seventh bit D7: indicates the transmission direction of the endpoint. 1 represents input (like the first letter of Input) 0 represents output (like the first letter of Output)

  • D6~D4: reserved bits, default is 0.
  • D3~D0: are the endpoint numbers

2) bmAttributes are the attributes of the endpoint.

  • The lowest two bits D1~D0: indicate the transmission type of the endpoint. 0 is control transmission; 1 is isochronous transmission; 2 is batch transmission; 3 is interrupt transmission. Two digits, there are four situations: 00 01 10 11
  • If it is a non-isochronous transmission (one of control transmission, batch transmission, and interrupt transmission), then D7~D2 are all 0
  • If the endpoint transmits isochronously, then: D3 D2 indicates the type of synchronization: 0 is no synchronization, 1 is asynchronous, 2 is adaptation, and 3 is synchronization. D5 D4 indicates the purpose: 0 is the data endpoint, 1 is the feedback endpoint, 2 is the data endpoint that implies feedback, and 3 is the reserved value. D7~D6: Reserved, set to 0.

3) wMaxPacketSize: size 2 bytes, indicating the maximum packet length (unit: bytes) transmitted by the endpoint each time. Regarding the maximum packet length of the endpoint, two points need to be noted. One is the upper limit of the maximum packet length stipulated by the USB protocol. The other is that the maximum packet length of the endpoint needs to be specified based on the actual development situation (for example: when developing UAC, the maximum packet length of the endpoint It must be calculated and set based on the sampling rate, number of channels, etc., otherwise the PC will not be able to resample correctly and the sound will be distorted).

4) bInterval: Indicates the time of endpoint query. For interrupt endpoints and synchronous transmission, it indicates the polling interval of the endpoint, while for block transmission, this field is meaningless (when introducing specific class devices, this field will be used as a key parsing field).

8.1.2.5 String descriptor

After obtaining the configuration descriptor set, the host will issue a request to obtain the language ID (index value is 0) and a request to obtain the string descriptor. In the USB protocol, string descriptors are optional. In the device descriptor, three non-zero index values ​​are applied for:
1: It is the index value of the manufacturer string
2: It is the index value of the product string
3: It is the index value of the product serial number
The host obtains the corresponding value through the index value string data. An index value of 0 means that the language ID string is obtained. The structure of the string descriptor is very simple, as follows:
First, the structure of the language ID descriptor:
Insert image description here
The structure of the string descriptor (product + manufacturer + product serial number):
Insert image description here
Description:
1) Language ID, only one type of American English is used , namely 0x0409.
2) The bString field uses a UNICODE encoded string, using two bytes to represent a character.
3) If iManufacturer, iProduct, and iSerialNumber in the device descriptor are all set to 0, the host will not issue a request for the device string descriptor (debugging). Therefore, during the development process, the string descriptor is not necessary. However, it is necessary for product research and development.

8.1.2.6 Relationship between various descriptors

1) A device has only one device descriptor.
2) A device descriptor can contain multiple configuration descriptors.
3) A configuration descriptor can contain multiple interface descriptors.
4) An interface descriptor can contain multiple endpoint descriptors.
The inclusion relationship between descriptors is as follows (understood from the perspective of a collection):
Insert image description here

8.2 USB host controller driver

The interface between the USB device and the host is the host controller. One host can support multiple host controllers, such as those belonging to different manufacturers. So what does the USB host controller itself do? Controller: used for control. Control what? Control all USB device communication.
The CPU assigns the things to be done to the host controller, and then does whatever it wants to do. The host controller completes the rest of the things for it, and then notifies the CPU when the things are done. Otherwise, it would be unrealistic for the CPU to focus on every device and do everything.
What is the main job of the controller? Throw the numbers out and get the numbers back. Data should never be processed secretly. The host controller controls the transmission of packets on the bus, using 1ms or 125us frames. At the beginning of each frame, the host controller generates a Start of Frame packet (SOF: Start of Frame). The SOF packet is used to synchronize the start of the frame and track the number of frames. Packets are transmitted in frames, either from Host to Device (out transaction) or from Device to Host (in transaction). The transfer is always initiated by the Host (polling transfer), so there can only be one Host per USB bus. Each packet transmission has a status phase (except for synchronous transmission), in which the data receiver can return ACK (acknowledgment received), NAK (retry), STALL (error condition) or nothing (garbled data phase, The device is unavailable or disconnected).

USB host controllers include:

  1. UHCI : Universal Host Controller Interface (Universal Host Controller Interface, USB1.0/1.1), incompatible with OHCI. UHCI needs to do more at the software driver level and it needs to be more complicated.
  2. OHCI : Open Host Controller Interface (Open Host Control Interface, USB1.0/1.1), not only for USB, but also supports other interfaces, such as supporting Apple's Firewire (IEEE 1394) interface. Compared with UHCI, OHCI's hardware is complex and many things are left to the hardware, so it is relatively simple to implement the corresponding software-driven tasks. Mainly used for non-x86 USB, such as USB master control of expansion cards and embedded development boards.
  3. EHCI : Enhanced Host Controller Interface ("Enhanced Host Control Interface" for USB2.0 high-speed devices) is an Intel-led USB2.0 interface standard. EHCI only provides the high-speed functionality of USB2.0, and relies on UHCI or OHCI to provide support for full-speed or low-speed devices.
  4. xHCI : eXtensible Host Controller Interface is the latest USB3.0 interface standard. It has greatly improved over the previous three in terms of speed, energy saving, virtualization, etc. xHCI supports USB devices of all speeds (USB 3.0 SuperSpeed; USB 2.0 Low-, Full-, and High-speed; USB 1.1 Low- and Full-speed). The purpose of xHCI is to replace the previous three types.

The USB host controller driver enables the host controller to work and realize its potential. Let the controller send and receive data. The host controller mainly includes the following steps:

  1. Organize the structure according to the requirements of the host controller
  2. Place the structure in the right place at the right time
  3. trigger
  4. Wait for completion signal

8.2.1 Host controller driver

In the Linux kernel, the usb_hcd structure is used to describe the USB host controller driver, which contains the "housework" information of the USB host controller, hardware resources, status description and hc_driver used to operate the host controller, etc.

struct usb_hcd
{
    
    
          /* 管理“家务” */
          struct usb_bus self;
          const char *product_desc; /* 产品/厂商字符串 */
          char irq_descr[24]; /* 驱动 + 总线 # */
         
          struct timer_list rh_timer; /* 根Hub轮询 */
          struct urb *status_urb; /* 目前的状态urb */

          /* 硬件信息/状态 */
          const struct hc_driver *driver; /* 硬件特定的钩子函数 */

          /* 需要维护的标志 */
          unsigned long flags;
          #define HCD_FLAG_HW_ACCESSIBLE  0x00000001
          #define HCD_FLAG_SAW_IRQ        0x00000002

          unsigned rh_registered: 1; /* 根Hub注册? */

          /* 下一个标志的采用只是“权益之计”,当所有HCDs支持新的根Hub轮询机制后将移除 */
          unsigned uses_new_polling: 1;
          unsigned poll_rh: 1; /* 轮询根Hub状态? */
          unsigned poll_pending: 1; /* 状态已经改变? */

          int irq; /* 被分配的irq */
          void _ _iomem *regs; /* 设备内存和I/O */
          u64 rsrc_start; /* 内存和I/O资源开始位置 */
          u64 rsrc_len; /* 内存和I/O资源长度 */
          unsigned power_budget; /* mA, 0 = 无限制 */

          #define HCD_BUFFER_POOLS        4
          struct dma_pool *pool[HCD_BUFFER_POOLS];

          int state;
          #define _ _ACTIVE                0x01
          #define _ _SUSPEND               0x04
          #define _ _TRANSIENT             0x80

          #define HC_STATE_HALT           0
          #define HC_STATE_RUNNING        (_ _ACTIVE)
          #define HC_STATE_QUIESCING   (_ _SUSPEND|_ _TRANSIENT|_ _ACTIVE)
          #define HC_STATE_RESUMING       (_ _SUSPEND|_ _TRANSIENT)
          #define HC_STATE_SUSPENDED      (_ _SUSPEND)

          #define HC_IS_RUNNING(state) ((state) & _ _ACTIVE)
          #define HC_IS_SUSPENDED(state) ((state) & _ _SUSPEND)
          /* 主机控制器驱动的私有数据 */
         unsigned long hcd_priv[0]
                           _ _attribute_ _((aligned(sizeof(unsigned long))));
};

The hc_driver member in the usb_hcd structure is very important. It contains specific hook functions for operating the host controller, namely "hw-specific hooks"

struct hc_driver
{
    
    
          const char *description; /* "ehci-hcd" 等 */
          const char *product_desc; /* 产品/厂商字符串 */
          size_t hcd_priv_size; /* 私有数据的大小 */

          /* 中断处理函数 */
          irqreturn_t(*irq)(struct usb_hcd *hcd, struct pt_regs *regs);

          int flags;
          #define HCD_MEMORY      0x0001      /* HC寄存器使用的内存和I/O */
          #define HCD_USB11       0x0010          /* USB 1.1 */
          #define HCD_USB2        0x0020          /* USB 2.0 */
 
          /* 被调用以初始化HCD和根Hub */
          int(*reset)(struct usb_hcd *hcd);
          int(*start)(struct usb_hcd *hcd);
 
          /* 挂起Hub后,进入D3(etc)前被调用 */
          int(*suspend)(struct usb_hcd *hcd, pm_message_t message);
 
          /* 在进入D0(etc)后,恢复Hub前调用 */
          int(*resume)(struct usb_hcd *hcd);
 
          /* 使HCD停止写内存和进行I/O操作 */
          void(*stop)(struct usb_hcd *hcd);
 
         /* 返回目前的帧数 */
         int(*get_frame_number)(struct usb_hcd *hcd);
 
         /* 管理I/O请求和设备状态 */
         int(*urb_enqueue)(struct usb_hcd *hcd, struct usb_host_endpoint *ep, struct
         urb *urb, gfp_t mem_flags);
         int(*urb_dequeue)(struct usb_hcd *hcd, struct urb *urb);
 
         /* 释放endpoint资源 */
         void(*endpoint_disable)(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
 
        /* 根Hub支持 */
        int(*hub_status_data)(struct usb_hcd *hcd, char *buf);
        int(*hub_control)(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
        char *buf, u16 wLength);
        int(*bus_suspend)(struct usb_hcd*);
        int(*bus_resume)(struct usb_hcd*);
        int(*start_port_reset)(struct usb_hcd *, unsigned port_num);
        void(*hub_irq_enable)(struct usb_hcd*);
 };

In the Linux kernel, use the following function to create HCD:
struct usb_hcd *usb_creat_hcd(const struct hc_driver *driver, struct device *dev, char *bus_name);
use the following function to add and remove HCD:
int usb_add_hcd(struct usb_hcd *hcd , unsigned int irqnum, unsigned long irflags);
void usb_remove_hcd(struct usb_hcd *hcd);
The urb_enqueue() function is very critical. In fact, after the upper layer submits a usb request through usb_submit_urb(), the function calls usb_hcd_submit_urb(), and finally calls To the driver member of usb_hcd (urb_enqueue of type hc_driver).

8.2.2 EHCI host controller

The EHCI host controller driver is responsible for converting the URB transmission request sent by usb_core into a format that can be recognized by HC and starting the HC transmission until the transmission is completed. Only this driver in the entire USB subsystem directly operates hardware registers. This layer also supports other different host controller drivers, such as UHCI, OHCI, etc.
The EHCI UCD driver is an instance of the HCD driver. It defines an ehci_hcd structure, which is usually used as the private data (hcd_priv) of the usb_hcd structure. The definition of this structure is located in drivers/usb/host/ehci.h. The usb host controller driver only needs to pay attention to the host directory and combine it with the core directory.

struct ehci_hcd {
    
                            /* one per controller */
        /* timing support */
        enum ehci_hrtimer_event        next_hrtimer_event;
        unsigned                enabled_hrtimer_events;
        ktime_t                        hr_timeouts[EHCI_HRTIMER_NUM_EVENTS];
        struct hrtimer                hrtimer;

        int                        PSS_poll_count;
        int                        ASS_poll_count;
        int                        died_poll_count;

        /* glue to PCI and HCD framework */
        struct ehci_caps __iomem *caps;
        struct ehci_regs __iomem *regs;
        struct ehci_dbg_port __iomem *debug;

        __u32                        hcs_params;        /* cached register copy */
        spinlock_t                lock;
        enum ehci_rh_state        rh_state;

        /* general schedule support */
        bool                        scanning:1;
        bool                        need_rescan:1;
        bool                        intr_unlinking:1;
        bool                        iaa_in_progress:1;
        bool                        async_unlinking:1;
        bool                        shutdown:1;
        struct ehci_qh                *qh_scan_next;

        /* async schedule support */
        struct ehci_qh                *async;
        struct ehci_qh                *dummy;                /* For AMD quirk use */
        struct list_head        async_unlink;
        struct list_head        async_idle;
        unsigned                async_unlink_cycle;
        unsigned                async_count;        /* async activity count */
        __hc32                        old_current;        /* Test for QH becoming */
        __hc32                        old_token;        /*  inactive during unlink */

        /* periodic schedule support */
#define        DEFAULT_I_TDPS                1024                /* some HCs can do less */
        unsigned                periodic_size;
        __hc32                        *periodic;        /* hw periodic table */
        dma_addr_t                periodic_dma;
        struct list_head        intr_qh_list;
        unsigned                i_thresh;        /* uframes HC might cache */

        union ehci_shadow        *pshadow;        /* mirror hw periodic table */
        struct list_head        intr_unlink_wait;
        struct list_head        intr_unlink;
        unsigned                intr_unlink_wait_cycle;
        unsigned                intr_unlink_cycle;
        unsigned                now_frame;        /* frame from HC hardware */
        unsigned                last_iso_frame;        /* last frame scanned for iso */
        unsigned                intr_count;        /* intr activity count */
        unsigned                isoc_count;        /* isoc activity count */
        unsigned                periodic_count;        /* periodic activity count */
        unsigned                uframe_periodic_max; /* max periodic time per uframe */


        /* list of itds & sitds completed while now_frame was still active */
        struct list_head        cached_itd_list;
        struct ehci_itd                *last_itd_to_free;
        struct list_head        cached_sitd_list;
        struct ehci_sitd        *last_sitd_to_free;

        /* per root hub port */
        unsigned long                reset_done[EHCI_MAX_ROOT_PORTS];//记录已经reset的port

        /* bit vectors (one bit per port) */
        unsigned long                bus_suspended;                /* which ports were
                        already suspended at the start of a bus suspend */
        unsigned long                companion_ports;        /* which ports are
                        dedicated to the companion controller */
        unsigned long                owned_ports;                /* which ports are
                        owned by the companion during a bus suspend */
        unsigned long                port_c_suspend;                /* which ports have
                        the change-suspend feature turned on */
        unsigned long                suspended_ports;        /* which ports are
                        suspended */
        unsigned long                resuming_ports;                /* which ports have
                        started to resume */

        /* per-HC memory pools (could be per-bus, but ...) */
        struct dma_pool                *qh_pool;        /* qh per active urb */
        struct dma_pool                *qtd_pool;        /* one or more per qh */
        struct dma_pool                *itd_pool;        /* itd per iso urb */
        struct dma_pool                *sitd_pool;        /* sitd per split iso urb */

        unsigned                random_frame;
        unsigned long                next_statechange;
        ktime_t                        last_periodic_enable;
        u32                        command;

        /* SILICON QUIRKS */
        unsigned                no_selective_suspend:1;
        unsigned                has_fsl_port_bug:1; /* FreeScale */
        unsigned                has_fsl_hs_errata:1;        /* Freescale HS quirk */
        unsigned                big_endian_mmio:1;
        unsigned                big_endian_desc:1;
        unsigned                big_endian_capbase:1;
        unsigned                has_amcc_usb23:1;
        unsigned                need_io_watchdog:1;
        unsigned                amd_pll_fix:1;
        unsigned                use_dummy_qh:1;        /* AMD Frame List table quirk*/
        unsigned                has_synopsys_hc_bug:1; /* Synopsys HC */
        unsigned                frame_index_bug:1; /* MosChip (AKA NetMos) */
        unsigned                need_oc_pp_cycle:1; /* MPC834X port power */
        unsigned                imx28_write_fix:1; /* For Freescale i.MX28 */

        /* required for usb32 quirk */
        #define OHCI_CTRL_HCFS          (3 << 6)
        #define OHCI_USB_OPER           (2 << 6)
        #define OHCI_USB_SUSPEND        (3 << 6)

        #define OHCI_HCCTRL_OFFSET      0x4
        #define OHCI_HCCTRL_LEN         0x4
        __hc32                        *ohci_hcctrl_reg;
        unsigned                has_hostpc:1;
        unsigned                has_tdi_phy_lpm:1;
        unsigned                has_ppcd:1; /* support per-port change bits */
        u8                        sbrn;                /* packed release number */

        /* irq statistics */
#ifdef EHCI_STATS
        struct ehci_stats        stats;
#        define COUNT(x) ((x)++)
#else
#        define COUNT(x)
#endif

        /* debug files */
#ifdef CONFIG_DYNAMIC_DEBUG
        struct dentry                *debug_dir;
#endif

        /* bandwidth usage */
#define EHCI_BANDWIDTH_SIZE        64
#define EHCI_BANDWIDTH_FRAMES        (EHCI_BANDWIDTH_SIZE >> 3)
        u8                        bandwidth[EHCI_BANDWIDTH_SIZE];
                                                /* us allocated per uframe */
        u8                        tt_budget[EHCI_BANDWIDTH_SIZE];
                                                /* us budgeted per uframe */
        struct list_head        tt_list;

        /* platform-specific data -- must come last */
        unsigned long                priv[0] __aligned(sizeof(s64));
};

The following inline functions can be used to achieve mutual conversion between usb_hcd and ehci_hcd:
struct ehci_hcd *hcd_to_ehci(struct usb_hcd *hcd);
struct usb_hcd *ehci_to_usb(const struct ehci_hcd *ehci);
getting ehci_hcd from usb_hcd only obtains private data, while getting ehci_hcd from ehci_hcd To obtain usb_hcd, obtain the structure pointer from the structure member through container_of.
Use the following function to initialize the EHCI host controller:
static int ehci_init(struct usb_hcd *hcd);
The following functions can be used to start, stop and reset the EHCI controller respectively:
static int ehci_run(struct usb_hcd *hcd);
static void ehci_stop(struct usb_hcd *hcd);
static int ehci_reset(struct ehci_hcd *hcd);
The above function is filled in an instance ehci_hc_driver of the hc_driver structure in the drivers/usb/host/ehci-hcd.c file.

static const struct hc_driver ehci_hc_driver = {
    
    
          ...
          .reset = ehci_reset,
          .start = ehci_run,
          .stop = ehci_stop,
          .shutdown = ehci_shutdown,
} 

drivers/usb/host/ehci-hcd.c implements most of the EHCI host driver work, just call
void ehci_init_driver(struct hc_driver *drv, const struct ehci_driver_overrides *over); to initialize hc_driver.

8.3 USB device driver

8.3.1 Overall structure of USB device driver

The USB device driver mentioned here refers to how to access the inserted USB device from the perspective of the USB host side, rather than the firmware program running inside the USB device itself. The Linux system implements several types of general USB device drivers (also called client drivers), which are divided into the following device categories:

  • Audio equipment category;
  • Communication equipment;
  • HID (human-machine interface) device class;
  • Display device class;
  • Mass storage device category;
  • Power equipment category;
  • Printing equipment category;
  • Hub device class;
    General Linux devices (such as USB flash drives, USB mice, USB keyboards, etc.) do not require engineers to write drivers. What engineers need to write are drivers for specific manufacturers and specific chips, and they can often refer to the ones already published in Driver templates provided in the kernel.
    The usb_driver structure is used in the Linux kernel to describe a USB device driver.
struct usb_driver {
    
    
        const char *name;
        int (*probe) (struct usb_interface *intf, const struct usb_device_id *id);
        void (*disconnect) (struct usb_interface *intf);
        int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code, void *buf);
        int (*suspend) (struct usb_interface *intf, pm_message_t message);
        int (*resume) (struct usb_interface *intf);
        int (*reset_resume)(struct usb_interface *intf);
        int (*pre_reset)(struct usb_interface *intf);
        int (*post_reset)(struct usb_interface *intf);
        const struct usb_device_id *id_table;
        struct usb_dynids dynids;
        struct usbdrv_wrap drvwrap;
        unsigned int no_dynamic_id:1;
        unsigned int supports_autosuspend:1;
        unsigned int disable_hub_initiated_lpm:1;
        unsigned int soft_unbind:1;
};

When writing a new USB device driver, the main work that should be done is the probe() and disconnect() functions, that is, the detection and disconnection functions. They are called when the device is inserted and unplugged respectively, and are used to initialize and release software and hardware resources. Registration and deregistration of usb_driver can be completed through the following two functions.
int usb_register(struct usb_driver *new_driver);
void usb_deregister(struct usb_driver *driver);
usb_driver itself only has the function of finding USB devices and managing the connection and disconnection of USB devices.

8.3.2 USB request block URB

8.3.2.1 URB structure

USB Request Block (URB) is the basic carrier and core data structure used in USB device drivers to describe communication with USB devices.

struct urb {
    
    
      /* 私有的:只能由USB 核心和主机控制器访问的字段 */
      struct     kref kref; /*urb 引用计数 */
      void     *hcpriv; /* 主机控制器私有数据 */
      atomic_t use_count; /* 并发传输计数 */
      u8 reject; /* 传输将失败*/
      int     unlink; /* unlink 错误码 */
      /* 公共的: 可以被驱动使用的字段 */
      struct     list_head urb_list; /* 链表头*/
      struct     usb_anchor *anchor;
      struct     usb_device *dev; /* 关联的USB 设备 */
      struct     usb_host_endpoint *ep;
      unsigned int     pipe; /* 管道信息 */
      int     status; /* URB 的当前状态 */
      unsigned int     transfer_flags; /* URB_SHORT_NOT_OK | ...*/
      void     *transfer_buffer; /* 发送数据到设备或从设备接收数据的缓冲区 */
      dma_addr_t transfer_dma; /*用来以DMA 方式向设备传输数据的缓冲区 */
      int     transfer_buffer_length;/*transfer_buffer 或transfer_dma 指向缓冲区的大小 */
       
      int     actual_length; /* URB 结束后,发送或接收数据的实际长度 */
      unsigned char     *setup_packet; /* 指向控制URB 的设置数据包的指针*/
      dma_addr_t setup_dma; /*控制URB 的设置数据包的DMA 缓冲区*/
      int     start_frame; /*等时传输中用于设置或返回初始帧*/
      int     number_of_packets; /*等时传输中等时缓冲区数量 */
      int     interval; /* URB 被轮询到的时间间隔(对中断和等时urb 有效) */
      int     error_count; /* 等时传输错误数量 */
      void     *context; /* completion 函数上下文 */
      usb_complete_t complete; /* 当URB 被完全传输或发生错误时,被调用 */
      /*单个URB 一次可定义多个等时传输时,描述各个等时传输 */
      struct     usb_iso_packet_descriptor iso_frame_desc[0];
};

8.3.2.2 URB processing flow

Each endpoint in a USB device handles an urb queue. The typical life cycle of an urb before the queue is cleared is as follows.
1) Created by a USB device driver (create URB).
The function to create the urb structure is:
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);
iso_packets is the number of isochronous data packets that this urb should contain. If it is 0, it means no isochronous data packets are created.
The mem_flags parameter is the flag for memory allocation, and has the same meaning as the allocation flag parameter of the kmalloc() function. If the allocation is successful, this function returns a pointer to the urb structure, otherwise it returns 0.
The urb structure cannot be created statically in the driver because this may destroy the reference counting method used by the USB core for urb.
The "inverse function" of usb_alloc_urb() is:
void usb_free_urb(struct urb *urb);
This function is used to release the urb structure allocated by usb_alloc_urb().
2) Initialization, assigned to a specific endpoint (populated URB) of a specific USB device.
For interrupt urb, use the usb_fill_int_urb() function to initialize the urb as follows:
void usb_fill_int_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer,
int buffer_length, usb_complete_t complete, void *context, int interval );
The urb parameter points to the pointer of the urb to be initialized; dev points to the USB device to which this urb is to be sent; pipe is the specific endpoint of the USB device to which this urb is to be sent; transfer_buffer points to the buffer for sending or receiving data. Pointer, like urb, cannot be a static buffer and must be allocated using kmalloc(); buffer_length is the size of the buffer pointed by the transfer_buffer pointer; the complete pointer points to the completion handler function that is called when the urb is completed; context is The "context" of the completion handler; interval is the interval at which this urb should be scheduled.
3) Submitted to the USB core (submitted URB) by the USB device driver.
After completing the creation and initialization of urb, urb can be submitted to the USB core through the usb_submit_urb() function, as shown below:
int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
The urb parameter is a pointer to the urb, and the mem_flags parameter The same meaning as the parameter passed to the kmalloc() function, it is used to tell the USB core how to allocate the memory buffer at this time. After submitting the urb to the USB core, do not access any members in the urb until the completion function is called.
usb_submit_urb() can be called in both atomic context and process context. The mem_flags variable needs to be set accordingly according to the calling environment, as shown below.
GFP_ATOMIC: In the interrupt processing function, bottom half, tasklet, timer processing function and urb completion function, when the caller holds a spin lock or read-write lock and when the driver changes the current→state to non-TASK_RUNNING, it should be Use this flag.
GFP_NOIO: This flag should be used in the block I/O and error handling paths of the storage device;
GFP_KERNEL: If there is no reason to use GFP_ATOMIC and GFP_NOIO, use GFP_KERNEL.
If the usb_submit_urb() call is successful, that is, the control of the urb is transferred to the USB core, the function returns 0; otherwise, an error number is returned.
4) Submit the USB host controller driver specified by the USB core.
5) Processed by the USB host controller, a transfer to the USB device is performed.
Steps 4) 5) are completed by the USB core and USB host controller and are not controlled by the USB device driver.
6) When the URB is completed, the USB host controller driver notifies the USB device driver (to process the URB).
7) Cancellation of URB.
If you want to cancel the previously submitted URB, you can use usb_unlink_urb to achieve it:
int usb_unlink_urb(struct urb *urb);

8.3.2.3 Simple batch and control URB

When submitting urb or canceling urb in the previous method, the program will not block and it is an asynchronous method. In addition to asynchronous mode, USB can also use synchronous mode to submit and cancel urb. Sometimes the USB driver just receives or sends some simple data to the USB device. At this time, there is no need to go through the entire process of urb creation, initialization, submission, and completion processing. Instead, two simpler functions can be used. ,As follows.
(1) usb_bulk_msg() function
The usb_bulk_msg() function creates a USB bulk urb and sends it to a specific device. This function is synchronous and it waits for the urb to complete before returning. The prototype of the usb_bulk_msg() function is:
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length,
int timeout);
The usb_dev parameter is the pointer of the USB device to which the bulk message is to be sent, and the pipe is The endpoint of the USB device to which the batch message is to be sent. The data parameter is a pointer to the data buffer to be sent or received. The len parameter is the length of the buffer pointed to by the data parameter. actual_length is used to return the actual bytes sent or received. Number, timeout is the sending timeout, in jiffies, 0 means waiting forever.
If the function call is successful, 0 is returned; otherwise, a negative error value is returned.
(2) usb_control_msg() function
The usb_control_msg() function is similar to the usb_bulk_msg() function, but it provides the driver with the ability to send and end USB control messages instead of bulk messages. The prototype of this function is:
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
__u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout);
dev points to the USB device to which the control message is sent, and pipe is the source of the control message. The endpoint of the USB device sent to, request is the USB request value of this control message, requesttype is the USB request type of this control message, value is the USB message value of this control message, index is the USB message index value of this control message, data Points to the data buffer to be sent or received, size is the size of the buffer pointed to by the data parameter, timeout is the sending timeout in jiffies, 0 means waiting forever.
The parameters request, requesttype, value and index directly correspond to the USB control messages defined in the USB specification.
If the function call is successful, the function returns the number of bytes sent to or received from the device; otherwise, a negative error value is returned.
Be particularly careful when using the usb_bulk_msg() and usb_control_msg() functions. Since they are synchronous, they cannot be used in an interrupt context and holding a spin lock. Moreover, this function cannot be canceled by any other function, so it is important that the driver's disconnect() function has enough information to judge and wait for the end of the call.

8.3.3 Probing and disconnecting functions

In the probe function probe() of the USB device driver usb_driver structure, the following work should be completed:

  • Detect the device's endpoint address, buffer size, and initialize any data structures that may be used to control the USB device.
  • Save the pointer of the initialized data structure to the interface device.
  • Register USB device.
    If it is a simple character device, you can call usb_register_dev(). The prototype of this function is:
    int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);
    In the disconnect() function of the USB device driver usb_driver structure, the following work should be completed :
  • Releases all resources allocated to the device.
  • Set the data pointer of the interface device to NULL.
  • Unregister the USB device.
    If it is a simple character device, you can call the "inverse function" of usb_register_dev(). The prototype of this function is:
    int usb_deregister_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);

8.3.4 USB skeleton program

The drivers/usb/usb-skeleton.c file in the open source code of the Linux kernel provides us with a most basic USB driver, the USB skeleton program. Although the specific USB device drivers vary widely, their skeleton remains the same.
First look at the usb_driver structure of the USB skeleton program, which is defined as follows:

static struct usb_driver skel_driver = {
    
    
        .name =                "skeleton",
        .probe =        skel_probe,
        .disconnect =        skel_disconnect,
        .suspend =        skel_suspend,
        .resume =        skel_resume,
        .pre_reset =        skel_pre_reset,
        .post_reset =        skel_post_reset,
        .id_table =        skel_table,
        .supports_autosuspend = 1,
};

In the above code, .id_table = skel_table defines the device list array skel_table[] supported by the driver. The code is as follows:

static const struct usb_device_id skel_table[] = {
    
    
        {
    
     USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
        {
    
     }                                        /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, skel_table);

MODULE_DEVICE_TABLE Detailed explanation of the function of macro definition -> MODULE_DEVICE_TABLE
The registration and deregistration of usb_driver occurs in the module loading and unloading functions of the USB skeleton program, and the usb_register() and usb_deregister() functions are called respectively.
These two functions are implemented using the shortcut macro module_usb_driver, which is in include/linux/usb.h:

#define usb_register(driver) \
        usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
 
void usb_deregister(struct usb_driver *driver)
{
    
    
        pr_info("%s: deregistering interface driver %s\n",
                        usbcore_name, driver->name);
        usb_remove_newid_files(driver);
        driver_unregister(&driver->drvwrap.driver);
        usb_free_dynids(driver);
}
EXPORT_SYMBOL_GPL(usb_deregister);
 
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{
      
       \
        return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{
      
       \
        __unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);
 
#define module_usb_driver(__usb_driver) \
        module_driver(__usb_driver, usb_register, \
       usb_deregister)

The module loading code of the USB skeleton program is as follows:

static struct usb_driver skel_driver = {
    
    
        .name =                "skeleton",
        .probe =        skel_probe,
        .disconnect =        skel_disconnect,
        .suspend =        skel_suspend,
        .resume =        skel_resume,
        .pre_reset =        skel_pre_reset,
        .post_reset =        skel_post_reset,
        .id_table =        skel_table,
        .supports_autosuspend = 1,
};

module_usb_driver(skel_driver);

Let’s start with the probe() function in the USB skeleton program. In the usb_probe() member function of usb_driver, we find the first batch input and batch output endpoint according to the usb_interface member, and store the endpoint address, buffer and other information as USB In the usb_skel structure defined by the skeleton program and pass the usb_skel instance pointer to usb_set_intfdata() as the private data of the USB interface, finally register the USB device. The code list is as follows:

static int skel_probe(struct usb_interface *interface,
                      const struct usb_device_id *id)
{
    
    
        ……
        /* allocate memory for our device state and initialize it */
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        ……
        dev->udev = usb_get_dev(interface_to_usbdev(interface));
        dev->interface = interface;
 
        /* set up the endpoint information */
        /* use only the first bulk-in and bulk-out endpoints */
        iface_desc = interface->cur_altsetting;
        for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
    
    
                endpoint = &iface_desc->endpoint[i].desc;
 
                if (!dev->bulk_in_endpointAddr &&
                    usb_endpoint_is_bulk_in(endpoint)) {
    
    
                        /* we found a bulk in endpoint */
                        buffer_size = usb_endpoint_maxp(endpoint);
                        dev->bulk_in_size = buffer_size;
                        dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
                        dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
                        if (!dev->bulk_in_buffer)
                                goto error;
                        dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
                        if (!dev->bulk_in_urb)
                                goto error;
                }
 
                if (!dev->bulk_out_endpointAddr &&
                    usb_endpoint_is_bulk_out(endpoint)) {
    
    
                        /* we found a bulk out endpoint */
                        dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
                }
        }
        if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
    
    
                dev_err(&interface->dev,
                        "Could not find both bulk-in and bulk-out endpoints\n");
                goto error;
        }
 
        /* save our data pointer in this interface device */
        usb_set_intfdata(interface, dev);
 
        /* we can register the device now, as it is ready */
        retval = usb_register_dev(interface, &skel_class);
        ……
        return 0;
        ……
}

The usb_skel structure can be regarded as a private data structure, and the code is defined as follows:

/* Structure to hold all of our device specific stuff */
struct usb_skel {
    
    
        struct usb_device        *udev;                        /* the usb device for this device */
        struct usb_interface        *interface;                /* the interface for this device */
        struct semaphore        limit_sem;                /* limiting the number of writes in progress */
        struct usb_anchor        submitted;                /* in case we need to retract our submissions */
        struct urb                *bulk_in_urb;                /* the urb to read data with */
        unsigned char           *bulk_in_buffer;        /* the buffer to receive data */
        size_t                        bulk_in_size;                /* the size of the receive buffer */
        size_t                        bulk_in_filled;                /* number of bytes in the buffer */
        size_t                        bulk_in_copied;                /* already copied to user space */
        __u8                        bulk_in_endpointAddr;        /* the address of the bulk in endpoint */
        __u8                        bulk_out_endpointAddr;        /* the address of the bulk out endpoint */
        int                        errors;                        /* the last request tanked */
        bool                        ongoing_read;                /* a read is going on */
        spinlock_t                err_lock;                /* lock for errors */
        struct kref                kref;
        struct mutex                io_mutex;                /* synchronize I/O with disconnect */
        wait_queue_head_t        bulk_in_wait;                /* to wait for an ongoing read */
};

The disconnect function will complete the opposite work of probe(), set the interface data to NULL, and log out the USB device. The code is as follows:

static void skel_disconnect(struct usb_interface *interface)
{
    
    
        struct usb_skel *dev;
        int minor = interface->minor;
 
        dev = usb_get_intfdata(interface);
        usb_set_intfdata(interface, NULL);
 
        /* give back our minor */
        usb_deregister_dev(interface, &skel_class);
 
        /* prevent more I/O from starting */
        mutex_lock(&dev->io_mutex);
        dev->interface = NULL;
        mutex_unlock(&dev->io_mutex);
 
        usb_kill_anchored_urbs(&dev->submitted);
 
        /* decrement our usage count */
        kref_put(&dev->kref, skel_delete);
 
        dev_info(&interface->dev, "USB Skeleton #%d now disconnected", minor);
}

The second parameter in usb_register_dev(interface, &skel_class) in the usb_probe() function contains the file_operation structure pointer of the character device, and the members of this structure are also another component of the USB character device. code show as below:

static const struct file_operations skel_fops = {
    
    
        .owner =        THIS_MODULE,
        .read =                skel_read,
        .write =        skel_write,
        .open =                skel_open,
        .release =        skel_release,
        .flush =        skel_flush,
        .llseek =        noop_llseek,
};

Since it is just a symbolic skeleton structure, the implementation of the open() member function is very simple. It obtains the USB interface through usb_find_interface() based on the usb_driver and minor device number, and then obtains the private data of the interface through usb_get_intfdata() and assigns it to file->private_data ,code show as below:

static int skel_open(struct inode *inode, struct file *file)
{
    
    
        struct usb_skel *dev;
        struct usb_interface *interface;
        int subminor;
        int retval = 0;
 
        subminor = iminor(inode);
 
        interface = usb_find_interface(&skel_driver, subminor);
        ……
        dev = usb_get_intfdata(interface);
        ……
        retval = usb_autopm_get_interface(interface);
        if (retval)
                goto exit;
 
        /* increment our usage count for the device */
        kref_get(&dev->kref);
 
        /* save our object in the file's private structure */
        file->private_data = dev;
 
exit:
        return retval;
}

Since no resources are requested in skel_open(), the skel_release() function only needs to reduce some reference counts.
The next thing to analyze is the read and write functions. As mentioned before, when accessing a USB device, the "central nerve" that runs through it is the URB structure.
The URB operations performed in the skel_write() function include URB allocation (calling usb_alloc_urb()), initialization (calling usb_fill_bluk_urb()) and submission (calling usb_submit_urb()). The code is as follows:

static ssize_t skel_write(struct file *file, const char *user_buffer,
                          size_t count, loff_t *ppos)
{
    
    
        struct usb_skel *dev;
        int retval = 0;
        struct urb *urb = NULL;
        char *buf = NULL;
        size_t writesize = min(count, (size_t)MAX_TRANSFER);
 
        dev = file->private_data;
        ……
        spin_lock_irq(&dev->err_lock);
        retval = dev->errors;
        ……
        spin_unlock_irq(&dev->err_lock);
        ……
        /* create a urb, and a buffer for it, and copy the data to the urb */
        urb = usb_alloc_urb(0, GFP_KERNEL);
        ……
        buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL,
                                 &urb->transfer_dma);
        ……
        if (copy_from_user(buf, user_buffer, writesize)) {
    
    
                retval = -EFAULT;
                goto error;
        }
 
        /* this lock makes sure we don't submit URBs to gone devices */
        mutex_lock(&dev->io_mutex);
        ……
        /* initialize the urb properly */
        usb_fill_bulk_urb(urb, dev->udev,
                          usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
                          buf, writesize, skel_write_bulk_callback, dev);
        urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
        usb_anchor_urb(urb, &dev->submitted);
 
        /* send the data out the bulk port */
        retval = usb_submit_urb(urb, GFP_KERNEL);
        mutex_unlock(&dev->io_mutex);
        ……
        usb_free_urb(urb);
 
        return writesize;
        ……
}

After the URB initiated in the write function ends, the completion function skel_write_bulk_callback() filled in the usb_fill_bulk_urb() function will be called to judge urb->status. The code is as follows:

static void skel_write_bulk_callback(struct urb *urb)
{
    
    
        struct usb_skel *dev;
 
        dev = urb->context;
 
        /* sync/async unlink faults aren't errors */
        if (urb->status) {
    
    
                if (!(urb->status == -ENOENT ||
                    urb->status == -ECONNRESET ||
                    urb->status == -ESHUTDOWN))
                        dev_err(&dev->interface->dev,
                                "%s - nonzero write bulk status received: %d\n",
                                __func__, urb->status);
 
                spin_lock(&dev->err_lock);
                dev->errors = urb->status;
                spin_unlock(&dev->err_lock);
        }
 
        /* free up our allocated buffer */
        usb_free_coherent(urb->dev, urb->transfer_buffer_length,
                          urb->transfer_buffer, urb->transfer_dma);
        up(&dev->limit_sem);
}

8.3.5 Example: USB keyboard driver

In Linux systems, the keyboard is recognized as a standard input device. For USB keyboards, the driver mainly consists of two parts: the member function of usb_driver (USB peripheral driver) and the input_event acquisition and reporting of the input device driver.
The module loading and unloading functions of the USB keyboard device driver register and unregister the usb_driver structure usb_kbd_driver corresponding to the USB keyboard respectively. The following code list is the definition of the module loading and unloading functions and the usb_driver structure.

static struct usb_device_id usb_kbd_id_table [] = {
    
    
     {
    
     USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
     USB_INTERFACE_PROTOCOL_KEYBOARD) },
     {
    
     } /* Terminating entry */
};

MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);

static struct usb_driver usb_kbd_driver = {
    
    
     .name = "usbkbd",
     .probe = usb_kbd_probe,
     .disconnect = usb_kbd_disconnect,
     .id_table = usb_kbd_id_table,
};

module_usb_driver(usb_kbd_driver);

In the probe() function of usb_driver, the input device will be initialized and registered, the interrupt URB and control URB used by the USB keyboard will be initialized, and the private data of the interface will be set, as shown in the following code list.

static int usb_kbd_probe(struct usb_interface *iface,
  const struct usb_device_id *id)
{
    
    
     struct usb_device *dev = interface_to_usbdev(iface);
     struct usb_host_interface *interface;
     struct usb_endpoint_descriptor *endpoint;
     struct usb_kbd *kbd;
     struct input_dev *input_dev; // 输入设备
     int i, pipe, maxp;
     int error = -ENOMEM;
    
     interface = iface->cur_altsetting;
     if (interface->desc.bNumEndpoints != 1)
         return -ENODEV;
    
     endpoint = &interface->endpoint[0].desc;
     if (!usb_endpoint_is_int_in(endpoint))
         return -ENODEV;
    
     pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
     maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
    
     kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL); // 分配内存
     input_dev = input_allocate_device(); // 动态初始化input_dev
     if (!kbd || !input_dev)
         goto fail1;
    
     /* 分配内存 */
     if (usb_kbd_alloc_mem(dev, kbd))
         goto fail2;
    
     kbd->usbdev = dev;
     kbd->dev = input_dev;
     spin_lock_init(&kbd->leds_lock);
    
     if (dev->manufacturer)
         strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
    
     if (dev->product) {
    
    
         if (dev->manufacturer)
             strlcat(kbd->name, " ", sizeof(kbd->name));
         strlcat(kbd->name, dev->product, sizeof(kbd->name));
     }
    
     if (!strlen(kbd->name))
         snprintf(kbd->name, sizeof(kbd->name),
      "USB HIDBP Keyboard %04x:%04x",
      le16_to_cpu(dev->descriptor.idVendor),
      le16_to_cpu(dev->descriptor.idProduct));
    
     usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
     strlcat(kbd->phys, "/input0", sizeof(kbd->phys));
     /* 输入设备初始化 */
     input_dev->name = kbd->name;
     input_dev->phys = kbd->phys;
     usb_to_input_id(dev, &input_dev->id);
     input_dev->dev.parent = &iface->dev;
    
     input_set_drvdata(input_dev, kbd);
    
     input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
     BIT_MASK(EV_REP);
     input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
     BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) |
     BIT_MASK(LED_KANA);
    
     for (i = 0; i < 255; i++)
         set_bit(usb_kbd_keycode[i], input_dev->keybit);
     clear_bit(0, input_dev->keybit);
    
     input_dev->event = usb_kbd_event;
     input_dev->open = usb_kbd_open;
     input_dev->close = usb_kbd_close;
     /* 中断urb初始化 */
     usb_fill_int_urb(kbd->irq, dev, pipe, kbd->new, (maxp > 8 ? 8 : maxp),
          usb_kbd_irq, kbd, endpoint->bInterval);
     kbd->irq->transfer_dma = kbd->new_dma;
     kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
    
     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);
     /* 控制urb初始化 */
     usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
          (void *) kbd->cr, kbd->leds, 1,
          usb_kbd_led, kbd);
     kbd->led->transfer_dma = kbd->leds_dma;
     kbd->led->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
     /* 注册输入设备 */
     error = input_register_device(kbd->dev);
     if (error)
         goto fail2;
    
     usb_set_intfdata(iface, kbd); /* 设置接口私有数据 */
     device_set_wakeup_enable(&dev->dev, 1);
     return 0;
    
fail2:  
     usb_kbd_free_mem(dev, kbd);
fail1:  
     input_free_device(input_dev);
     kfree(kbd);
     return error;
}

The disconnect function of usb_driver sets the interface private data to NULL, terminates the submitted URB, and logs out the input device, as shown in the following code list.

static void usb_kbd_disconnect(struct usb_interface *intf)
{
    
    
     struct usb_kbd *kbd = usb_get_intfdata (intf);
    
     usb_set_intfdata(intf, NULL); /* 设置接口私有数据为NULL */
     if (kbd) {
    
    
         usb_kill_urb(kbd->irq); /* 终止已提交的URB */
         input_unregister_device(kbd->dev); /* 注销输入设备 */
         usb_kill_urb(kbd->led); /* 终止已提交的URB */
         usb_kbd_free_mem(interface_to_usbdev(intf), kbd); /*释放内存*/
         kfree(kbd);
     }
}

The keyboard mainly relies on the interrupt transmission mode. In the completion function usb_kbd_irq() of the keyboard interrupt URB, key events are reported through input_report_key() and synchronization events are reported through input_sync(), as shown in the following code list.

static void usb_kbd_irq(struct urb *urb)
{
    
    
     struct usb_kbd *kbd = urb->context;
     int i;
    
     switch (urb->status) {
    
    
         case 0: /* success */
         break;
         case -ECONNRESET: /* unlink */
         case -ENOENT:
         case -ESHUTDOWN:
         return;
         /* -EPIPE:  should clear the halt */
         default: /* error */
         goto resubmit;
     }

     /* 报告按键事件 */
     for (i = 0; i < 8; i++)
         input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
    
     for (i = 2; i < 8; i++) {
    
    
         if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
    
    
             if (usb_kbd_keycode[kbd->old[i]])
                 input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
             else
                 hid_info(urb->dev, "Unknown key (scancode %#x) released.\n", kbd->old[i]);
         }
     
         if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
    
    
             if (usb_kbd_keycode[kbd->new[i]])
                 input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
             else
                 hid_info(urb->dev, "Unknown key (scancode %#x) pressed.\n", kbd->new[i]);
         }
     }
     /* 报告同步事件 */
     input_sync(kbd->dev);
     memcpy(kbd->old, kbd->new, 8);

resubmit:
     i = usb_submit_urb (urb, GFP_ATOMIC);
     if (i)
         hid_err(urb->dev, "can't resubmit intr, %s-%s/input0, status %d",
         kbd->usbdev->bus->bus_name,
         kbd->usbdev->devpath, i);
}

From the USB keyboard driver example, we can further see that the usb_driver itself only plays the role of a bus connection. The driver of the specific device type is still the main body of the work. For example, the keyboard is input and the USB serial port is tty. It only performs hardware access at the bottom of the device. , all interfaces related to URB (USB Request Block) are called. The existence of this USB core layer API-URB makes it unnecessary to care about the specific details of the underlying USB host controller. Therefore, the USB device driver becomes platform-independent. The same driver can be applied to different SoCs.

8.4 USB UDC and Gadget driver

8.4.1 Basic concepts of UDC (USB device controller) and gadget (device) driver

The USB device controller driver refers to the driver of the underlying hardware controller on the USB hardware device as a peripheral of other USB host controllers. The hardware and driver are responsible for attaching a USB device to a USB host controller. For example, when a certain When a mobile phone running a Linux system is used as a USB flash drive for a PC, the underlying USB controller in the mobile phone functions as a USB device controller. At this time, the UDC driver is running at the bottom layer. For the mobile phone to become a USB flash drive, it still needs to be on top of the UDC driver. Another driver, for USB mass storage, is the File Storage driver, called the Function driver. The USB device driver calls the USB core API, and the specific driver has nothing to do with the SoC; the function driver calls the general Gadget Function API, and the specific function driver has nothing to do with the SoC.
In order to distinguish it from the USB Device Driver concept of host-side device driver, the driver running in the peripheral device is called USB Gadget Driver. Among them, the host-side device driver is the master, and the device-side gadget driver is the slave or function driver.
The Gadget Driver is similar to the USB Host driver in that it uses a request queue to buffer I/O packets. These requests can be submitted and canceled. At the same time, the relationship between driver and device is established through bind and unbind.
Both UDC driver and Function driver are located in the drivers/usb/gadget directory of the kernel. For example, at91_udc.c, omap_udc.c, s3c2410_udc.c, etc. under drivers/usb/gadget/udc correspond to the UDC driver on the SoC platform, drivers/usb The f_serial.c, f_mass_storage.c, f_rndis.c and other files in the /gadget/function directory implement some Gadget functions. The important Function drivers are as follows.

  • Ethernet over USB: This driver simulates an Ethernet network port and supports multiple operating modes - CDC Ethernet (implementing the standard Communications Device Class "Ethernet Model" protocol), CDC Subset and RNDIS (Microsoft's variant implementation of CDC Ethernet).
  • File-Backed Storage Gadget: The most common U disk function implementation.
  • Serial Gadget: includes Generic Serial implementation (only Bulk-in/Bulk-out endpoint + ep0 required) and CDC ACM specification implementation.
  • Gadget MIDI (Music Equipment Digital Interface): Exposes the ALSA MIDI interface.
  • USB Video Class Gadget driver: Let the Linux system become the USB video capture source of another system.

In addition, the drivers/usb/gadget source code also implements a Gadget file system (GadgetFS), which exposes the Gadget API interface to the application layer to implement user space drivers at the application layer.

8.4.2 Key data structures and APIs

The USB device controller driver needs to care about several core data structures, including usb_gadget that describes a USB device controller, UDC operation usb_gadget_ops, usb_ep that describes an endpoint, and usb_ep_ops structure that describes endpoint operations. The UDC driver revolves around these data structures and their member functions. These key data structures are listed below, path: include/linux/usb/gadget.h.

struct usb_gadget {
    
    
        struct work_struct              work;
        /* readonly to gadget driver */
        const struct usb_gadget_ops     *ops;
        struct usb_ep                   *ep0;
        struct list_head                ep_list;        /* of usb_ep */
        enum usb_device_speed           speed;
        enum usb_device_speed           max_speed;
        enum usb_device_state           state;
        const char                      *name;
        struct device                   dev;
        unsigned                        out_epnum;
        unsigned                        in_epnum;

        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;
        bool                            remote_wakeup;
        u32                             xfer_isr_count;
        u8                              usb_core_id;
        bool                            l1_supported;
        bool                            bam2bam_func_enabled;
        u32                             extra_buf_alloc;
        int                             interrupt_num;
};

struct usb_ep {
    
    
        void                    *driver_data;
        const char              *name;
        const struct usb_ep_ops *ops;
        struct list_head        ep_list;
        unsigned                maxpacket:16;
        unsigned                maxpacket_limit:16;
        unsigned                max_streams:16;
        unsigned                mult:2;
        unsigned                maxburst:5;
        u8                      address;
        const struct usb_endpoint_descriptor    *desc;
        const struct usb_ss_ep_comp_descriptor  *comp_desc;
        enum ep_type            ep_type;
        u8                      ep_num;
        u8                      ep_intr_num;
        bool                    endless;
};

struct usb_gadget_ops {
    
    
        int     (*get_frame)(struct usb_gadget *);
        int     (*wakeup)(struct usb_gadget *);
        int     (*func_wakeup)(struct usb_gadget *, int interface_id);
        int     (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
        int     (*vbus_session) (struct usb_gadget *, int is_active);
        int     (*vbus_draw) (struct usb_gadget *, unsigned mA);
        int     (*pullup) (struct usb_gadget *, int is_on);
        int     (*restart)(struct usb_gadget *);
        int     (*ioctl)(struct usb_gadget *,
                                unsigned code, unsigned long param);
        void    (*get_config_params)(struct usb_dcd_config_params *);
        int     (*udc_start)(struct usb_gadget *,
                        struct usb_gadget_driver *);
        int     (*udc_stop)(struct usb_gadget *,
                        struct usb_gadget_driver *);
};

struct usb_ep_ops {
    
    
        int (*enable) (struct usb_ep *ep,
                const struct usb_endpoint_descriptor *desc);
        int (*disable) (struct usb_ep *ep);

        struct usb_request *(*alloc_request) (struct usb_ep *ep,
                gfp_t gfp_flags);
        void (*free_request) (struct usb_ep *ep, struct usb_request *req);

        int (*queue) (struct usb_ep *ep, struct usb_request *req,
                gfp_t gfp_flags);
        int (*dequeue) (struct usb_ep *ep, struct usb_request *req);

        int (*set_halt) (struct usb_ep *ep, int value);
        int (*set_wedge) (struct usb_ep *ep);

        int (*fifo_status) (struct usb_ep *ep);
        void (*fifo_flush) (struct usb_ep *ep);
        int (*gsi_ep_op)(struct usb_ep *ep, void *op_data,
                enum gsi_ep_op op);
};

In the specific UDC driver, it is necessary to encapsulate usb_gadget and each endpoint usb_ep, implement usb_gadget_ops of usb_gadget and implement usb_ep_ops of the endpoint, and complete usb_request. After these things are done, register a UDC, which is performed through the usb_add_gadget_udc() API. Its prototype is:
include/linux/usb/gadget.h
int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget);
Register UDC ( USB device controller), you need to fill in the ep_list (endpoint linked list) of the usb_gadget structure, and fill in the usb_gadget_ops of usb_gadget and the usb_gadget_ops of each endpoint.
As for Gadget's Function, you need to fill in usb_interface_descriptor and usb_endpoint_descriptor yourself, synthesize some
usb_descriptor_headers, and implement the member functions of the usb_function structure. The usb_function structure is defined in
include/linux/usb/composite.h, and its form is as shown in the code list below.

struct usb_function {
    
    
        const char                      *name;
        int                             intf_id;
        struct usb_gadget_strings       **strings;
        struct usb_descriptor_header    **fs_descriptors;
        struct usb_descriptor_header    **hs_descriptors;
        struct usb_descriptor_header    **ss_descriptors;
        struct usb_configuration        *config;
        struct usb_os_desc_table        *os_desc_table;
        unsigned                        os_desc_n;

        /* REVISIT:  bind() functions can be marked __init, which
         * makes trouble for section mismatch analysis.  See if
         * we can't restructure things to avoid mismatching.
         * Related:  unbind() may kfree() but bind() won't...
         */

        /* configuration management:  bind/unbind */
        int                     (*bind)(struct usb_configuration *,  struct usb_function *);
        void                    (*unbind)(struct usb_configuration *,  struct usb_function *);
        void                    (*free_func)(struct usb_function *f);
        struct module           *mod;

        /* runtime state management */
        int                     (*set_alt)(struct usb_function *, unsigned interface, unsigned alt);
        int                     (*get_alt)(struct usb_function *, unsigned interface);
        void                    (*disable)(struct usb_function *);
        int                     (*setup)(struct usb_function *,
                                        const struct usb_ctrlrequest *);
        void                    (*suspend)(struct usb_function *);
        void                    (*resume)(struct usb_function *);

        /* USB 3.0 additions */
        int                     (*get_status)(struct usb_function *);
        int                     (*func_suspend)(struct usb_function *, u8 suspend_opt);
        unsigned                func_is_suspended:1;
        unsigned                func_wakeup_allowed:1;
        unsigned                func_wakeup_pending:1;
        /* private: */
        /* internals */
        struct list_head                list;
        DECLARE_BITMAP(endpoints, 32);
        const struct usb_function_instance *fi;
};

fs_descriptors are full-speed and low-speed descriptor tables; hs_descriptors are high-speed descriptor tables; ss_descriptors are ultra-high-speed descriptor tables. bind() completes the acquisition of I/O buffers, endpoints and other resources when Gadget is registered.
After the usb_function member functions and various descriptors are prepared, the kernel completes the registration of the Gadget Function through the usb_function_register() API. The prototype of the API is: include/linux/usb/composite.h int usb_function_register
(
struct usb_function_driver *newf) ;
In the Gadget driver, the usb_request structure is used to describe a transfer request (describe an I/O request). The status of this structure is similar to the URB on the USB host side. The usb_request structure is defined as follows.

struct usb_request {
    
    
        void                    *buf;
        unsigned                length;
        dma_addr_t              dma;
        struct scatterlist      *sg;
        unsigned                num_sgs;
        unsigned                num_mapped_sgs;
        unsigned                stream_id:16;
        unsigned                no_interrupt:1;
        unsigned                zero:1;
        unsigned                short_not_ok:1;
        unsigned                dma_pre_mapped:1;
        void                    (*complete)(struct usb_ep *ep, struct usb_request *req);
        void                    *context;
        struct list_head        list;
        int                     status;
        unsigned                actual;
        unsigned                udc_priv;
};

In the include/linux/usb/gadget.h file, some commonly used APIs are also encapsulated for Gadget Function driver calls to facilitate their operation of endpoints.
USB Gadget driver object.

struct usb_gadget_driver {
    
    
    char *function; //驱动名称
    enum usb_device_speed speed; //USB设备速度类型
    int (*bind)(struct usb_gadget *); //将驱动和设备绑定,一般在驱动注册时调用
    void (*unbind)(struct usb_gadget *);//卸载驱动时调用,rmmod时调用
    int (*setup)(struct usb_gadget *, const struct usb_ctrlrequest *); //处理ep0的控制请求,在中断中调用,不能睡眠
    void (*disconnect)(struct usb_gadget *); //可能在中断中调用不能睡眠
    void (*suspend)(struct usb_gadget *); //电源管理模式相关,设备挂起
    void (*resume)(struct usb_gadget *);//电源管理模式相关,设备恢复
    /* FIXME support safe rmmod */
    struct device_driver driver; //内核设备管理使用
};

String structure.

struct usb_gadget_strings {
    
    
    u16 language; /* 0x0409 for en-us */
    struct usb_string *strings;
    };
    struct usb_string {
    
    
    u8 id; //索引
    const char *s;
};

The upper-layer calling interface that the UDC driver needs to implement
int usb_gadget_register_driver(struct usb_gadget_driver *driver);
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);

8.4.3 UDC driver

The main data structure of the UDC layer, taking S3C2410 as an example, is in the driver/usb/gadget/s3c2410_udc.c and s3c2410_udc.h files. The following structure is basically implemented by every UDC driver, but the specific implementation details are not the same. But everything remains the same, which is the basic gadget driver data structure introduced above. Basically, the data structures implemented by the UDC driver itself are secondary encapsulation of these basic data structures.
a. Equipment structure

struct s3c2410_udc {
    
    
      spinlock_t lock;
      struct s3c2410_ep ep[S3C2410_ENDPOINTS];
      int address;
      struct usb_gadget gadget;
      struct usb_gadget_driver *driver;
      struct s3c2410_request fifo_req;
      u8 fifo_buf[EP_FIFO_SIZE];
      u16 devstatus;
      u32 port_status;
      int ep0state;
      unsigned got_irq : 1;
      unsigned req_std : 1;
      unsigned req_config : 1;
      unsigned req_pending : 1;
      u8 vbus;
      struct dentry *regs_info;
};

Initialization of this structure in the program:

static struct s3c2410_udc memory = {
    
    
    .gadget = {
    
    
           .ops = &s3c2410_ops,
           .ep0 = &memory.ep[0].ep,
           .name = gadget_name,
           .dev = {
    
    
                    .init_name = "gadget",
            },
    },
    /* control endpoint */
    .ep[0] = {
    
     //struct s3c2410_ep
            .num = 0,
            .ep = {
    
    //struct usb_ep
                   .name = ep0name,
                   .ops = &s3c2410_ep_ops,
                   .maxpacket = EP0_FIFO_SIZE,
             },
             .dev = &memory,
            },
    /* first group of endpoints */
    .ep[1] = {
    
    
               .num = 1,
               .ep = {
    
    
                    .name = "ep1-bulk",
                    .ops = &s3c2410_ep_ops,
                    .maxpacket = EP_FIFO_SIZE,
                },
               .dev = &memory,
               .fifo_size = EP_FIFO_SIZE,
               .bEndpointAddress = 1,
               .bmAttributes = USB_ENDPOINT_XFER_BULK,
     },
    .ep[2] = {
    
    
                  .num = 2,
                  .ep = {
    
    
                         .name = "ep2-bulk",
                         .ops = &s3c2410_ep_ops,
                         .maxpacket = EP_FIFO_SIZE,
                  },
                   .dev = &memory,
                   .fifo_size = EP_FIFO_SIZE,
                   .bEndpointAddress = 2,
                   .bmAttributes = USB_ENDPOINT_XFER_BULK,
     },
    .ep[3] = {
    
    
                .num = 3,
                .ep = {
    
    
                      .name = "ep3-bulk",
                      .ops = &s3c2410_ep_ops,
                      .maxpacket = EP_FIFO_SIZE,
                 },
                 .dev = &memory,
                 .fifo_size = EP_FIFO_SIZE,
                 .bEndpointAddress = 3,
                 .bmAttributes = USB_ENDPOINT_XFER_BULK,
    },
    .ep[4] = {
    
    
               .num = 4,
               .ep = {
    
    
                     .name = "ep4-bulk",
                     .ops = &s3c2410_ep_ops,
                     .maxpacket = EP_FIFO_SIZE,
               },
               .dev = &memory,
               .fifo_size = EP_FIFO_SIZE,
               .bEndpointAddress = 4,
               .bmAttributes = USB_ENDPOINT_XFER_BULK,
    }
};

Different UDCs have different customized data structures. But generally there is such a data structure to represent a UDC device, which encapsulates the usb_gadget device object and contains all endpoints of the device.
b. endpoint structure

struct s3c2410_ep {
    
    
    struct list_head queue;
    unsigned long last_io; /* jiffies timestamp */
    struct usb_gadget *gadget;
    struct s3c2410_udc *dev;
    const struct usb_endpoint_descriptor *desc;
    struct usb_ep ep; //封装的struct usb_ep结构
    u8 num;
    unsigned short fifo_size;
    u8 bEndpointAddress;
    u8 bmAttributes;
    unsigned halted : 1;
    unsigned already_seen : 1;
    unsigned setup_stage : 1;
};

Encapsulate the usb_ep structure and have a queue to queue requests on the port.
c. Request structure

struct s3c2410_request {
    
    
struct list_head queue; /* ep's requests */
struct usb_request req;
};

Encapsulate usb_request and queue the queue variable.
1. The UDC driver is registered with the platform subsystem as a platform driver, so the UDC driver first needs to implement the function members in the struct platform_driver structure:

struct platform_driver {
    
    
      int (*probe)(struct platform_device *); //驱动和设备绑定
      int (*remove)(struct platform_device *); //支持热插拔的设备移除
      void (*shutdown)(struct platform_device *); //设备关闭
      int (*suspend)(struct platform_device *, pm_message_t state); //电源管理相关,挂起设备
      int (*resume)(struct platform_device *); //电源管理相关,恢复设备
      struct device_driver driver;
      struct platform_device_id *id_table; //驱动和设备匹配信息
};

In the following source code analysis, take the s3c2410_udc.c file as an example:

static struct platform_driver udc_driver_2410 = {
    
    
     .driver = {
    
    
          .name = "s3c2410-usbgadget",
          .owner = THIS_MODULE,
     },
    .probe = s3c2410_udc_probe,
    .remove = s3c2410_udc_remove,
    .suspend = s3c2410_udc_suspend,
    .resume = s3c2410_udc_resume,
};

Among them, the s3c2410_udc_probe and s3c2410_udc_remove functions are the most important. The s3c2410_udc_probe function realizes the matching binding of the driver and the device and allocates resources; and the s3c2410_udc_remove function realizes the release of resources.

static int s3c2410_udc_probe(struct platform_device *pdev)
{
    
    
       struct s3c2410_udc *udc = &memory; //s3c2410的UDC设备,在其中对usb_gadget设备对象和端点等进行了初始化
       struct device *dev = &pdev->dev;
       int retval;
       int irq;
       //获取总线时钟并使能
       usb_bus_clock = clk_get(NULL, "usb-bus-gadget");
       clk_enable(usb_bus_clock);
     //获取设备时钟并使能
       udc_clock = clk_get(NULL, "usb-device");
       clk_enable(udc_clock);
       mdelay(10);
       spin_lock_init (&udc->lock); //初始化设备的自旋锁
       udc_info = pdev->dev.platform_data;
       rsrc_start = S3C2410_PA_USBDEV; //s3c2410 UDC端口起始地址
       rsrc_len = S3C24XX_SZ_USBDEV; //s3c2410端口地址长度
       if (!request_mem_region(rsrc_start, rsrc_len, gadget_name)) //申请端口资源
                   return -EBUSY;
       base_addr = ioremap(rsrc_start, rsrc_len); //端口映射
       if (!base_addr) {
    
    
                 retval = -ENOMEM;
                 goto err_mem;
       }
       device_initialize(&udc->gadget.dev); //初始化device设备对象
       udc->gadget.dev.parent = &pdev->dev; //当前UDC设备的父设备对象是platform_device
       udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
       the_controller = udc;
       platform_set_drvdata(pdev, udc); //驱动和设备绑定,在platform_device结构中保存udc设备对象

       /*重新初始化设备*/
       s3c2410_udc_disable(udc);
       s3c2410_udc_reinit(udc);
       /* irq setup after old hardware state is cleaned up */
       /*申请中断,并绑定中断函数,中断函数是UDC功能驱动的入口函数*/
       retval = request_irq(IRQ_USBD, s3c2410_udc_irq, IRQF_DISABLED, gadget_name, udc);
       if (udc_info && udc_info->vbus_pin > 0) {
    
    
               retval = gpio_request(udc_info->vbus_pin, "udc vbus");
               irq = gpio_to_irq(udc_info->vbus_pin);
               retval = request_irq(irq, s3c2410_udc_vbus_irq, IRQF_DISABLED | IRQF_TRIGGER_RISING| IRQF_TRIGGER_FALLING | IRQF_SHARED,gadget_name, udc);
       } else {
    
    
               udc->vbus = 1;
       }
       if (s3c2410_udc_debugfs_root) {
    
     //创建虚拟文件debugfs
                 udc->regs_info = debugfs_create_file("registers", S_IRUGO, s3c2410_udc_debugfs_root,udc, &s3c2410_udc_debugfs_fops);
                 if (!udc->regs_info)
                             dev_warn(dev, "debugfs file creation failed\n");
       }

       dev_dbg(dev, "probe ok\n");
       return 0;

err_gpio_claim:
       if (udc_info && udc_info->vbus_pin > 0)
       gpio_free(udc_info->vbus_pin);
err_int:
       free_irq(IRQ_USBD, udc);
err_map:
       iounmap(base_addr);
err_mem:
       release_mem_region(rsrc_start, rsrc_len);
   
       return retval;
}

It can be seen from the s3c2410_udc_probe function that the probe function mainly completes the relationship between the platform_device device object and the UDC device object, some initialization work of the UDC device and driver, and applies for the resources required by the driver, such as port range, interrupt number, etc., and Bind the interrupt function and interrupt number. This interrupt handling function is very important. All operations on this device will enter from the interrupt function.

static int s3c2410_udc_remove(struct platform_device *pdev)
{
    
    
          struct s3c2410_udc *udc = platform_get_drvdata(pdev); //获取UDC设备对象,在probe函数中绑定的
          unsigned int irq;
          
          if (udc->driver) //设备的驱动usb_gadget_driver对象,说明设备正在使用
                     return -EBUSY;
          debugfs_remove(udc->regs_info); //移除debugfs文件系统中建立的文件
          if (udc_info && udc_info->vbus_pin > 0) {
    
    
                     irq = gpio_to_irq(udc_info->vbus_pin);
                     free_irq(irq, udc);
          }
          free_irq(IRQ_USBD, udc); //释放中断
          /*释放端口资源*/
          iounmap(base_addr);
          release_mem_region(rsrc_start, rsrc_len);
          /*解除绑定*/
          platform_set_drvdata(pdev, NULL);
          /*释放时钟*/
          if (!IS_ERR(udc_clock) && udc_clock != NULL) {
    
    
                            clk_disable(udc_clock);
                            clk_put(udc_clock);
                            udc_clock = NULL;
          }
          if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
    
    
                            clk_disable(usb_bus_clock);
                            clk_put(usb_bus_clock);
                            usb_bus_clock = NULL;
          }
          dev_dbg(&pdev->dev, "%s: remove ok\n", __func__);
          return 0;
}

It can be seen that the remove function is basically the reverse operation of the probe function, which releases the resources applied for in the probe function.
2. The UDC driver also needs to implement two gadget driver registration interfaces, usb_gadget_register_driver and usb_gadget_unregister_driver, for the upper layer. These two functions will implement the binding of gadget driver and udc driver.

int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
    
    
          struct s3c2410_udc *udc = the_controller; //UDC设备对象
          int retval;
          /* Sanity checks */
          if (!udc)
                 return -ENODEV;
         /*UDC设备只能绑定一个gadget driver对象*/
         if (udc->driver)
                 return -EBUSY;
         /*检查gadget driver是否实现了绑定函数、setup函数。同时低速设备也不支持gadget driver*/
         if (!driver->bind || !driver->setup|| driver->speed < USB_SPEED_FULL) {
    
    
                 printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n",driver->bind, driver->setup, driver->speed);
                 return -EINVAL;
         }
         //支持卸载的话,还需要实现unbind函数
         #if defined(MODULE)
                 if (!driver->unbind) {
    
    
                           printk(KERN_ERR "Invalid driver: no unbind method\n");
                           return -EINVAL;
                 }
         #endif
         /* Hook the driver */
         /*将gadget driver和udc设备绑定*/
         udc->driver = driver;
         udc->gadget.dev.driver = &driver->driver;
         /* Bind the driver */
         if ((retval = device_add(&udc->gadget.dev)) != 0) {
    
     //完成gadget设备在内核中的注册
                    printk(KERN_ERR "Error in device_add() : %d\n",retval);
                    goto register_error;
         }
          if ((retval = driver->bind (&udc->gadget)) != 0) {
    
    //gadget驱动绑定函数
                    device_del(&udc->gadget.dev);
                    goto register_error;
         }
         /* Enable udc */
         //使能UDC设备
         s3c2410_udc_enable(udc);
         return 0;
register_error:
         udc->driver = NULL;
         udc->gadget.dev.driver = NULL;
         return retval;
}

/*gadget 驱动注销函数*/
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
    
    
         struct s3c2410_udc *udc = the_controller;
         if (!udc)
                  return -ENODEV;
         /*驱动必须和注册时的驱动是一致的,同时实现了unbind函数*/
         if (!driver || driver != udc->driver || !driver->unbind)
                  return -EINVAL;
         dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n", driver->driver.name);
         /*调用gadget driver实现的unbind函数*/
         driver->unbind(&udc->gadget);
         device_del(&udc->gadget.dev); //和register函数中的device_add对应
         udc->driver = NULL;//这个很重要,这里说明gadget驱动注销之后,才能移除udc设备
         /* Disable udc */
         /*关闭设备*/
         s3c2410_udc_disable(udc);
         return 0;
  }

3. Interrupt function
The interrupt processing function is the core function of the UDC driver layer. Since UDC is a slave device and the host is the control terminal, all operations are initiated by the host, so the interrupt processing function is the core function of the UDC driver layer.

static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)
{
    
    
      struct s3c2410_udc *dev = _dev; //UDC设备对象
      int usb_status;
      int usbd_status;
      int pwr_reg;
      int ep0csr;
      int i;
      u32 idx;
      unsigned long flags;
      spin_lock_irqsave(&dev->lock, flags);
      /* Driver connected ? */
      if (!dev->driver) {
    
     //还没有和驱动绑定,清除中断
                     /* Clear interrupts */
                     udc_write(udc_read(S3C2410_UDC_USB_INT_REG), S3C2410_UDC_USB_INT_REG);
                     udc_write(udc_read(S3C2410_UDC_EP_INT_REG),S3C2410_UDC_EP_INT_REG);
      }
      /* Save index */
      idx = udc_read(S3C2410_UDC_INDEX_REG); //这是哪个端点产生中断的索引号
      /* Read status registers */
      usb_status = udc_read(S3C2410_UDC_USB_INT_REG); //UDC设备状态
      usbd_status = udc_read(S3C2410_UDC_EP_INT_REG); //UDC产生中断的端点状态
      pwr_reg = udc_read(S3C2410_UDC_PWR_REG);
      udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
      ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
      dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n", usb_status, usbd_status, pwr_reg, ep0csr);
      /*
      开始中断的实际处理,这里的中断只有两种类型:
      1. UDC设备中断: 重置、挂起和恢复
      2. 端点中断
      */
      /* UDC设备RESET操作处理 */
      if (usb_status & S3C2410_UDC_USBINT_RESET) {
    
     //Reset中断
              /* two kind of reset :
               * - reset start -> pwr reg = 8
               * - reset end -> pwr reg = 0
               **/
              dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n", ep0csr, pwr_reg);
              dev->gadget.speed = USB_SPEED_UNKNOWN;
              udc_write(0x00, S3C2410_UDC_INDEX_REG);
              udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3, S3C2410_UDC_MAXP_REG);
              dev->address = 0;
              dev->ep0state = EP0_IDLE;
              dev->gadget.speed = USB_SPEED_FULL;
              /* clear interrupt */
              udc_write(S3C2410_UDC_USBINT_RESET, S3C2410_UDC_USB_INT_REG);
              udc_write(idx, S3C2410_UDC_INDEX_REG);
              spin_unlock_irqrestore(&dev->lock, flags);
              return IRQ_HANDLED;
        }
      /* UDC设备RESUME操作处理 */
       if (usb_status & S3C2410_UDC_USBINT_RESUME) {
    
     //Resume中断
              dprintk(DEBUG_NORMAL, "USB resume\n");
              /* clear interrupt */
              udc_write(S3C2410_UDC_USBINT_RESUME, S3C2410_UDC_USB_INT_REG);
              if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver&& dev->driver->resume)//调用resume函数
              dev->driver->resume(&dev->gadget);
       }
       /* UDC设备SUSPEND操作处理 */
       if (usb_status & S3C2410_UDC_USBINT_SUSPEND) {
    
     //Suspend中断
              dprintk(DEBUG_NORMAL, "USB suspend\n");
              /* clear interrupt */
              udc_write(S3C2410_UDC_USBINT_SUSPEND, S3C2410_UDC_USB_INT_REG);
              if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver&& dev->driver->suspend)//调用suspend函数
              dev->driver->suspend(&dev->gadget);
              dev->ep0state = EP0_IDLE;
       }
       /* 下面就是端点中断得处理 */
       /* 首先是控制端点(端点0)的中断处理*/
       /* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
              * generate an interrupt
       */
       if (usbd_status & S3C2410_UDC_INT_EP0) {
    
     //端点0的中断
               dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");
               /* Clear the interrupt bit by setting it to 1 */
               udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
               s3c2410_udc_handle_ep0(dev); //处理端点0
       }
       /* 其他端点,就是数据传输的处理*/
       for (i = 1; i < S3C2410_ENDPOINTS; i++) {
    
     //遍历所有端点,找出中断的端点
               u32 tmp = 1 << i;
               if (usbd_status & tmp) {
    
    
                         dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);
                          /* Clear the interrupt bit by setting it to 1 */
                           udc_write(tmp, S3C2410_UDC_EP_INT_REG);
                         s3c2410_udc_handle_ep(&dev->ep[i]); //处理对应端点
               }
       }
       dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", IRQ_USBD);
       /* Restore old index */
       udc_write(idx, S3C2410_UDC_INDEX_REG);
       spin_unlock_irqrestore(&dev->lock, flags);
       return IRQ_HANDLED;
}

4. Endpoint operation function
The endpoint operation function is the basis of the UDC driver, because most actions are actually related to the endpoint, such as data transmission. First, let’s look at the two functions involved in the interrupt function, one is the processing function of endpoint 0, and the other is the processing function of other endpoints.

static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev)
{
    
    
       u32 ep0csr;
       struct s3c2410_ep *ep = &dev->ep[0];
       struct s3c2410_request *req;
       struct usb_ctrlrequest crq;
       if (list_empty(&ep->queue))
              req = NULL;
       else
              req = list_entry(ep->queue.next, struct s3c2410_request, queue);
       /* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to
       * S3C2410_UDC_EP0_CSR_REG when index is zero */
       udc_write(0, S3C2410_UDC_INDEX_REG);
       ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
       dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n", ep0csr, ep0states[dev->ep0state]);
       /* clear stall status */
       if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {
    
     //清除STALL状态
                 s3c2410_udc_nuke(dev, ep, -EPIPE);
                 dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
                 s3c2410_udc_clear_ep0_sst(base_addr);
                 dev->ep0state = EP0_IDLE;
                 return;
       }
       /* clear setup end */
       if (ep0csr & S3C2410_UDC_EP0_CSR_SE) {
    
    
                 dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
                 s3c2410_udc_nuke(dev, ep, 0);
                 s3c2410_udc_clear_ep0_se(base_addr);
                 dev->ep0state = EP0_IDLE;
       }
       /*端点0的状态处理*/
       switch (dev->ep0state) {
    
    
       case EP0_IDLE:
                   s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr); //在这个函数中会调用上层提供的gadget_driver中实现的setup函数来处理控制数据包
                   break;
       case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ //向主机发送数据
                   dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
                   if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) {
    
    
                             s3c2410_udc_write_fifo(ep, req); //写UDC FIFO
                   }
                   break;
       case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ //从主机接收数据
                      dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
                       if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req ) {
    
    
                                          s3c2410_udc_read_fifo(ep,req);
                      }
                      break;
       case EP0_END_XFER:
                     dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");
                     dev->ep0state = EP0_IDLE;
                     break;
       case EP0_STALL:
                dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");
                dev->ep0state = EP0_IDLE;
                break;
    }
}
/*
* handle_ep - Manage I/O endpoints
其他端点的处理函数,主要是数据发送和接收
*/
static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep)
{
    
    
           struct s3c2410_request *req;
           int is_in = ep->bEndpointAddress & USB_DIR_IN;
           u32 ep_csr1;
           u32 idx;
           if (likely (!list_empty(&ep->queue))) //取出申请
                          req = list_entry(ep->queue.next, struct s3c2410_request, queue);
           else
                          req = NULL;
           idx = ep->bEndpointAddress & 0x7F; //端点地址
           if (is_in) {
    
     //向主机发送数据
                         udc_write(idx, S3C2410_UDC_INDEX_REG);
                         ep_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG);
                         dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n", idx, ep_csr1, req ? 1 : 0);
                         if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) {
    
    
                                          dprintk(DEBUG_VERBOSE, "st\n");
                                           udc_write(idx, S3C2410_UDC_INDEX_REG);
                                           udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL, S3C2410_UDC_IN_CSR1_REG);
                                           return;
                         }
                         if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) {
    
    
                               s3c2410_udc_write_fifo(ep,req);
                        }
           }
           else  //从主机接收数据
           {
    
    
                      udc_write(idx, S3C2410_UDC_INDEX_REG);
                      ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG);
                       dprintk(DEBUG_VERBOSE, "ep%01d rd csr:%02x\n", idx, ep_csr1);
                      if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) {
    
    
                                 udc_write(idx, S3C2410_UDC_INDEX_REG);
                                 udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL, S3C2410_UDC_OUT_CSR1_REG);
                                 return;
                      }
                     if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) {
    
    
                                 s3c2410_udc_read_fifo(ep,req);
                     }
          }
}

Endpoint operation function set: basic operation functions of endpoints

static const struct usb_ep_ops s3c2410_ep_ops = {
    
    
        .enable = s3c2410_udc_ep_enable, //端点使能
        .disable = s3c2410_udc_ep_disable, //关闭端点
        .alloc_request = s3c2410_udc_alloc_request, //分配一个请求
        .free_request = s3c2410_udc_free_request, //释放请求
        .queue = s3c2410_udc_queue, //向端点提交一个请求
        .dequeue = s3c2410_udc_dequeue, //从端点请求队列中删除一个请求
        .set_halt = s3c2410_udc_set_halt,
};

Mainly analyze the queue function, because the upper layer mainly deals with this function, and receiving or sending data requires the corresponding endpoint queue to submit a request.

static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
{
    
    
          struct s3c2410_request *req = to_s3c2410_req(_req);
          struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
          struct s3c2410_udc *dev;
          u32 ep_csr = 0;
          int fifo_count = 0;
          unsigned long flags;
          if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
    
    
                   dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__);
                   return -EINVAL;
          }
          dev = ep->dev;
          if (unlikely (!dev->driver|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
    
    
                   return -ESHUTDOWN;
          }
          local_irq_save (flags);
          if (unlikely(!_req || !_req->complete || !_req->buf || !list_empty(&req->queue))) {
    
    
                   if (!_req)
                            dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__);
                   else {
    
    
                            dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n", __func__, !_req->complete,!_req->buf, !list_empty(&req->queue));
                   }
                   local_irq_restore(flags);
                   return -EINVAL;
          }
          _req->status = -EINPROGRESS;
          _req->actual = 0;
          dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n", __func__, ep->bEndpointAddress, _req->length);
          if (ep->bEndpointAddress) {
    
     //设置端点号
                udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG);
                ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN) ? S3C2410_UDC_IN_CSR1_REG: S3C2410_UDC_OUT_CSR1_REG);
                fifo_count = s3c2410_udc_fifo_count_out();
       }
       else
       {
    
    
                udc_write(0, S3C2410_UDC_INDEX_REG);
                ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
                fifo_count = s3c2410_udc_fifo_count_out();
       }
       /* kickstart this i/o queue? */
       if (list_empty(&ep->queue) && !ep->halted) {
    
     //该端点队列为空,且没有关闭,则直接完成申请
             if (ep->bEndpointAddress == 0 /* ep0 */) {
    
     //端点0
                   switch (dev->ep0state) {
    
    
                   case EP0_IN_DATA_PHASE:
                                 if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY) &&s3c2410_udc_write_fifo(ep,req)) {
    
    
                                                       dev->ep0state = EP0_IDLE;
                                                         req = NULL;
                                    }
                                    break;
                    case EP0_OUT_DATA_PHASE:
                                    if ((!_req->length)|| ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)&& s3c2410_udc_read_fifo(ep,req))) {
    
    
                                                                   dev->ep0state = EP0_IDLE;
                                                           req = NULL;
                                     }
                                    break;
                    default:
                                   local_irq_restore(flags);
                                   return -EL2HLT;
                    }
            }
            else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 && (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY)) &&s3c2410_udc_write_fifo(ep, req)) {
    
    
                           req = NULL;
            }
            else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)&& fifo_count&& s3c2410_udc_read_fifo(ep, req)) {
    
    
                           req = NULL;
            }
       }
       /* pio or dma irq handler advances the queue. */
        if (likely (req != 0))
                        list_add_tail(&req->queue, &ep->queue); //加入队列,等待中断处理
        local_irq_restore(flags);
        dprintk(DEBUG_VERBOSE, "%s ok\n", __func__);
        return 0;
}

There is also an important function in the UDC driver. After the data transmission or reception is completed, that is, after a request is completed, the completion function of the request will be called to notify the upper driver.

static void s3c2410_udc_done(struct s3c2410_ep *ep,  struct s3c2410_request *req,  int status) 
{
    
     
         unsigned halted = ep->halted;
         list_del_init(&req->queue);  //将这个请求从端点请求队列中删除
         if (likely (req->req.status == -EINPROGRESS)) 
               req->req.status = status; //返回完成状态
        else 
               status = req->req.status;
        ep->halted = 1; 
        req->req.complete(&ep->ep, &req->req); 
        ep->halted = halted; 
}

Summary:
The source code of the UDC device driver layer is almost analyzed. Many other functions are operating registers and are closely related to the UDC device, but in general the completed functions are the same. It can be found that the UDC device driver layer mainly needs to do the following work:

  1. Encapsulate the three standard data structures of usb_gadget, usb_ep, and usb_request, and design your own data structure according to some device characteristics of your own UDC;
  2. Implement the functions in the platform_driver data structure and register the UDC device driver with the platform system;
  3. Implement the usb_gadget_ops function set, which mainly operates some characteristics of UDC devices (for devices);
  4. Implement the usb_ep_ops function set, which are mainly functions of operating endpoints, such as request allocation and submission (for endpoints);
  5. Implement the interrupt handling function of the UDC device. This function is basically the core of the UDC device driver;
  6. Implement the upper-layer function driver registration interface function:
    int usb_gadget_register_driver(struct usb_gadget_driver *driver)
    int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)

8.4.4 USB Gadget FUNC API

Gadget device layer
This layer is optional and is between the UDC driver layer and the Gadget function layer. The main source code is in the composite.c and composite.h files. The device layer has nothing to do with the hardware. It mainly implements some universal codes to reduce code duplication in the gadget function layer. The Gadget device layer serves as a link between the previous and the following, contacting the Gadget functional layer and UDC driver layer.
Separating the composite source code also provides a universal framework for the implementation of composite devices. A composite device refers to a device that supports multiple functions in a configuration descriptor, or a device that supports multiple configurations, each configuration having a different function. For example, one device supports both network and storage, and one device supports both keyboard and mouse functions.
The main data structures of the Gadget device layer:
1. Function

struct usb_function {
    
     //描述一个配置的一个功能
        const char *name; //功能名称
        struct usb_gadget_strings **strings; //string数组,通过bind中分配的id访问
        struct usb_descriptor_header **descriptors; //全速和低速的描述符表,用于bind中分配的接口描述符和string描述符
        struct usb_descriptor_header **hs_descriptors; //高速描述符表
        struct usb_configuration *config;//调用usb_add_function()函数赋值,是这个功能关联的配置描述符
        /* REVISIT: bind() functions can be marked __init, which
        * makes trouble for section mismatch analysis. See if
        * we can't restructure things to avoid mismatching.
        * Related: unbind() may kfree() but bind() won't...
        */
        /* configuration management: bind/unbind */
        /*在Gadget注册时,分配资源*/
        int (*bind)(struct usb_configuration *, struct usb_function *);
        /*unbind的逆操作*/
        void (*unbind)(struct usb_configuration *, struct usb_function *);
        /* runtime state management */
        /*重新配置altsetting,*/
        int (*set_alt)(struct usb_function *,unsigned interface, unsigned alt);
        /*获取当前altsetting*/
        int (*get_alt)(struct usb_function *, unsigned interface);
        /*关闭功能*/
        void (*disable)(struct usb_function *);
        /*接口相关的控制处理*/
        int (*setup)(struct usb_function *, const struct usb_ctrlrequest *);
        /*电源管理相关的挂起和恢复功能*/
        void (*suspend)(struct usb_function *);
        void (*resume)(struct usb_function *);
        /* private: */
        /* internals */
        struct list_head list;
        DECLARE_BITMAP(endpoints, 32);
};

2.Config

struct usb_configuration {
    
     //表示一个Gadget配置
       const char *label; //配置名称
       struct usb_gadget_strings **strings; //字符串表
       const struct usb_descriptor_header **descriptors; //功能描述符表
       /* REVISIT: bind() functions can be marked __init, which
            * makes trouble for section mismatch analysis. See if
             * we can't restructure things to avoid mismatching...
       */
       /* configuration management: bind/unbind */
       /*在usb_add_config函数中调用,分配资源等*/
       int (*bind)(struct usb_configuration *);
       void (*unbind)(struct usb_configuration *);
       /*处理驱动框架不能处理的配置控制请求*/
       int (*setup)(struct usb_configuration *, const struct usb_ctrlrequest *);
       /* fields in the config descriptor */
       /*用来赋值配置描述符*/
       u8 bConfigurationValue;
       u8 iConfiguration;
       u8 bmAttributes;
       u8 bMaxPower;
       /*和composite设备关联,在usb_add_config函数中设置*/
       struct usb_composite_dev *cdev;
       /* private: */
       /* internals */
       struct list_head list;
       struct list_head functions; //功能链表
       u8 next_interface_id;
       unsigned highspeed:1;
       unsigned fullspeed:1;
       struct usb_function *interface[MAX_CONFIG_INTERFACES];
};

3. Driver

struct usb_composite_driver {
    
    
        const char *name; //驱动名称
        const struct usb_device_descriptor *dev; //设备描述符
        struct usb_gadget_strings **strings; //字符串表
        /* REVISIT: bind() functions can be marked __init, which
        * makes trouble for section mismatch analysis. See if
        * we can't restructure things to avoid mismatching...
        */
        int (*bind)(struct usb_composite_dev *);
        int (*unbind)(struct usb_composite_dev *);
        /* global suspend hooks */
        void (*suspend)(struct usb_composite_dev *);
        void (*resume)(struct usb_composite_dev *);
};

4. Dev

struct usb_composite_dev {
    
     //表示一个composite设备
       struct usb_gadget *gadget; //关联的gadget
       struct usb_request *req; //用于控制响应,提前分配的
       unsigned bufsiz; //req中提前分配的buffer长度
       struct usb_configuration *config; //当前配置
       /* private: */
       /* internals */
       struct usb_device_descriptor desc;
       struct list_head configs; //配置链表
       struct usb_composite_driver *driver;
       u8 next_string_id;
       /* the gadget driver won't enable the data pullup
        * while the deactivation count is nonzero.
        */
       unsigned deactivations;
       /* protects at least deactivation count */
       spinlock_t lock;
};

8.4.5 Gadget FUNC driver

The Gadget function layer completes the specific functions of the USB device, and its manifestations are different, such as keyboard, mouse, storage and network card, etc. The functional layer not only involves Gadget driver-related content, but also involves its function-related kernel subsystems. For example, storage also involves the kernel storage subsystem, and the network card also involves the network driver subsystem. Therefore, the code for the Gadget function is very complex. Taking zero.c as an example, this module simply echoes back the received data.
1. Data structure
First, you need to implement the usb_composite_driver function set:

static struct usb_composite_driver zero_driver = {
    
    
         .name = "zero",
         .dev = &device_desc,
         .strings = dev_strings,
         .bind = zero_bind,
         .unbind = zero_unbind,
         .suspend = zero_suspend,
         .resume = zero_resume,
};

2. Main functions
The implementation of this module is as simple as:

static int __init init(void)
{
    
    
      return usb_composite_register(&zero_driver);
}
module_init(init);

static void __exit cleanup(void)
{
    
    
      usb_composite_unregister(&zero_driver);
}

The Bind function is an important function that the functional layer needs to implement to associate with the device layer:

static int __init zero_bind(struct usb_composite_dev *cdev)
{
    
    
         int gcnum;
         struct usb_gadget *gadget = cdev->gadget; //Gadget设备
         int id;
         /* Allocate string descriptor numbers ... note that string
          * contents can be overridden by the composite_dev glue.
          */
        /*分配字符串描述符的id,并赋值给设备描述符中字符串索引*/
        id = usb_string_id(cdev);
        strings_dev[STRING_MANUFACTURER_IDX].id = id;
        device_desc.iManufacturer = id;
        id = usb_string_id(cdev); i
        strings_dev[STRING_PRODUCT_IDX].id = id;
        device_desc.iProduct = id;
        id = usb_string_id(cdev);
        strings_dev[STRING_SERIAL_IDX].id = id;
        device_desc.iSerialNumber = id;
        /*设置挂起后,设备自动恢复的定时器*/
        setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev);
        /*核心代码,实现功能*/
        if (loopdefault) {
    
    
               loopback_add(cdev, autoresume != 0); //数据简单回显功能
               if (!gadget_is_sh(gadget))
                     sourcesink_add(cdev, autoresume != 0);
        } else
        {
    
    
               sourcesink_add(cdev, autoresume != 0);
               if (!gadget_is_sh(gadget))
                     loopback_add(cdev, autoresume != 0);
        }
        /*初始化设备描述符*/
        gcnum = usb_gadget_controller_number(gadget);
        if (gcnum >= 0)
               device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
        else {
    
    
               device_desc.bcdDevice = cpu_to_le16(0x9999);
        }
        return 0;
}

/*增加数据简单回显功能*/
int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume)
{
    
    
     int id;
     /*获取字符串描述符id索引*/
     id = usb_string_id(cdev);
      strings_loopback[0].id = id;
      loopback_intf.iInterface = id;
      loopback_driver.iConfiguration = id;
        /* support autoresume for remote wakeup testing */
      if (autoresume)
                            sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
     /* support OTG systems */
     if (gadget_is_otg(cdev->gadget)) {
    
    
                            loopback_driver.descriptors = otg_desc;
                            loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
     }
     return usb_add_config(cdev, &loopback_driver); //增加一个配置
}

/*loopback配置*/
static struct usb_configuration loopback_driver = {
    
    
       .label = "loopback",
       .strings = loopback_strings,
       .bind = loopback_bind_config,
       .bConfigurationValue = 2,
       .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
       /* .iConfiguration = DYNAMIC */
};
将增加配置的usb_add_config函数中会调用其bind函数,即loopback_bind_config函数,来分配这个配置所需要的资源。
struct f_loopback {
    
    
        struct usb_function function;
        struct usb_ep *in_ep;
        struct usb_ep *out_ep;
};
static int __init loopback_bind_config(struct usb_configuration *c)
{
    
    
       struct f_loopback *loop;
       int status;
       loop = kzalloc(sizeof *loop, GFP_KERNEL); //分配一个loop结构
       if (!loop)
             return -ENOMEM;
       /*初始化一个功能*/
       loop->function.name = "loopback";
       loop->function.descriptors = fs_loopback_descs;
       loop->function.bind = loopback_bind;
       loop->function.unbind = loopback_unbind;
       loop->function.set_alt = loopback_set_alt;
       loop->function.disable = loopback_disable;
       status = usb_add_function(c, &loop->function); //加入这个功能
       if (status)
            kfree(loop);
       return status;
}

In the usb_add_function function, the bind function of this function is called, that is, the loopback_bind function:

static int __init  loopback_bind(struct usb_configuration *c, struct usb_function *f)
{
    
    
             struct usb_composite_dev *cdev = c->cdev;
             struct f_loopback *loop = func_to_loop(f);
             int id;
             /* allocate interface ID(s) */
             id = usb_interface_id(c, f); //分配一个接口id
             if (id < 0)
                  return id;
             loopback_intf.bInterfaceNumber = id;
             /* allocate endpoints */
             /*返回一个输入端点*/
             loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
             if (!loop->in_ep) {
    
    
                   autoconf_fail:
                   ERROR(cdev, "%s: can't autoconfigure on %s\n", f->name, cdev->gadget->name);
                   return -ENODEV;
             }
             loop->in_ep->driver_data = cdev; /* claim */
             /*返回一个输出端点,返回合适的端点*/
             loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
             if (!loop->out_ep)
                       goto autoconf_fail;
             loop->out_ep->driver_data = cdev; /* claim */
             /* support high speed hardware */
             if (gadget_is_dualspeed(c->cdev->gadget)) {
    
    
                       hs_loop_source_desc.bEndpointAddress = fs_loop_source_desc.bEndpointAddress;
                       hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
                       f->hs_descriptors = hs_loopback_descs;
             }
             DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",f->name, loop->in_ep->name, loop->out_ep->name);
             return 0;
}

Functional implementation
The Loopback_set_alt function will be called in the setup function of the device layer to control the communication setting interface.

static int loopback_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
    
    
          struct f_loopback *loop = func_to_loop(f);
          struct usb_composite_dev *cdev = f->config->cdev;
          /* we know alt is zero */
          if (loop->in_ep->driver_data)
                disable_loopback(loop);
          return enable_loopback(cdev, loop); //开启功能
}

static int enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
{
    
    
          int result = 0;
          const struct usb_endpoint_descriptor *src, *sink;
          struct usb_ep *ep;
          struct usb_request *req;
          unsigned i;
          /*选择端点描述符*/
          src = ep_choose(cdev->gadget, &hs_loop_source_desc, &fs_loop_source_desc);
          sink = ep_choose(cdev->gadget, &hs_loop_sink_desc, &fs_loop_sink_desc);
          /* one endpoint writes data back IN to the host */
          /*输入输出端点使能*/
          ep = loop->in_ep;
          result = usb_ep_enable(ep, src);
          if (result < 0)
                return result;
          ep->driver_data = loop;
          /* one endpoint just reads OUT packets */
          ep = loop->out_ep;
          result = usb_ep_enable(ep, sink);
          if (result < 0) {
    
    
fail0:
                ep = loop->in_ep;
                usb_ep_disable(ep);
                ep->driver_data = NULL;
                return result;
          }
          ep->driver_data = loop;
          /* allocate a bunch of read buffers and queue them all at once.
             * we buffer at most 'qlen' transfers; fewer if any need more
             * than 'buflen' bytes each.
            */
           /*qlen=32,分配32个请求,将这个请求放入输出端点队列,等待接收数据*/
          for (i = 0; i < qlen && result == 0; i++) {
    
    
                 req = alloc_ep_req(ep);
                 if (req) {
    
    
                          req->complete = loopback_complete;
                          result = usb_ep_queue(ep, req, GFP_ATOMIC);
                          if (result)
                                 ERROR(cdev, "%s queue req --> %d\n", ep->name, result);
                 }
                 else {
    
    
                      usb_ep_disable(ep);
                      ep->driver_data = NULL;
                      result = -ENOMEM;
                      goto fail0;
                 }
          }
          DBG(cdev, "%s enabled\n", loop->function.name);
          return result;
}

/*接收到数据之后,将调用这个完成函数*/
static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
{
    
    
          struct f_loopback *loop = ep->driver_data;
          struct usb_composite_dev *cdev = loop->function.config->cdev;
          int status = req->status;
          switch (status) {
    
    
              case 0: /* normal completion? */
              if (ep == loop->out_ep) {
    
     //将接收到的数据放入输入端点,返回给主机
                     /* loop this OUT packet back IN to the host */
                     req->zero = (req->actual < req->length);
                     req->length = req->actual;
                     status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
                     if (status == 0)
                            return;
                     /* "should never get here" */
                     ERROR(cdev, "can't loop %s to %s: %d\n", ep->name, loop->in_ep->name,status);
              }
              /* queue the buffer for some later OUT packet */
              req->length = buflen; //将输入端点完成的申请,重新放入输出队列,等待接收新的数据
              status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC);
              if (status == 0)
                   return;
              /* "should never get here" */
              /* FALLTHROUGH */
              default:
              ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, status, req->actual, req->length);
              /* FALLTHROUGH */
              /* NOTE: since this driver doesn't maintain an explicit record
               * of requests it submitted (just maintains qlen count), we
               * rely on the hardware driver to clean up on disconnect or
               * endpoint disable.
               */
               case -ECONNABORTED: /* hardware forced ep reset */
               case -ECONNRESET: /* request dequeued */
               case -ESHUTDOWN: /* disconnect from host */
               free_ep_req(ep, req);
               return;
           }
}

8.5 USB OTG

OTG is the abbreviation of On-The-Go. The original intention of its design is to allow two "peripheral devices" to transmit data through USB without a PC (Host). It can be understood that a USB device (OTG device) with OTG function can be used as either host or peripheral.

8.6 Summary

USB drivers are divided into USB host drivers and USB device drivers. If the system's USB host controller complies with OHCI (Open Host Controller Interface) and other standards, most of the work of the host driver can use common code. For a USB device, the USB device has at least two identities: firstly, the USB device is "USB", and secondly, the USB device is "own". The USB device is "USB", which means that the USB device is connected to the USB bus and must complete the initialization and registration of usb_driver; the USB device is "own", which means that it may be a character device, tty device, network device, etc. , the code that conforms to the corresponding framework must also be implemented in the USB device driver.
The operation process of reading and writing the device driver part of the USB device driver has its own particularity, which is run through by URB (USB request block). The life cycle of a URB usually includes creation, initialization, submission and delivery by the USB core and USB host. , the process in which the callback function is called after completion can also be canceled after the URB is submitted by the driver.
On the UDC (USB Device Controller) and Gadget Function (on top of the UDC driver) side, UDC cares about the underlying hardware operations, while the Function (File Storage) driver just uses a common API and interacts with the underlying UDC driver through usb_request.

9. Linux USB hot plug

9.1 Hardware knowledge (USB sockets and plugs)

In the original standard, the USB connector had 4 wires: power, D-, D+, and ground. Let’s call this a standard USB connector for now. Later, OTG appeared and a miniUSB connector was added. The miniUSB connector has 5 lines, and an additional ID line is used for identification. Standard USB ports are only Type A and Type B. Each type is divided into plugs and sockets, such as type A plug, type A socket, etc. The socket we usually use on computers is called a Type A USB socket, and the corresponding plug, such as the one on a USB flash drive, is called a Type A plug. The socket on the printer is a type B socket (more square, not as flat as on the computer), and the corresponding plug is a type B plug. Maybe you have seen a USB extension cable with a square end and a flat end. That’s right, the flat end is called a type A plug, and the square end is called a type B plug, and the two corresponding sockets are plugged into them. They are type A sockets and type B sockets respectively. Type A plugs cannot fit into type B sockets, and vice versa.
miniUSB is also divided into type A and type B, but an additional type AB is added. Since it is called miniUSB, of course it is very small and is mainly used for portable devices, such as MP3, mobile phones, digital cameras, etc. USB is a master-multi-slave structure, that is, there can only be one host at a time. For example, a PC is a host, and other devices can only be devices, so there is no direct communication between the two devices. The emergence of USB OTG (on the go) solves this contradiction: a device can change its identity under certain circumstances and appear as a host. Therefore, the AB-type miniUSB socket appeared. Whether it is a type A miniUSB plug or a type B miniUSB plug, you can plug it in, and rely on the extra ID line inside to identify its identity: whether it is a host or a slave. . In this way, two USB devices can be directly connected for data transfer. The miniUSB socket we use on our MP3 player is a type B miniUSB socket. Since USB supports hot plugging, it also has corresponding measures in the design of the connector. The ground pin and power pin of the USB plug are relatively long, while the two data pins are relatively short, so that when inserted into the socket, the power and ground are first connected, and then the two data lines are connected. This ensures that power is turned on before the data lines, preventing latch-up. As for USB cables, usually we don't care much and just buy ready-made ones, unless you are producing USB cables. In full-speed mode, shielded twisted-pair cables are required, while in low-speed mode, shielding and twisted pairs are not required. In addition, the USB protocol stipulates that the length of the USB low-speed cable shall not exceed 3 meters, while the length of the full-speed cable shall not exceed 5 meters. This is because there is a delay in cable transmission. To ensure correct response, there cannot be too much delay. The USB standard specifies the color of the signal lines inside, where Vbus is red, D- is white, D+ is green, and GND is black. However, I have seen many USB cables that do not follow the standards, so everyone should be careful when using them. It is more reliable to measure them with a meter.

9.2 The hub reports the USB device connection to the USB host controller

First, connect a 15K ohm pull-down resistor to ground on D+ and D- of each downstream port of the USB hub. In this way, when the hub port is floating, it is pulled to a low level by these two pull-down resistors. On the USB device side, a 1.5K ohm pull-up resistor is connected to D+ or D-. For full-speed and high-speed devices, the pull-up resistor is connected to D+; for low-speed devices, the pull-up resistor is connected to D-. In this way, when the device is plugged into the hub, the voltage is divided by the 1.5K pull-up resistor and the 15K pull-down resistor, resulting in one of the differential data lines being pulled high. After the hub detects this status, it reports it to the USB host controller, thus detecting the insertion of the device. The USB high-speed device is first recognized as a full-speed device, and then switches to high-speed mode through confirmation between HOST and DEVICE. In high-speed mode, it is the current transfer mode. At this time, the pull-up resistor on D+ is disconnected.

9.3 Hot plug

Hot-plugging (hot-plugging or Hot Swap) refers to hot-plugging. The hot-plugging function allows users to remove and replace damaged hard drives, power supplies, boards and other components without shutting down the system or cutting off the power supply, thus improving the efficiency of the system. It improves the system’s timely recovery capability, scalability and flexibility from disasters.

9.3.1 Benefits of hot plugging

The benefits of adding hot plugging to the system include:

  • Damaged modules can be removed while the system is powered on, and updates or expansions can be made while the system is powered on without affecting system operations.
  • Due to the improved reliability of hot-swappable parts, they can also be used as breakers, and because hot-swappable parts can automatically recover, there are many hot-swappable chips that provide signals for the system's line power supply so that the system can perform fault analysis. Reduced costs.

9.4 USB hot plug processing under Linux

9.4.1 Implementation and analysis of USB HUB driver under Linux:

During system initialization, call the usb_hub_init function in the usb_init function to enter the hub initialization. The registration of the hub driver is completed in the usb_hub_init function, and a kernel thread is created using the function kthread_run. This thread is used to manage the status of the monitoring hub, and all situations are reported through this thread.
USB devices are hot-swappable, which is different from PCI devices. PCI devices are fixed when the system starts, so PCI devices only need to be initialized and enumerated, using a recursive algorithm. USB devices need to be hot-plugged, so the hub_configure function is called in the hub_probe function to configure the hub. In this function, the function usb_alloc_urb function is mainly used to allocate an urb, and usb_fill_int_urb is used to initialize the urb structure, including the hub's interrupt service program hub_irq. , query cycle, etc.
Whenever a device is connected to the USB interface, the USB bus will trigger the hub's interrupt service program hub_irq when querying the hub status information. This function uses kick_khubd to add the hub structure to khubd's queue hub_event_list through event_list, and then wakes up khubd. Enter the hub_events function, which is used to process the khubd event queue, from each usb_hub data structure in khubd's hub_event_list. This function first determines whether the hub has an error, and then uses a for loop to detect the status information of each port. Use usb_port_status to obtain port information, and if it changes, call the hub_port_connect_change function to configure the port, etc.

9.4.2 Software level analysis

Here we first talk about the processing of USB hot plug events. —Khubd daemon process
Khubd is the daemon process of HUB. It is used to check USB port events, notify HCD and USB core, and then handle them accordingly.
After the hub driver is installed OK, the system will call kthread_run(hub_thread, NULL, "khubd") to start the daemon process. khubd is the name of the daemon process. This process is almost an infinite loop. It will only exit when kthread_should_stop is executed, and the process It is not executed all the time. When no device is plugged into the hub, the process is in a sleep state. Only when a device is plugged in, will the process be awakened for processing.
Driver directory drivers/usb/*

  • usb/serial usb serial device driver (such as usb 3G card, Bluetooth, etc.)
  • usb/storage usb large storage disk drive (u disk)
  • usb/host usb host usb host controller driver (embedded otg: dwc_otg)
  • usb/core USB core processing code, all driver-related processing are here, and they are all registered in it.
  • usb/usb-skeleton.c is the classic USB client driver framework, you can refer to it.
    Of course, there are others that will not be explained here. The overall driver framework of USB is posted below:
    Insert image description here
    Here we mainly analyze the working principle of khub: The hardware level is the work of the hub, how to communicate with the host and its devices and the corresponding events
    [usb/core/ hub.c ]
int usb_hub_init(void)
{
    
    
        if (usb_register(&hub_driver) < 0) {
    
    
                printk(KERN_ERR "%s: can't register hub driver\n",
                        usbcore_name);
                return -1;
        }
 
        khubd_task = kthread_run(hub_thread, NULL, "khubd");
        if (!IS_ERR(khubd_task))
                return 0;
 
        /* Fall through if kernel_thread failed */
        usb_deregister(&hub_driver);
        printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);
 
        return -1;
}

Here we only care about kthread_run(hub_thread, NULL, “khubd”); Then we look at the hub_thread function.

static int hub_thread(void *__unused)
{
    
    
        do {
    
    
                hub_events();
                wait_event_interruptible(khubd_wait,
                                !list_empty(&hub_event_list) ||
                                kthread_should_stop());
                try_to_freeze();
        } while (!kthread_should_stop() || !list_empty(&hub_event_list));
 
        pr_debug("%s: khubd exiting\n", usbcore_name);
        return 0;
}

Here we see the hub_events() function. Then set the running status and add hub_event_list if there is an event. Since then khubd has been running.
Insert image description here
Insert image description here
Through the flow chart, we can clearly understand that when the usb device is inserted into the usb interface, khubd runs. It detects the change in port status, calls hub_port_connect_change(), if it is a new device, then usb_allco_dev, and then calls usb_new_device to configure the usb device. normal work.

Guess you like

Origin blog.csdn.net/qq_41483419/article/details/128788133