Timeline
Timeline
2026-02-16
init
This article introduces the basics of Linux USB drivers, thoroughly reviews the technological development history of USB, physical interface classifications and their relationship with transmission protocol versions, the evolution rules of USB version naming, and explains the USB Host and Slave interface configurations in conjunction with the RK3568 processor.
Linux Driver Notes
| Table of Contents | Links |
|---|---|
| 1. Linux Driver Framework | |
| 2. Linux Driver Loading Logic | |
| 3. Character Device Basics | |
| 4. Concurrency and Competition | |
| 5. Advanced Character Device Progression | |
| 6. Interrupts | |
| 7. Platform Bus | |
| 8. Device Tree | |
| 9. Device Model | |
| 10. Hot Plug | |
| 11. pinctrl Subsystem | |
| 12. gpio Subsystem | |
| 13. Input Subsystem | |
| 14. Single Bus | |
| 15. I2C | |
| 16. SPI | |
| 17. UART | |
| 18. PWM | |
| 19. RTC | |
| 20. Watchdog | |
| 21. CAN | |
| 22. Network Device | |
| 23. ADC | |
| 24. IIO | |
| 25. USB | |
| 26. LCD |
USB Basics
**USB (Universal Serial Bus)**is a widely used universal interface standard between computers and electronic devices, capable of transmitting data and power. USB is a standard.
- Concept Proposal and First Generation Standard Development (1994-1996)
In 1994, companies such as Compaq, IBM, and Intel jointly developed USB technology to simplify the connection between computers and peripherals. In 1996, the USB 1.0 standard was released, supporting 1.5Mbps (Low Speed) and 12Mbps (Full Speed) transfer rates, meeting the needs of low-speed devices like mice and keyboards, and also enabling hot-swapping for user convenience. - Technical Improvement and Popularization (1998-2000)
In 1998, USB 1.1 optimized electrical characteristics and compatibility, improving reliability and driving device adoption. In 2000, USB 2.0 was a major breakthrough, introducing a 480Mbps High-Speed transfer mode to meet the needs of high-speed devices like digital cameras and external hard drives, becoming the mainstream interface standard. - High-Speed Development and Performance Enhancement (2008-2013)
In 2008, USB 3.0 (later called USB 3.1 Gen 1) emerged, increasing the speed to 5Gbps, optimizing power management and signal transmission, supporting high-power device charging, and meeting needs such as high-definition video transmission. In 2013, USB 3.1 Gen 2 was released, reaching speeds of 10Gbps, enabling possibilities for high-performance external SSDs and other devices. - Continuous Evolution and Diversification (2014 to Present)
In 2014, the USB Type-C interface appeared. It is small in size, reversible, supports high-power transmission (up to 100W), and can transfer data at high speeds, gradually becoming mainstream. In 2017, USB 3.2 defined different speed identifiers, enriching speed options. In 2019, USB4, based on Thunderbolt 3 technology, reached speeds of up to 40Gbps, enhancing features like multi-display support and expanding application scenarios.
USB continues to evolve, constantly improving in data transfer, device compatibility, and power delivery, becoming an indispensable connection method for modern electronic devices. There are different types of USB interfaces, and the interface standards can be referenced on the official website:
USB Interface Classification
USB interfaces can be roughly divided into three types: Type-A, Type-B, and Type-C interfaces, which include Type-A, Type-B, Mini-USB, Micro-USB, and Micro-B SuperSpeed interfaces. The following provides a detailed introduction.
- The Type-A interface, as shown in the figure below, is a rectangular interface widely used in host devices such as computers and chargers, as well as traditional peripherals like USB drives, mice, and keyboards.

- The Type-B interface, as shown in the figure below, is a square interface commonly used in devices such as printers, microphones, and scanners.

- The Mini-B interface, as shown in the figure below, is often used in dashcam devices.

- The Micro-USB interface, as shown in the figure below, is a small interface that was used in older mobile phones, power banks, and other portable devices.

- The Micro-B SuperSpeed interface, as shown in the figure below, is commonly used in external hard drives.

- The Type-C interface, as shown in the figure below, is reversible. Currently, the Type-C interface is the mainstream interface for modern devices such as smartphones, laptops, and headphones.

Summary:

Although the different types of USB interfaces in the figure vary in appearance and usage, they all follow the USB standard protocol, ensuring compatibility and interoperability between devices.
It should be noted here that just because a device uses a Type-C interface, it cannot be assumed to have high-speed data transfer capabilities. Type-C is merely a physical specification; the actual data transfer speed depends on the USB protocol version it supports. For example, if it uses the USB 2.0 protocol, even with a Type-C interface, its speed is relatively limited, while USB 3.0 and higher versions can achieve high-speed transmission.
USB Version

Initially, USB 3.0 was named SuperSpeed USB, emphasizing its high-speed transmission characteristics. Later, the USB 3.1 version was released, which further improved the transfer rate.
However, to simplify the naming:
- USB 3.0 was renamed to USB 3.1 Gen 1
- USB 3.1 was renamed to USB 3.1 Gen 2.
USB-IF introduced the latest USB naming specification:
- USB 3.1 Gen 1 was renamed to USB 3.2 Gen 1
- USB 3.1 Gen 2 was renamed to USB 3.2 Gen 2
- USB 3.2 Gen 2x2 is USB 3.2
After two naming changes:
- USB 3.0 is named USB 3.2 Gen 1
- USB 3.1 is named USB 3.2 Gen 2
- USB 3.2 is named USB 3.2 Gen 2x2
The version of a USB interface can generally be inferred from its color.
- Black/White: USB 1.0/2.0
- Dark Blue: USB 3.0
- Light Blue: USB 2.0
- Red: USB 3.1
RK3568 USB Interface

In the diagram above, we can see that the RK3568 processor has USB 2.0 HOSTx2, one USB 3.0 HOST, and one USB 3.0 OTG.
USB devices are divided into Host (master device) and Slave (slave device). Data transmission can only occur when a Host is connected to a Slave.
USB Host: USB Host refers to a device with USB host functionality. The USB master device controls and manages the USB bus, and is typically a computer or other host device, such as a tablet, laptop, or desktop.
USB Slave: USB Slave refers to a device with USB slave functionality. The USB slave device is controlled by the USB host and relies on the USB host device for data transmission and communication. USB slave devices can be various peripherals, such as keyboards, mice, USB flash drives, etc.
USB OTG(USB On-The-Go)
Refers to devices that support USB OTG functionality (plug-and-play). USB OTG allows a device to switch between master and slave roles, enabling direct communication with other USB devices without the need for a traditional USB host. For example, OTG can be used to directly connect a camera and a printer.
The RK3568 chip has two USB 2.0 host controllers. Each USB 2.0 host controller fully supports USB 2.0 functionality through an Enhanced Host Controller Interface (EHCI) host controller and an Open Host Controller Interface (OHCI) host controller, and each host controller has one USB port.
The OHCI host controller only supports full-speed and low-speed modes, used for connecting full-speed and low-speed devices.
The EHCI host controller only supports high-speed mode, used for connecting high-speed devices.
The OHCI host controller and the EHCI host controller share the same USB port. The EHCI host controller automatically selects the control ownership of the USB port (OHCI or EHCI) based on the speed mode of the connected device.
- When OHCI is selected as the host controller, the OHCI host controller will provide services to the connected devices;
- When EHCI is selected as the host controller, the EHCI host controller will provide services to the connected devices.

The RK3568 chip has two USB3.0 controllers, one of which can be used as a USB3.0 OTG (On-The-Go) controller, while the other can only be used as a USB3.0 host controller. The USB3.0 OTG controller can act as a static host, static device, USB2.0/3.0 OTG A device, or B device based on the input ID state of the USB2.0 physical layer (PHY).
It can perform data transmission between the host and device as either a host or device at SuperSpeed, High-Speed, Full-Speed, or Low-Speed.

As can be seen from the figure above, the USB3 OTG module connects to the USB2.0 PHY module below via the HS/FS/LS MAC (High-Speed/Full-Speed/Low-Speed MAC) interface. The USB2.0 PHY module is responsible for the physical layer functions of USB2.0. The USB3 OTG module connects to the SS PHY module on the right via the SS MAC (SuperSpeed MAC) interface. The SS PHY module is responsible for the physical layer functions of SuperSpeed.

From the above diagram, it can be seen that the RK3568 chip is equipped with two USB2.0 HOSTs, and the USB2.0 HOST pins do not have multiplexing. USB 2.0 Host_2 controller and USB 2.0 Host_3 controller use port0 and port1 of USB 2.0 Comb PHY_1 respectively.
At the same time, the chip has a USB3.0 OTG, which is backward compatible with USB2.0 OTG, and this USB 3.0 OTG controller shares the USB3/SATA Combo PHY_0 controller with the USB3/SATA Combo PHY_0。
Additionally, the RK3568 chip also has a USB3.0 HOST, which is backward compatible with USB2.0 HOST. USB 3.0 Host_1 controller shares the USB3/SATA/QSGMII Combo PHY_1 with SATA_1/QSGMII controller.
USB 3.0 OTG controller and USB 3.0 Host_Controller 1 uses USB 2.0 Comb PHY_for port0 and port1 of Controller 0.
topeet RK3568 USB

Two USB 2.0 interfaces, where the lower one is shared with the RTL8723DU WiFi module.
The development board also has a USB 3.0 OTG interface, as shown in the schematic diagram below:


In the above schematic diagram,USB3_OTG0_VBUSDETis a high-level active detection signal used for OTG and Device mode identification. USB 3.0 OTG supports three modes: OTG mode, Device mode, and Host mode.
- In OTG mode: The working mode is automatically switched by detecting the ID pin level (high level for Device mode, low level for Host mode). Additionally, VBUSDET must be high to pull up USB3_OTG0DP and start enumeration.
- In Device mode: Only VBUSDET needs to be high to trigger enumeration, without detecting the ID pin.
- In Host mode: The ID and VBUSDET states are completely ignored. Note that although some products may only require Host mode, since the USB 3.0_OTG0_DP/DM interface also handles system firmware flashing and ADB debugging, it must be switched to Device mode during debugging and production, so the VBUSDET signal connection must be retained.
The system defaults to Device mode before U-Boot starts. After entering U-Boot, these three working modes can be configured according to actual needs.
The RK3568 chip is also equipped with a USB 3.0 HOST interface. By checking the baseboard schematic, as shown below, this USB 3.0 HOST interface is specifically reserved for the 5G module (RM500U-CN module). On the iTOP-RK3568 development board, the 4G and 5G modules share the same U58 socket, achieving compatibility through hardware design.

USB Bus Architecture and Device Interaction Mechanism
USB Topology
USB adopts a tree topology, which is a master-slave structure, i.e.,All devices are connected to the host via hubs, forming a tree-like structure, as shown in the figure below. USB only allows data communication between the host and devices; communication between devices is not permitted

Although USB hubs can expand the number of interfaces, their expansion capability is strictly limited. According to the USB 2.0 protocol specification, the entire system supports a maximum of 7 levels of cascading expansion (counting from the root hub), and the total number of connected devices must not exceed 127 (including the hub itself, with address 0 having a special role). This pyramid-shaped topology design (as shown in the figure below) ensures flexibility in interface expansion while avoiding signal attenuation and system overload through level restrictions.

USB Device State Transition

From the USB device not yet being connected, to being fully recognized by the USB Host and ensuring its normal functionality, the USB device will go through the following stages in sequence.
- Attached State: This is the stage where the device is physically connected to the USB host but not yet powered. This stage is primarily ensured by hardware.
- Powered State: This is the second stage, corresponding to a device connected to the USB host and just powered on. This stage is primarily ensured by hardware.
- Default State: When a USB device first connects to the host, it enters the default state. In the default state, the device waits for the host to send a reset signal.
- Address State: After receiving a reset signal, the device enters the address state. In this state, the host assigns a unique address to the device.
- Configured State: Once the device receives its address, it enters the configured state. In this state, the device can select its configuration descriptor, which determines how it communicates with the host.
- Suspend State: When the USB device is inactive or the host enters a low-power mode, the device can enter the suspend state. In the suspend state, the device consumes no or very little power.
USB Device Hardware Identification
How does the USB HOST detect the insertion of a USB device?
USB 2.0 is backward compatible with USB 1.0 and USB 1.1, and is divided into three modes: Low-speed, Full-speed, and High-speed.
- Full-speed Device
A full-speed device is connected in the following manner, as shown in the figure below, with the USB host side on the left and the USB device side on the right.

As shown in the figure above, on the left hub side, both data lines D+ and D- have 15kΩ pull-down resistors (Rpd), while on the right device side, the D+ data line has a 1.5kΩ pull-up resistor (Rpu).
When the D+ signal line transitions from low to high, the USB host can determine that a full-speed device has been inserted.
- Low-speed Device
A low-speed device is connected in the following manner, as shown in the figure below, with the USB host side on the left and the USB device side on the right.

As shown in the figure above, on the left hub side, the data lines D+ and D- both have 15kΩ pull-down resistors Rpd, while on the right device side, the data line D- has a 1.5kΩ pull-up resistor Rpu.
When the D- signal line transitions from low to high level, the USB host can determine that a low-speed device has been inserted.
- High-speed device
When a high-speed device is connected to the system, it is initially recognized as a full-speed device. Subsequently, the HOST detects the DEVICE to determine if it is a high-speed device, during which the HOST and DEVICE must mutually confirm. After confirmation, the system switches to high-speed mode. In high-speed mode, current transmission mode is used, and the pull-up resistor on D+ needs to be disconnected.
When a device is disconnected, its differential termination resistors disappear immediately, but high-speed data packets continue to be transmitted from the port where the device was connected. When these packets reach the unloaded path endpoint, strong reflections occur, and the reflected signals return to the hub interface, causing the differential voltage at the hub’s connected port to rise. When the differential signal level on D+ and D- of a high-speed device exceeds 625mV, it can be determined that the USB device has been disconnected.

When a USB host and USB device are connected, an equivalent pull-down resistor is generated on theSSRX+/-USB host and deviceR_Term, with a range of 18 to 30 ohms, formed bySSRX+/-the parallel combination of two 50-ohm equivalent pull-down resistors on each side.
In the figure below, the left circuit is the equivalent circuit when no device is connected, and the right circuit is the equivalent circuit when a USB device is connected.

From the figure above, when no device is connected on the left, the circuit model is actually an RC series circuit, with a charge/discharge time constantT = R_Detect * C_Parasitic. When a device is connected on the right, at this timeR_termexists, and the charge/discharge time constantT=(R_Detect+R_Term)(C_AC+C_Parasitic)Obviously, the latter is much larger than the former, so whether a device is connected can be
judged based on the time constant.
OTG Dual Role Switching
USB OTG can act as both HOST and Device.
The schematic diagram of the topeet RK3568 OTG interface circuit is shown below:

From the above figure, it can be seen that J48 is a USB socket. The USB OTG interface has 5 lines: 2 for data transmission (D+, D-), 1 power line (VBUS), 1 ground line (GND), and 1 ID line. The ID line is used to implement the OTG function, determining whether the currently connected device is a host (HOST) or a slave (SLAVE).
If a slave device (such as a USB flash drive) is connected, the development board’s USB device acts as the host, and the ID pin is pulled low.
- When
USB_OTG1_IDthe pin is at low level (i.e., the development board acts as the host), Q7 is cut off, and the EN pin of U14 is at high level,USB_OTG1_VBUSoutputting 5V voltage to power the slave device.
- When
If a host device (such as a computer) is connected, the development board’s USB device acts as the slave, and the ID pin remains at high level.
- When
USB_OTG1_IDthe pin is at high level (i.e., the development board acts as the slave), Q7 conducts, the EN pin of U14 is at low level, U14 stops working,USB_OTG1_VBUSand no 5V voltage is output.
- When
The Type-C interface on the iTOP-RK3399 baseboard is as follows


In the figure above,Typec_CC1andTypec_CC2The pins are used as ID pins, and their working principle is similar to that described earlier. After detecting the states of CC1 and CC2, the U24 chip sends an interrupt signal to the main controller and reads the chip register values via the I2C pins. Subsequently, the main controller controls the power supply pins based on this information.
USB Protocol
USB Descriptors
USB descriptors are structures that describe USB device information. The host reads these descriptors to identify the device type, functions, configuration, and other information, thereby correctly loading drivers and communicating. The main USB descriptors are:
- Device Descriptor
- Configuration Descriptor
- Interface Descriptor
- Endpoint Descriptor
Device Descriptor
The device descriptor contains basic information about the device, such as the vendor ID and product ID. It is the first information requested and returned when the device connects to the host, providing the basic characteristics of the device. In the Linux kernel, USB devices are described using theusb_devicestructure, and the USB device descriptor is defined as theusb_device_descriptorstructure. The device descriptor structure is as follows
1 | // include/uapi/linux/usb/ch9.h |
Configuration Descriptor
The configuration descriptor describes the different configurations supported by the device, including the number of interfaces, configuration number, power supply information, etc. Every USB device must have one configuration descriptor. Additionally, a USB device can have multiple configurations, but only one configuration is used per transfer process. USB configurations are described in the kernel using theusb_host_configstructure, while the USB configuration descriptor is defined as theusb_config_descriptorstructure, as shown below:
1 | // include/uapi/linux/usb/ch9.h |
Interface descriptor
The interface descriptor describes the characteristics of an interface within a configuration, including the number of endpoints, device class and subclass, etc. A USB configuration has one or more interface descriptors. USB interfaces are described in the kernel using theusb_interfacestructure, while the USB interface descriptor is defined as theusb_interface_descriptorstructure, as shown below:
1 | // include/uapi/linux/usb/ch9.h |
Endpoint Descriptor
An endpoint descriptor describes an endpoint in an interface. An endpoint is the termination point for data transfer between a device and a host. A specific endpoint can only belong to one of four transfer modes. A USB interface has zero or more endpoint descriptors (excluding endpoint 0). In the Linux kernel, USB endpoints are described using theusb_host_endpointstructure, while the USB endpoint descriptor is defined as theusb_endpoint_descriptorstructure. The endpoint descriptor structure is as follows:
1 | // include/uapi/linux/usb/ch9.h |
A USB device has a device descriptor, which determines how many configurations the device has, each configuration corresponding to a configuration descriptor; the configuration descriptor defines how many interfaces are in that configuration, each interface having a corresponding interface descriptor; the interface descriptor defines how many endpoints the interface has, each endpoint corresponding to an endpoint descriptor; the endpoint descriptor defines the endpoint’s size, type, etc. From this, we can see that the relationship between USB descriptors is hierarchical, with the device descriptor at the top, followed by the configuration descriptor, then the interface descriptor, and then the endpoint descriptor, as shown in the following figure:

For example:

It can be seen that the root hub contains one device descriptor, one configuration descriptor, one interface descriptor, and one endpoint descriptor. The information content in the figure directly corresponds to theusb_device_descriptor、usb_config_descriptor、usb_interface_descriptor、usb_endpoint_descriptorstructure.
USB communication data format
The USB communication data format uses packets as the basic unit, achieving reliable communication between the device and the host through a hierarchical structure (field -> packet -> transaction -> transfer).
Field
A field is the smallest unit of USB data, composed of binary bits with different functions. There are 7 types in total, as shown in the table below.
| Field Type | Function | Length | Example |
|---|---|---|---|
| Sync Field (SYNC) | Synchronizes the clock to ensure alignment between transmitter and receiver. | 8 bits (full/low speed) or 32 bits (high speed) | 0000 0001 (full speed) |
| Packet Identifier Field (PID) | Identifies the packet type (e.g., token, data, handshake), consisting of 4 valid bits + 4 complement check bits. | 8 bits | 0001 1000 (OUT packet) |
| Address Field (ADDR) | Device address (7 bits), supporting up to 127 devices. | 7 bits | 0101 010 (address 5) |
| Endpoint field (ENDP) | Endpoint number (4 bits), up to 16 endpoints per device | 4 bits | 0001 (endpoint 1) |
| Frame number field (FRAM) | Frame number (11 bits), increments every 1ms (full speed) or 125μs (high speed), used for synchronization | 11 bits | 0x7FF (maximum frame number) |
| Data field (DATA) | Transmitted data (0-1024 bytes), length determined by transfer type | 0-1024 bytes | 0x01 0x02 0x03 |
| CRC field (CRC) | Error checking, token packets and data packets use different algorithms | 5 bits (token packet) or 16 bits (data packet) | CRC-5 or CRC-16 |
Packet
USB is like a conveyor belt that can only queue (serial communication), where data (fields) must be sent one bit at a time in sequence.
To prevent data from getting lost, USB first packages data into small parcels (data packets), and then uses four different types of “delivery boxes” (packet structures) to distinguish purposes—such as those for files, commands, data transmission, etc.—ensuring each parcel is accurately delivered to its destination.
These four different types of “delivery boxes” correspond to four different packet structures, which are:Token packet、Data packet、Handshake packetandSpecial packet。
These four packet structures are distinguished by the identifier PID. In a USB packet, the PID field uses 8 bits, with the format as follows:

The first 4 bits represent the PID, and the last 4 bits are the complement of the first 4 bits. In the first 4 bits of the PID, bits 1 and 0 determine the category, while bits 3 and 2 further subdivide, as shown in the table below:
| PID Type | PID Name | PID[3:0] | Description |
|---|---|---|---|
| Token Type | OUT | 0001B | Notify device that data will be output |
| IN | 1001B | Notify device that data will be input | |
| SOF | 0101B | Notify device that this is a start-of-frame packet | |
| SETUP | 1101B | Notify device that a control transfer will begin | |
| Data Type | DATA0 | 0011B | Data packets of different types |
| DATA1 | 1011B | ||
| DATA2 | 0111B | ||
| MDATA | 1111B | ||
| Handshake Type | ACK | 0010B | Acknowledge |
| NACK | 1010B | Not Acknowledge | |
| STALL | 1110B | Suspend | |
| NYET | 0110B | Not Ready | |
| Special Type | PRE | 1100B | Preamble, this is a token packet |
| ERR | 1100B | Error, this is a handshake packet. | |
| SPLIT | 1000B | Split transaction (this is a token packet). | |
| PING | 0100B | PING test (this is a token packet). | |
| Reserved | 0000B | Reserved, not used. |
A complete data packet consists of multiple different fields.
All data packets start with a SYNC field, followed by a Packet Identifier (PID), and end with an End of Packet (EOP) signal. The bit fields in the middle vary for different types of packets, commonly including the target address (ADDR), target endpoint (ENDP), data, frame index, and CRC. The specific structure of each data packet needs to be analyzed based on the actual situation.
Transaction
The basic unit of USB transmission is the packet, and the type of packet is indicated by the PID. A single packet alone cannot transmit complete data. For example, to output data, an OUT token packet can be sent because it can specify the destination. However, an OUT token packet alone is not enough; data transmission also requires sending a DATA0 or DATA1 data packet.
When the device receives data, it also needs to reply with an ACK handshake packet. Therefore, a complete data transmission involves multiple packets, including token packets, data packets, and handshake packets. This complete data transmission process is called a transaction. There are different types of transactions; some require handshake packets, while others do not; some can transmit large amounts of data, while others can only transmit small amounts of data.
There are four types of transactions, which are:
- Bulk transaction
Bulk transactions are used to transmit large amounts of data, with guaranteed correctness but no guaranteed timeliness.
- Interrupt transaction
Interrupt transactions are used to transmit periodic, small amounts of data, with both correctness and timeliness guaranteed.
- Isochronous transaction
Real-time transactions are used to transmit real-time data, where data correctness is not guaranteed, but timeliness is guaranteed.
- Setup Transaction
A setup transaction is similar to a bulk transaction, except that the token packet is a SETUP token packet.
USB Transfer Types
- Control TransferIt is the most basic data transfer method in the USB protocol, mainly used for querying, configuring, and sending general commands to USB devices. Control transfer is bidirectional, and the data volume is usually small. Control transfer consists of setup transactions and bulk transactions, and all USB devices must support control transfer.
- Isochronous TransferUsed to transmit large amounts of data at a constant rate with requirements on service period. Generally, isochronous transfer is commonly used for audio and video devices, as these devices require high real-time performance. Isochronous transfer uses real-time transactions for data transmission.
- Interrupt TransferSuitable for transmitting small or medium amounts of data with a fixed transaction processing period. Generally, USB interrupt transfer is often used in HID human interface devices such as USB mice and USB keyboards, because these devices require fast response and a fixed transaction processing period, but have low data demand, which is the advantage of USB interrupt transfer. Interrupt transfer uses interrupt transactions for data transmission.
- Bulk TransferAlso called USB block transfer. Bulk transfer is suitable for transmitting large amounts of data, requiring correct transmission, but with no requirements on transmission time, rate, or real-time performance. Generally, bulk transfer is used for storage devices such as USB flash drives. Bulk transfer uses bulk transactions for data transmission.
Bits form fields, fields form packets, packets form transactions, and transactions form transfers.
USB Enumeration
After a USB device is connected to a USB host, the host uses the bus enumeration process to identify and manage the connected device.**USB enumeration is essentially the process of information exchange between the USB host and device.**During this process, the device reports its own parameters to the host, and the host obtains key information based on these parameters, thereby determining the specific type of device and the method of communication with it.
Based on the obtained information, the host can load the appropriate driver for the device. The USB device enumeration process is described as follows:
- After a USB device is inserted into a USB port, the host detects
D+/D-the voltage on the line, confirms that a device is connected, and the USB hub reports the connection of a USB device to the host via the interrupt IN channel. - After detecting the insertion of a USB device, the host resets the USB device. After reset, the USB device’s address is 0, allowing the host to communicate with the USB device using address 0.
- The host sends a standard request to get the device descriptor to endpoint 0 (the default endpoint) of the device at address 0 (i.e., the newly inserted USB device). After receiving the request, the USB device sends the device descriptor to the host. Upon receiving the device descriptor, the host returns a zero-length data acknowledgment packet (ACK).
- The host resets the device again. After reset, the host sends a set address request packet to endpoint 0 of the device at address 0, with the new device address contained in this packet. The host sends a request for status return, and the device returns a zero-length status packet.
- After receiving the status packet, the host sends an ACK packet to the device. Upon receiving the ACK, the device activates the new device address, and thereafter the host accesses the device using the new address.
- The host retrieves the device descriptor again. This time is slightly different from the first; it needs to obtain the full 18-byte device descriptor.
- Next, the host retrieves the configuration descriptor. After obtaining the configuration descriptor, the host retrieves the configuration set based on the total length of the configuration set within it. The configuration set includes the configuration descriptor, interface descriptor, endpoint descriptor, and so on. If there are also string descriptors, the system will retrieve them as well.
USB Waveform

In the figure above, it is a start-of-frame packet. The SYNC field starts with 0x80 (10000000) and is used for clock synchronization. The PID field value is 0xA5, marked as SOF in the figure, indicating that this is a start-of-frame packet.

On the left is a token packet. The SYNC field starts with 0x80 (10000000) to indicate clock synchronization. The PID field value is 0x69, marked as IN in the diagram, indicating that the device will be notified to input data. Next, the host requests data from endpoint 2.
In the diagram above, on the right is a handshake packet. The SYNC field starts with 0x80 (10000000) to indicate clock synchronization. The PID field value is 0x5A, marked as NAK in the diagram, indicating that the device is temporarily unable to respond.

This waveform is a control transfer request during the enumeration phase. The SYNC field starts with 0x80 (10000000) to indicate clock synchronization. The PID field is SETUP, indicating that the device will be notified to start a control transfer. Next are the address field Address=0x11 and the endpoint field Endpoint=0x00, indicating that the host sends a control request to endpoint 0. Finally, there is the CRC field, and the packet ends with EOP.

The SYNC field starts with 0x80 (10000000) to indicate clock synchronization. The PID field is DATA0, indicating the data stage used for control transfer.
Application Programming Basics
Introduction to libusb Library
libusb is a library written in C that provides a generic way to access USB devices. Through it, applications can conveniently access USB devices without needing to write USB device drivers. The libusb library has three features:
- Good portability, supporting Linux, macOS, and Windows systems.
- Simple and easy to use; applications do not require privileged mode or elevated permissions to access USB devices.
- Supports all USB protocols from USB 1.0 to USB 3.1, with the API interface remaining unchanged for ease of development.
Compiling libusb
Source code:
Local compilation
1 | sudo apt install autoconf automake libtool libudev-dev m4 |
In the examples directory of the libusb source code, libusb provides some official examples as follows:
1 | $ ls examples |
hotplugtest.cUsed to monitor the insertion and removal of USB devices in the system.listdevs.cRetrieves and displays the current USB device information of the system, including: VID, PID, bus number, device address, and port number.testlibusb.cUsed to print detailed information of the USB device list: including device descriptors, configurations, interfaces, and endpoint descriptors.
Cross Compilation
1 | make distclean |
Compile Examples
1 | make -C examples \ |
libusb API
libusb_init()
| Project | Description |
|---|---|
| Function Prototype | int libusb_init(libusb_context **ctx); |
| Parameters | libusb_context **ctx: Context pointer, can be NULL to use the default context |
| Return Value | Returns 0 on success; returns a negative error code on failure |
| Function Description | Initialize the libusb library, must be called before any other libusb operations |
libusb_get_device_list()
| Item | Description |
|---|---|
| Function Prototype | ssize_t libusb_get_device_list(libusb_context *ctx, libusb_device ***list); |
| Parameters | ctx: Contextlist: Output device array |
| Return Value | Returns the number of devices on success; returns a negative number on failure |
| Function Description | Get the list of currently connected USB devices |
libusb_free_device_list()
| Item | Description |
|---|---|
| Function Prototype | void libusb_free_device_list(libusb_device **list, int unref_devices); |
| Parameters | list: Device listunref_devices: Whether to decrease the reference count |
| Return value | None |
| Function description | Release device list |
libusb_open_device_with_vid_pid()
| Item | Description |
|---|---|
| Function prototype | libusb_device_handle *libusb_open_device_with_vid_pid(libusb_context *ctx, uint16_t vid, uint16_t pid); |
| Parameters | ctx: Contextvid: Vendor IDpid: Product ID |
| Return value | Returns device handle on success; returns NULL on failure. |
| Function Description | Find and open device by VID/PID. |
libusb_open()
| Item | Description |
|---|---|
| Function Prototype | int libusb_open(libusb_device *dev, libusb_device_handle **dev_handle); |
| Parameters | dev: Device pointerdev_handle: Output device handle |
| Return Value | Returns 0 on success; returns a negative number on failure. |
| Function Description | Open the specified USB device. |
libusb_get_device_descriptor()
| Item | Description |
|---|---|
| Function Prototype | int libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc); |
| Parameters | dev: Devicedesc: Output Device Descriptor |
| Return Value | Returns 0 on success; Returns a negative number on failure |
| Function Description | Get Device Descriptor |
libusb_get_config_descriptor()
| Item | Description |
|---|---|
| Function Prototype | int libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, struct libusb_config_descriptor **config); |
| Parameters | dev: Deviceconfig_index: Configuration Indexconfig: Output configuration descriptor |
| Return value | Returns 0 on success; Returns a negative number on failure |
| Function description | Get device configuration descriptor |
libusb_free_config_descriptor()
| Item | Description |
|---|---|
| Function prototype | void libusb_free_config_descriptor(struct libusb_config_descriptor *config); |
| Parameters | config: Configuration descriptor |
| Return value | None |
| Function description | Release configuration descriptor |
libusb_bulk_transfer()
| Item | Description |
|---|---|
| Function Prototype | int libusb_bulk_transfer(libusb_device_handle *dev_handle, unsigned char endpoint, unsigned char *data, int length, int *actual_length, unsigned int timeout); |
| Parameters | dev_handle: Device Handleendpoint: Endpoint Addressdata: Bufferlength: Lengthactual_length: Actual Lengthtimeout: Timeout (ms) |
| Return Value | Returns 0 on success; Returns a negative number on failure |
| Function Description | Performs USB bulk transfer |
libusb_interrupt_transfer()
| Item | Description |
|---|---|
| Function Prototype | int libusb_interrupt_transfer(libusb_device_handle *dev_handle, unsigned char endpoint, unsigned char *data, int length, int *actual_length, unsigned int timeout); |
| Parameters | dev_handle: Device Handleendpoint: Endpoint Addressdata: Bufferlength: Lengthactual_length: Actual Lengthtimeout: Timeout (ms) |
| Return Value | Returns 0 on success; Returns a negative number on failure |
| Function Description | Performs USB interrupt transfer |
libusb_set_auto_detach_kernel_driver()
| Item | Description |
|---|---|
| Function Prototype | int libusb_set_auto_detach_kernel_driver(libusb_device_handle *dev_handle, int enable); |
| Parameters | dev_handle: Device Handleenable: 1 Enable / 0 Disable |
| Return Value | Returns 0 on success; Returns a negative number on failure |
| Function Description | Automatically detach kernel driver |
libusb_claim_interface()
| Item | Description |
|---|---|
| Function Prototype | int libusb_claim_interface(libusb_device_handle *dev_handle, int interface_number); |
| Parameters | dev_handle: Device handleinterface_number: Interface number |
| Return value | Returns 0 on success; Returns a negative number on failure |
| Function description | Declares a device interface |
libusb_alloc_transfer()
| Item | Description |
|---|---|
| Function prototype | struct libusb_transfer *libusb_alloc_transfer(int iso_packets); |
| Parameters | iso_packets: Number of isochronous transfer packets |
| Return value | Returns a transfer pointer on success; Returns NULL on failure |
| Function Description | Allocate Asynchronous Transfer Structure |
libusb_fill_interrupt_transfer()
| Item | Description |
|---|---|
| Function Prototype | void libusb_fill_interrupt_transfer(struct libusb_transfer *transfer, libusb_device_handle *dev_handle,unsigned char endpoint, unsigned char *buffer, int length,libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) |
| Parameters | transfer: Transfer Objectdev_handle: Device Handleendpoint: Endpointbuffer: Bufferlength: Lengthcallback: Callback Functionuser_data: User Datatimeout: Timeout |
| Return Value | None |
| Function | Configure asynchronous interrupt transfer object |
libusb_submit_transfer()
| Item | Description |
|---|---|
| Function prototype | int libusb_submit_transfer(struct libusb_transfer *transfer); |
| Parameters | transfer: Configured transfer object |
| Return value | Returns 0 on success; Returns a negative number on failure |
| Function | Submit asynchronous transfer |
libusb_cancel_transfer()
| Item | Description |
|---|---|
| Function Prototype | int libusb_cancel_transfer(struct libusb_transfer *transfer); |
| Parameters | transfer: Transfer Object |
| Return Value | Returns 0 on success; Returns a negative number on failure |
| Function Description | Cancel asynchronous transfer |
libusb_free_transfer()
| Item | Description |
|---|---|
| Function Prototype | void libusb_free_transfer(struct libusb_transfer *transfer); |
| Parameters | transfer: Transfer Object |
| Return Value | None |
| Function Description | Release the transmission structure |
libusb_handle_events()
| Item | Description |
|---|---|
| Function Prototype | int libusb_handle_events(libusb_context *ctx); |
| Parameters | ctx: Context |
| Return Value | Returns 0 on success; Returns a negative number on failure |
| Function Description | Process pending events (blocking mode) |
Example
Example 1
1 |
|
Example 2
1 |
|
Example 3
1 |
|
USB driver development framework
In the Linux kernel, USB driver development is mainly divided into two categories:
- Host-side driver
- and Device-side driver.
The host-side driver is responsible for managing USB devices plugged into the host; the device-side driver is responsible for how the device communicates with the host as a USB device.
Since the term “USB devices drivers” can be confusing, people usually use “usb gadget driver” to describe USB device drivers.
At the hardware level, the USB controllers on the host side and device side are called the Host Controller and USB Device Controller (UDC), respectively, as shown in the figure below
1 | ┌─────────────────────┐ ┌─────────────────────┐ |
In Linux systems, USB drivers can be observed from both the host side and the device side. From the host side, the USB host controller driver is at the bottom layer of USB drivers, responsible for controlling the hardware. Above it runs the USB core layer, and above that is the USB device driver.
From the device side, USB device-side drivers have three layers: the UDC driver, the Gadget Function API, and the Gadget Function driver.
The USB driver architecture is shown in the following figure.

The Linux USB protocol stack is a layered architecture, with USB Device drivers on the left, USB Host drivers on the right, the application layer at the top, the drivers for different USB controllers and PHYs of Rockchip series chips at the bottom, and the USB core layer in the middle.
USB Keyboard Driver Example
Let the iTOP-RK3568 development board act as a USB host, connecting external USB devices such as a USB keyboard.
In Rockchip’s official kernel source code, USB keyboard and mouse drivers are enabled by default. To conduct subsequent USB keyboard driver development experiments, this default driver setting must be disabled. Since USB mice and keyboards are HID devices, we can open the kernel configuration interface by entering the following command to enable or disable the HID driver.
1 | ---> Device Drivers |
Disable the USB HID transport layer.
Its help information is as follows:
1 | CONFIG_USB_HID: |
struct usb_driver *driver
is the driver’s description structure, with its core members defined as follows:
1 | /** |
struct usb_device_id
In the main structure of the driverusb_driver, through.id_tablethe field binds the device table to the driver
1 | /** |
usb_register()
| Item | Description |
|---|---|
| Function prototype | int usb_register(struct usb_driver *driver); |
| Header file | #include <linux/usb.h> |
| Parameters | struct usb_driver *driver: Pointer to USB driver description structure |
| Return value | Returns 0 on success; Returns negative error code on failure |
| Function description | Registers the USB driver with the Linux USB subsystem, enabling the driver to respond to plug/unplug events of matching devices |
In theusb_registerfunction, it internally callsusb_register_driver, passing the driver structureusb_driverRegister to the kernel USB subsystem. Callusb_register_driverAfter, theusb_driverstructure is mounted to the USB bus (usb_bus_type) driver list, as shown below:
1 | /* use a define to avoid include chaining to get THIS_MODULE & friends */ |
usb_register_driver()
1 | /** |
usb_bus_typeThe structure has a match member,usb_device_matchthe function is responsible for the matching logic.
1 | struct bus_type usb_bus_type = { |
usb_device_match()
1 | static int usb_device_match(struct device *dev, struct device_driver *drv) |
The above function usesusb_match_idThe function matches the static device ID table,usb_match_idThe function implementation is as follows:
usb_match_id()
1 | /** |
In the above code,usb_match_one_idthe function is used to determine whether the USB interface matchesusb_device_idthe implementation of the function is as follows:
usb_match_one_id()
1 | /* returns 0 if no match, 1 if match */ |
According to theusb_device_idfield in thematch_flagsfield, determine the fields to match (such as VID, PID, interface class, etc.). An example matching table is shown below
1 | // Define the device matching table |
usb_deregister()
| Item | Description |
|---|---|
| Function prototype | void usb_deregister(struct usb_driver *driver); |
| Header file | #include <linux/usb.h> |
| Parameters | struct usb_driver *driver: Registered USB driver |
| Return value | None |
| Function Description | Unregister the driver from the USB subsystem and trigger the disconnect() callback for all matching devices |
USB Request Block
URB, which stands for USB Request Block, is the basic carrier used to describe communication with USB devices in USB device drivers, and is also a core data structure, similar to thesk_buffstructure in network device drivers. The URB structure is as follows:
1 | /** |
Each endpoint in a USB device processes a URB queue. The USB communication steps are as follows:
USB Communication Steps
The first step uses theusb_alloc_urbfunction to create a URB structure,usb_alloc_urbThe function description is as follows:
1 | struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags); |
usb_alloc_urbThe function is used to create a USB Request Block (URB) for managing data transfer requests of USB devices. URB is the core structure for data transfer in USB driver development, supporting the following transfer types:
- Control Transfer: Device initialization and configuration.
- Interrupt Transfer: Devices with high real-time requirements (e.g., keyboard, mouse).
- Bulk Transfer: Large-volume data transfer (e.g., USB flash drives).
- Isochronous Transfer: Real-time streaming data (e.g., cameras, audio devices).
Function parameters:
int iso_packets: Number of packets for isochronous transfer (only for isochronous transfer; set to 0 for other transfer types)gfp_t mem_flags: Memory allocation flags, specifying the memory allocation method (e.g.,GFP_KERNEL、GFP_ATOMIC)
Returns the allocated URB pointer on success, or NULL on failure.
Step 2: URB initialization, specified for a particular endpoint of a specific USB device. For interrupt URBs, use theusb_fill_int_urbfunction to initialize the URB,usb_fill_int_urbThe function is described as follows:
1 | static inline void usb_fill_int_urb(struct urb *urb, // Target URB pointer |
usb_fill_int_urbFunction used to initialize an interrupt transfer URB.
Function parameters:
struct urb *urb: Pointer to the URB to be initializedstruct usb_device *dev: Pointer to the USB device to which this URB will be sentunsigned int pipe: The specific endpoint of the USB device to which this URB will be sent
The pipe parameter is created using the following function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 /* Create various pipes... */
void *transfer_buffer: Pointer to the buffer for sending or receiving dataint buffer_length: Istransfer_bufferThe size of the buffer pointed to by the pointerusb_complete_t complete_fn: Transfer completion callback functionvoid *context: The “context” of the completion handlerint interval: Context data passed to the callback function (usually a driver private structure pointer)
For bulk URBs, use usb_fill_bulk_urb() function to initialize. For control URBs, use usb_fill_control_urb() function to initialize.
After completing the first and second steps of creating and initializing the URB, the URB can be submitted to the USB core usingusb_submit_urbfunction.usb_submit_urbThe function is described as follows
1 | int usb_submit_urb(struct urb *urb, gfp_t mem_flags); |
Function purpose:
Submit the initialized USB Request Block (URB) to the kernel’s USB subsystem to start data transfer. Upon completion of the transfer, the callback function specified in the URB is triggered.
Function parameters:
struct urb *urb: Pointer to the URBgfp_t mem_flags: Memory allocation flags, specifying the memory allocation method (e.g.,GFP_KERNEL、GFP_ATOMIC)GFP_ATOMIC: In interrupt handlers, bottom halves,tasklet, timer handlers, and URB completion functions, when the caller holds a spinlock or read-write lock, and when the driver setscurrent->stateto non-TASK_RUNNINGthis flag should be used.GFP_NOIO: This flag should be used in the block I/O and error handling paths of storage devices.GFP_KERNEL: If there is no reason to useGFP_ATOMICandGFP_NOIO, then useGFP_KERNEL。
Returns 0 on success, and a negative error code on submission failure.
Step 4: Submit the USB host controller driver specified by the USB core.
Step 5: Processed by the USB host controller, performing a transfer to the USB device. Steps 4 and 5 are completed by the USB core and host controller and are not controlled by the USB device driver.
Step 6: When the URB processing is complete, the USB host controller driver notifies the USB device driver.
The data reported by a USB keyboard is 8 bytes, which represents the data of a USB key being pressed and then released.

The format is as follows:
| Offset | Size | Description |
|---|---|---|
| 0 | Byte | Specific function keys |
| 1 | Byte | Reserved bits |
| 2 | Byte | Key 1 |
| 3 | Byte | Button 2 |
| 4 | Byte | Button 3 |
| 5 | Byte | Button 4 |
| 6 | Byte | Button 5 |
| 7 | Byte | Button 6 |
Here, buff[0] is a bit field, where each bit corresponds to the function of a special button. When a bit is set to 1, it indicates that the button is pressed. The bit field structure is shown in the following table:
| Bit | Bit Length | Description |
|---|---|---|
| 0 | 1 | Left Ctrl |
| 1 | 1 | Left Shift |
| 2 | 1 | Left Alt |
| 3 | 1 | Left GUI(Windows/Super key) |
| 4 | 1 | Right Ctrl |
| 5 | 1 | Right Shift |
| 6 | 1 | Right Alt |
| 7 | 1 | Right GUI(Windows/Super key) |
Example
1 |
|
UDC and Gadget Driver
USB gadget refers to the driver framework on the USB device side, primarily used to configure a USB-capable device as a slave device to communicate with the host.
Taking a smartphone as an example, when the phone is connected to a computer via a USB cable, the phone acts as a USB Gadget. In the following experiments, we will replace the phone with a development board to act as a USB Gadget.
According to the USB driver development framework diagram below, the Gadget framework provides a set of standardized API interfaces (Gadget Function API), which are implemented at the bottom layer by the USB Device Controller (UDC) driver.
1 | ┌─────────────────────┐ ┌─────────────────────┐ |
It is worth noting that different UDCs (USB Device Controllers) may require different drivers to be adapted, and even different hardware boards based on the same UDC may require modifications to the driver. The USB Gadget module in the kernel
1 | DeviceDrivers---> |
Configure the iTOP-RK3568 development board as a USB network card device
- Enable the RNDIS driver (if compiled as a module, compiling the kernel alone will not generate .ko files; to avoid cumbersome mounting steps)
- Next, select some network protocol configurations
As shown in the figure:

Next, find USB Gadget precomposed configurations and select ‘with CDC Ethernet support’, as shown in the figure below

After configuration is complete, recompile the kernel source code, then reflash the kernel image. After the development board completes the reboot process, executeifconfig -athe command to list network configuration interfaces, and you will be able to observe the usb0 network interface.
Host and PHY driver

In the Linux kernel source code, USB host and PHY drivers are loaded by default. The USB PHY module is located at the following path in the kernel.
1 | Device Drivers---> |
- The USB 2.0 PHY uses Innosilicon IP, so you should select “Rockchip INNO USB2PHY Driver”.
- The USB 3.0 PHY uses Type-C, so you should select “Rockchip TYPEC PHY Driver”.
- The USB 3.0 PHY uses Innosilicon USB 3.0 PHY, so you should select “Rockchip INNO USB 3.0 PHY Driver”.
The configuration of the USB Host module is located at the following path in the kernel.
1 | Device Drivers---> |
You must select the USB Support option to enable the USB module and perform further configuration. To support USB Host, first select<*>Supportfor Host-side USBoption, and then the following Host-related configurations will appear, where:
- For USB Host 1.1, select the OHCI Driver configuration.
- For USB Host 2.0, select the EHCI Driver configuration.
- For USB Host 3.0, select the xHCI Driver configuration.
After confirming that the above modules are selected, proceed to configure the device tree.
1 | // rk3568.dtsi |
To configure the USB 3.0 OTG function, the device tree configuration is as follows:
1 | // topeet-rk3568-linux.dtsi |
HOST1 is configured as a USB 3.0 Host function, the device tree configuration is as follows:
1 | // topeet-rk3568-linux.dtsi |
HOST1 is configured as a USB 2.0 Host, the device tree configuration is as follows:
1 | &usbhost30 { |
For more detailed information, please refer to Rockchip’s official documentation.rk356x_linux/doc/Common/USB/Rockchip_Developer_Guide_USB_CN.pdf。
4G Module Porting

The U58 interface in the above figure is compatible with both 4G and 5G modules. Here, it connects to the CPU via a USB interface, with the purpose ofThe CPU mounts the EM05-CE (4G) module to the system via the USB interface, emulating the EM05-CE as a network device and 4 USB serial port devices.
- The serial port devices are used for performing initialization operations, error detection, reading positioning information, etc., on the module.
- The network device serves to use the EM05-CE as a general network card device for upper-layer socket calls.
For porting 4G or 5G modules, please refer to Quectel’s technical documentation.
Kernel Driver Porting
First, modify the driver to support the PID and VID of the EM05-CE module. Opendrivers/usb/serial/option.cfile,struct usb_device_id option_ids[]add the following code to the array:
1 | static const struct usb_device_id option_ids[] = { |
Modifydrivers/usb/serial/usb_wwan.cfile, add the zero-packet mechanism.
1 | static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port, |
Then add the restart sleep mechanism, opendrivers/usb/serial/option.cfile, and add the following statement:
1 | static struct usb_serial_driver option_1port_device = { |
For modules accessed via USB interface, integrate the USB driver into the Linux kernel. We need to configure the kernel to select the USB-to-serial driver supporting GSM and CDMA modules. Select the driver at the following path. This driver virtualizes in the kernel/dev/ttyUSB0、/dev/ttyUSB1、/dev/ttyUSB2、/dev/ttyUSB3these serial ports, which serve as error diagnosis, GPS information interface, and module communication interface respectively, as shown below.
1 | --> Device Drivers |
Dial-up Script Porting
First, configure the kernel to support PPP dial-up. In menuconfig, select the following options as shown below.
1 | --> Device Drivers |
After connecting the hardware, power on the development board. Switch to ECM dial-up and enter the commandcat /dev/ttyUSB2 & echo -e “AT+QCFG=\”usbnet\”,1\r\n” > /dev/ttyUSB2, then restart the development board.
After the development board restarts, it will automatically dial up. Then enter the “ifconfig” command in the debug serial terminal to see the usb0 node, and then enterping www.baidu.comcommand to check if the network is reachable via ping.
In addition to the ECM dial-up mentioned above, we can also use the quectel-CM tool for dial-up
After compilation, the quectel-CM executable is generated. We copy this program to the development board for the following tests. Since ECM dial-up was used in the previous operation, before using quectel-CM dial-up, we need to entercat /dev/ttyUSB2 & echo -e “AT+QCFG=\”usbnet\”,0\r\n” > /dev/ttyUSB2command to switch to RmNET dial-up. Then run the quectel-CM executable, as shown below:
After the dial-up script is executed, enter in the serial terminalifconfigcommand to see an additional wwan0 node.

