Linux USB driver framework analysis

Source: Internet
Author: User

Linux USBDevice Driver Framework Analysis

In fact, all the device drivers in Linux follow the same Convention-representing the structure of the driver program (which is more appropriate with the driver, it should be called a better driver, the struct should contain all the resources required by the driver. In terms of terms, it is the attributes and members of the drive object. Since the Linux kernel is written in C, We also analyze the code according to this structured idea, but I still want to elaborate these details from the OO perspective. The name of this struct is determined by the driver developer. For example, the mouse may have a struct called mouse_dev, And the keyboard may be composed of a keyboard_dev struct (Dev for device, we only do device drivers ). This time, we will analyze a USB-skeleton (the skeleton of the USB driver) in the Linux kernel source code. Naturally, the device structure he defines is called USB-skel:
Struct usb_skel {
Struct usb_device * udev;
Struct usb_interface * interface;
Struct semaphore limit_sem;
Unsigned char * bulk_in_buffer;
Size_t bulk_in_size;
_ U8 bulk_in_endpointaddr;
_ U8 bulk_out_endpointaddr;
Struct kref;
};
Here we need to explain some detailed USB protocol specifications. USB can automatically monitor the device and call the corresponding driver to process the device, so its specification is actually quite complicated. Fortunately, we don't have to worry about most of the details, because Linux has provided corresponding solutions. As far as I understand it, the USB driver is divided into two parts, one of which is the USB bus driver. the Linux kernel is ready, and we can ignore it, but we should at least understand its functions. In the image, the USB bus driver is equivalent to laying out a path so that all information can be reached through this USB channel, which is completed by usb_core. When a USB device is connected to a USB controller interface, usb_core Checks information about the device, such as the manufacturer ID and product ID, or the class, subclass, and Protocol of the device, to determine which driver should be called to process the device. We don't need to worry about the complicated details. What we need to do is another work-USB device driver. That is to say, we are waiting for usb_core to tell us that we have to work.
From the perspective of developers, each USB device consists of several configurations, and each configuration can have multiple interfaces ), each interface has multiple settings (not given in the setting diagram), and the interface may have no or multiple endpoints ). USB data exchange is performed through the endpoint. A one-way pipeline is established between the host and each endpoint to transmit data. These interfaces can be divided into four types:
Control)
Used to configure devices, obtain device information, send commands, or obtain device status reports.
Interrupt)
When a USB host requires a device to transmit data, the interrupt endpoint transmits a small amount of data at a fixed rate. It is also used to send data to a USB device to control the device. Generally, it is not used to transmit a large amount of data.
Bulk)
It is used for reliable transmission of a large amount of data. If the space on the bus is insufficient to send the entire batch package, it will be divided into multiple packages for transmission.
Isochronous)
The transmission of a large amount of data is not reliable, and the arrival of data is not guaranteed, but the constant data flow is ensured, which is mostly used for data collection.
In Linux, struct usb_host_endpoint is used to describe the USB endpoint. Each usb_host_endpoint contains a struct usb_endpoint_descriptor struct, which contains information about this endpoint and custom information of the device, including:
Bendpointaddress (B for byte)
The eight-bit endpoint address also hides the information of the endpoint direction (as mentioned earlier, the endpoint is unidirectional) and can be determined by the mask usb_dir_out and usb_dir_in.
Bmattributes
The type of the endpoint. Combined with usb_endpoint_xfertype_mask, you can determine whether the endpoint is usb_endpoint_xfer_isoc (_endpoint_xfer_bulk (batch) or usb_endpoint_xfer_int (Interrupt ).
Wmaxpacketsize
The maximum number of bytes processed at a time by the endpoint. The sent bulk package can be greater than this value, but will be split and transmitted.
Binterval
If the endpoint is of the interrupt type, this value is the interval setting of the endpoint, in milliseconds.
Logically, functions of a USB device are divided by interfaces. For example, a USB speaker may have two interfaces: one for keyboard control and the other for audio stream transmission. In fact, this type of device requires two different drivers to operate, one controlling the keyboard and the other controlling the audio stream. However, there are also some exceptions. For example, a bluetooth device requires two interfaces. The first interface is used for the transfer of ACL and event, and the other interface is used for the SCO link, but the two are controlled by one driver. In Linux, the interface is described using struct usb_interface. The following are important fields in the struct:
Struct usb_host_interface * altsetting (note that it is not usb_interface)
In fact, I understand that it should be set for each interface, although the name is a bit strange. This field is a set array (an interface can have multiple settings). Each usb_host_interface contains an endpoint configuration defined by struct usb_host_endpoint. However, the configuration order is not fixed.
Unsigned num_altstting
Optional quantity, that is, the number of elements in the array indicated by altsetting.
Struct usb_host_interface * cur_altsetting
The setting of the Current Activity points to one of the altsetting arrays.
Int minor
The secondary device number allocated by the USB core when the USB driver bound to this interface uses the USB master device number. Valid only after successful call of usb_register_dev.
Except that it can be described using struct usb_host_config, I do not know much about the configuration so far. The entire USB device can be described using struct usb_device, but basically it will only be used to initialize the function interface, what we actually use is a custom struct we mentioned earlier.

After learning about some USB specifications, let's take a look at the Linux driver framework. In fact, Linux Device Drivers, especially the hotplug USB device drivers, will be compiled into modules and then mounted to the kernel as needed. Writing a LINUX module is not complicated. Take helloworld as an example:
# Include # Include Module_license ("GPL ");
Static int hello_init (void)
{
Printk (kern_alert "Hello world! /N ");
Return 0;
}
Static int hello_exit (void)
{
Printk (kern_alert "goodbye! /N ");
}
Module_init (hello_init );
Module_exit (hello_exit );
This simple program tells everyone how to write a module. module_license tells the kernel about the copyright of this module. In many cases, GPL or BSD or two are used, because a private module is generally difficult to get help from the community. Module_init and module_exit are used to launch functions to the initialization functions and modules of the kernel registration module. As shown in the program, the initialization function is hello_init, And the exit function is hello_exit.
In addition, to compile a module, you usually need to use the makefile in the kernel source code tree. Therefore, the makefile of the module can be written as follows:
Ifneq ($ (kernelrelease ),)
OBJ-M: = Hello. O # usb-dongle.o
Else
Kdir: =/usr/src/Linux-headers-$ (shell uname-R)
Bdir: = $ (shell PWD)
Default:
$ (Make)-C $ (kdir) M = $ (PWD) Modules
. Phony: clean
Clean:
Make-C $ (kdir) M = $ (bdir) clean
Endif
You can use insmod and rmmod to verify the mounting and uninstalling of the module, but you must use the root identity to log on to the command line. It is not possible to use a common user to add Su or sudo for testing on Ubuntu.

The following describes the source code of USB-skeleton. This sample program can be found in the linux-2.6.17/Drivers/USB, other versions of the kernel program source code may be different, but the difference is not big. You can first look at the source code, first with a general impression.
As mentioned earlier, the module first registers initialization and destruction functions with the kernel:
Static int _ init usb_skel_init (void)
{
Int result;
Result = usb_register (& skel_driver );
If (result)
Err ("usb_register failed. Error Number % d", result );
Return result;
}
Static void _ exit usb_skel_exit (void)
{
Usb_deregister (& skel_driver );
}
Module_init (usb_skel_init );
Module_exit (usb_skel_exit );
Module_license ("GPL ");
From the code, the init and exit functions are only used to register the driver. The structure of the driver is the standard structure defined by the system, struct usb_driver. The method of registration and cancellation is very simple, usb_register (struct * usb_driver) and usb_deregister (struct * usb_driver ). What does this struct need to do? He wants to provide several function portals to the system, with the driver name:
Static struct usb_driver skel_driver = {
. Name = "skeleton ",
. Probe = skel_probe,
. Disconnect = skel_disconnect,
. Id_table = skel_table,
};
In the code, usb_driver needs to initialize four things: Module name skeleton, probe function skel_probe, disconnect function skel_disconnect, and id_table.
Before explaining each member of skel_driver, let's take a look at another struct. The struct name is customized by the developer. It describes all the resources and statuses of the driver:
Struct usb_skel {
Struct usb_device * udev;
Struct usb_interface * interface;
Struct semaphore limit_sem;
Unsigned char * bulk_in_buffer;
Size_t bulk_in_size;
_ U8 bulk_in_endpointaddr;
_ U8 bulk_out_endpointaddr;
Struct kref;
};
Let's first make a simple analysis on this usb_skel. It has a structure udev describing the USB device, an interface, and semaphore (semaphore) limit_sem for concurrent access control, the buffer bulk_in_buffer used to receive data and its size bulk_in_size are input and output port addresses bulk_in_endpointaddr and bulk_out_endpointaddr in batches, and finally a reference counter used by the kernel. Their role will be seen in the code below.
Let's look back at skel_driver.
Name is used to tell the kernel module the name. It is used by the system after registration and has little to do with us.
Id_table is used to notify the kernel of the devices supported by this module. The USB sub-system identifies the device by combining the production ID and vendor ID of the device, or by combining the class, subclass, and Protocol of the device, and calls the relevant driver for processing. Let's see what this id_table is:
# Define usb_skel_vendor_id 0xfff0
# Define usb_skel_product_id 0xfff0
Static struct usb_device_id skel_table [] = {
{Usb_device (usb_skel_vendor_id, usb_skel_product_id )},
{}
};
Module_device_table (USB, skel_table );
The first parameter of module_device_table is the device type. If it is a USB device, it is naturally USB (if it is a PCI device, it will be PCI, the two subsystems use the same macro to register the supported devices. This involves the driver of the PCI device ). The following parameter is the device table. The last element of the device table is null and is used to identify the end of the device table. The Code defines that the usb_skel_vendor_id is 0xfff0, And the usb_skel_product_id is 0xfff0. That is to say, when a device is connected to a hub, the USB subsystem checks the vendor ID and product ID of the device, if their values are 0xfff0, the subsystem will call this skeleton module as the device driver.

Probe is a function automatically called by the USB subsystem. When a USB device is connected to a hardware hub, the USB subsystem identifies a device to call the probe (PROBE) function of the corresponding Driver Based on the Combination of Production ID and vendor ID or the combination of the device class, subclass, and Protocol. For skeleton, it is skel_probe. The system will pass a usb_interface * to the test function and a struct usb_device_id * as the parameter. They are the Interface Description of the USB device (usually the interface No. 0th of the device, and the default setting of this interface is also set No. 0th) and its device ID (including the vendor ID and production ID ). The probe function is relatively long. We will analyze this function in segments:
Dev-> udev = usb_get_dev (interface_to_usbdev (Interface ));
Dev-> interface = interface;
After initializing some resources, you can see the first key function call-interface_to_usbdev. The same USB _ interface is used to obtain the device description structure of the device where the interface is located. To obtain a usb_device, you only need to use interface_to_usbdev. However, to increase the reference count for this usb_device, we should perform a usb_get_dev operation to increase the reference count, when the device is released, usb_put_dev is used to reduce the reference count. Here, we need to explain that the reference count value is the count of this usb_device, not the Count of this module, and the count of this module should be maintained by kref. Therefore, probe has initialized kref at the beginning. In fact, the kref_init operation not only initializes kref, but also sets it to 1. Therefore, the error handling code contains kref_put, which reduces the kref count by 1. If the kref count is already 0, kref will be released. The second parameter of kref_put is a function pointer pointing to a cleanup function. Note: The pointer cannot be empty or kfree. This function will be called when the last reference to kref is released (if I do not understand it accurately, please correct it ). The following is a comment and code in the kernel source code:
Int kref_put (struct kref * kref, void (* release) (struct kref * kref ))
{
Warn_on (release = NULL );
Warn_on (release = (void (*) (struct kref *) kfree );
If (atomic_read (& kref-> refcount) = 1) |
(Atomic_dec_and_test (& kref-> refcount ))){
Release (kref );
Return 1;
}
Return 0;
}
When we execute the open operation, we need to increase the kref count. We can use kref_get to complete it. All operations on struct kref have kernel code to ensure atomicity.
After obtaining the usb_device, We need to initialize the status and resources of the custom usb_skel. The task of this part is to register the endpoint of the USB device with usb_skel. Here, you may need to add the following knowledge about usb_interface_descriptor, but because the kernel source code does not comment much on this struct, you can only rely on personal guesses. In a usb_host_interface structure, there is a usb_interface_descriptor called DESC Member, which should be used to describe some attributes of the interface. bnumendpoints is an 8-bit (B for byte) number, it represents the number of endpoints of the interface. Probe then traverses all endpoints, checks their types and directions, and registers them to usb_skel.
Iface_desc = interface-> cur_altsetting;
For (I = 0; I desc. bnumendpoints; ++ I ){
Endpoint = & iface_desc-> endpoint. DESC;
If (! Dev-> bulk_in_endpointaddr &&
(Endpoint-> bendpointaddress & usb_endpoint_dir_mask) = usb_dir_in )&&
(Endpoint-> bmattributes & usb_endpoint_xfertype_mask) = usb_endpoint_xfer_bulk )){
Buffer_size = le16_to_cpu (endpoint-> wmaxpacketsize );
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 ){
Err ("cocould not allocate bulk_in_buffer ");
Goto error;
}
}
If (! Dev-> bulk_out_endpointaddr &&
(Endpoint-> bendpointaddress & usb_endpoint_dir_mask) = usb_dir_out )&&
(Endpoint-> bmattributes & usb_endpoint_xfertype_mask) = usb_endpoint_xfer_bulk )){
Dev-> bulk_out_endpointaddr = endpoint-> bendpointaddress;
}
}
If (! (Dev-> bulk_in_endpointaddr & Dev-> bulk_out_endpointaddr )){
Err ("cocould not find both bulk-in and bulk-out endpoints ");
Goto error;
}

The next step is to register some information that will be used in the future with the system. First, let's introduce usb_set_intfdata (). He registers a data file with the kernel. The data structure can be arbitrary. This program registers a usb_skel structure with the kernel, it is the initialized one we just saw. This data can be obtained later using usb_get_intfdata.
Usb_set_intfdata (interface, Dev );
Retval = usb_register_dev (interface, & skel_class );
Then we register a skel_class structure with this interface. What is this structure? Let's see what this is:
Static struct usb_class_driver skel_class = {
. Name = "skel % d ",
. Fops = & skel_fops,
. Minor_base = usb_skel_minor_base,
};
It is actually a system-defined structure that contains a name, a file operation structure, and a reference value of the device number. In fact, it is a function that defines the true completion of device Io operations. So his core content should be skel_fops. Here I would like to add some personal estimates: because a USB device can have multiple interfaces and each interface may have different I/O operations, you must register a USB _ class_driver with the system, instead of device. Therefore, the first parameter of usb_register_dev is interface, and the second parameter is a USB _ class_driver. Generally, the Linux system uses the main device number to identify the driver program of a certain type of equipment, and uses the sub-device number management to identify specific devices. The driver can distinguish different devices according to the sub-device number, the sub-device here is actually used to manage different interfaces, but because this example only has one interface, this conjecture cannot be verified in the code.
Static struct file_operations skel_fops = {
. Owner = this_module,
. Read = skel_read,
. Write = skel_write,
. Open = skel_open,
. Release = skel_release,
};
The file operation structure defines the read/write, open, and release of the device (release is usually used for USB devices ). They are all function pointers pointing to the four functions skel_read, skel_write, skel_open, and skel_release respectively. The four functions should be implemented by developers themselves.
When the device is removed from the hub, the USB subsystem will automatically call disconnect, which does not do much. The most important thing is to cancel the data of class_driver (the device number is returned) and interface:
Dev = usb_get_intfdata (Interface );
Usb_set_intfdata (interface, null );
Usb_deregister_dev (interface, & skel_class );
Then, he will use kref_put (& Dev-> kref, skel_delete) to clean up. For details about kref_put, refer to the previous article.
So far, we have analyzed the main operations required by the USB subsystem. The next section will discuss the I/O operations on the USB device.
When talking about the I/O operation of the USB sub-system, you have to say that the USB request block is urb. In fact, we can make a metaphor that a USB Bus is like a highway. goods, people, and so on can be seen as the data that the system interacts with the device, while urb can be seen as a car. At the beginning, we introduced the USB specification details. We have said that there are four different types of USB endpoints, that is, there are four types of data that can flow on this highway. But this is not required for cars, so urb can carry four types of data, but you must first tell the driver what you want to transport and what the destination is. Now let's take a look at the specific content of struct urb. It has a lot of content. In order not to mislead you, you 'd better take a look at the comments of the kernel source code. For details, see include/Linux/USB. h under the source code tree.
Here we will focus on several key fields in the program:
Struct usb_device * Dev
The destination device sent by urb.
Unsigned int Pipe
An MPs queue number that records the endpoint of the target device and the MPs queue type. Each pipe has only one type and one direction. It corresponds to the endpoint of the target device. We can use the following functions to obtain the pipe number and set the pipe type:
Unsigned int usb_sndctrlpipe (struct usb_device * Dev, unsigned int endpoint)
Sets the specified endpoint of the specified USB device as a control out endpoint.
Unsigned int usb_rcvctrlpipe (struct usb_device * Dev, unsigned int endpoint)
Sets the specified endpoint of the specified USB device as a control in endpoint.
Unsigned int usb_sndbulkpipe (struct usb_device * Dev, unsigned int endpoint)
Set the specified endpoint of the specified USB device to a batch out endpoint.
Unsigned int usb_rcvbulkpipe (struct usb_device * Dev, unsigned int endpoint)
Set the specified endpoint of the specified USB device to a batch out endpoint.
Unsigned int usb_sndintpipe (struct usb_device * Dev, unsigned int endpoint)
Set the specified endpoint of the specified USB device to an out-of-interruption endpoint.
Unsigned int usb_rcvintpipe (struct usb_device * Dev, unsigned int endpoint)
Set the specified endpoint of the specified USB device to an out-of-interruption endpoint.
Unsigned int usb_sndisocpipe (struct usb_device * Dev, unsigned int endpoint)
Sets the specified endpoint of the specified USB device as an out-of-waiting endpoint.
Unsigned int usb_rcvisocpipe (struct usb_device * Dev, unsigned int endpoint)
Sets the specified endpoint of the specified USB device as an out-of-waiting endpoint.
Unsigned int transfer_flags
When DMA is not used, it should be transfer_flags | = urb_no_transfer_dma_map (according to the code understanding, hopefully there is no error ).
Int status
When an urb sends data to the device, the urb will be returned to the driver by the system, and the urb of the driver will be called to complete the callback function processing. At this time, status records the status of this data transmission, for example, whether the transfer is successful or not. If it succeeds, it will be 0.
To be able to deliver goods, of course, there must be a car first, so the first step is to create urb:
Struct urb * usb_alloc_urb (INT isoc_packets, int mem_flags );
The first parameter is the number of equal-time packages. If it is not a carry-on package, it should be 0. The second parameter is the same as the kmalloc flag.
To release an urb, you can use:
Void usb_free_urb (struct urb * urb );
To carry data, we also need to tell the driver the destination information and the goods to be transported. for different data, the system provides different functions. For urb interruption, we use
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 );
Here, we need to explain that transfer_buffer is a buffer for data to be sent/received, buffer_length is its length, complete is the entry to the callback function of urb, and context is defined by the user, the data may be used in the callback function. interval is the interval at which the urb is scheduled.
For batch urb and control urb, we use:
Void usb_fill_bulk_urb (struct urb * urb, struct usb_device * Dev, unsigned int pipe,
Void * transfer_buffer, int buffer_length, usb_complete_t complete,
Void * context );
Void usb_fill_bulk_urb (struct urb * urb, struct usb_device * Dev, unsigned int pipe,
Unsigned char * setup_packet, void * transfer_buffer,
Int buffer_length, usb_complete_t complete, void * context );
The control package has a special parameter setup_packet, which points to the data of the Set datagram to be sent to the endpoint.
The system does not have a special Fill function for the hour urb and can only assign values to each urb field.
With a car and a driver, the next step is to start shipping the goods. We can use the following function to submit urb.
Int usb_submit_urb (struct urb * urb, int mem_flags );
Mem_flags has several types: gfp_atomic, gfp_noio, and gfp_kernel. We usually use gfp_atomic to interrupt the context.
When our truck is loaded, the system will call it back, call urb to complete the callback function, and pass the car as a function to the driver. Check the status field in the callback function to check whether the data is successfully transmitted. The following are the details of using urb to transmit data.
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;
Retval = usb_submit_urb (urb, gfp_kernel );
Here, skel_write_bulk_callback is a complete callback function, and the main thing it does is to check the data transmission status and release urb:
Dev = (struct usb_skel *) urb-> context;
If (urb-> Status &&! (Urb-> Status =-enoent | urb-> Status =-econnreset | urb-> Status =-eshudown )){
Dbg ("% s-nonzero write bulk status terminated ed: % d", _ FUNCTION __, urb-> status );
}
Usb_buffer_free (urb-> Dev, urb-> transfer_buffer_length,
Urb-> transfer_buffer, urb-> transfer_dma );
In fact, if the data volume is small, it may not necessarily use trucks to deliver the goods. The system also provides a transmission method that does not use urb, the read operation of USB-skeleton is implemented in this way:
Retval = usb_bulk_msg (Dev-> udev,
Usb_rcvbulkpipe (Dev-> udev, Dev-> bulk_in_endpointaddr ),
Dev-> bulk_in_buffer,
Min (Dev-> bulk_in_size, count ),
& Bytes_read, 10000 );
If (! Retval ){
If (copy_to_user (buffer, Dev-> bulk_in_buffer, bytes_read ))
Retval =-efault;
Else
Retval = bytes_read;
}
The program uses usb_bulk_msg to transmit data. Its prototype is as follows:
Int usb_bulk_msg (struct usb_device * usb_dev, unsigned int pipe, void * data,
Int Len, int * actual length, int timeout)
This function will block waiting for data transmission to complete or wait for timeout. Data is the input/output buffer, Len is its size, actual length is the actual size of transmitted data, and timeout is the blocking timeout.
The system provides another function for controlling data. Its prototype is:
Int usb_contrl_msg (struct usb_device * Dev, unsigned int pipe, _ u8 request,
_ U8 requesttype, _ 2010value, _ 2010index, void * data,
_ 2010size, int timeout );
Request is the USB request value that controls messages, requesttype is the USB request type that controls messages, value is the USB message value that controls messages, and index is the USB message index that controls messages. What is it, it is not very clear for the moment, I hope you will provide instructions.
Now, the analysis of the USB driver framework in Linux is complete.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.