The first contact with the OS-related device driver writing, it is very interesting, in order to not forget what I have seen, notes and summary of course indispensable, not to mention I decided to work for embedded. Well, to get to the next step, I'll talk about the harvest of this time and share with you the driver development of Linux. But this time only for Linux USB subsystem for analysis, because Friday research boss reminders. Of course, you'll also mention other driver writing in passing.
In fact, Linux's device drivers follow a convention--a structure that characterizes the driver (which is more appropriate for the driver, which should be called a drive-better), which should contain all the resources required by the driver. In terms of the attributes and members that this drive object has. Because the Linux kernel is written in C, we also analyze the code in terms of this structured thought, but I would like to illustrate these details from an OO perspective. The name of this structure is driven by developers, for example, the mouse may have a struct called Mouse_dev, and the keyboard may be made up of a keyboard_dev struct (dev for device, all we do is device driven). And this time we analyze the Linux kernel source of a Usb-skeleton (is the USB-driven skeleton slightly), naturally, he defined the device structure is called Usb-skel:
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 * *
unsigned char * bulk_in_buffer; /* The buffer to receive data * *
size_t Bulk_in_size; /* The size of the receive buffer * *
__u8 bulk_in_endpointaddr; /* The address of the bulk in endpoint * *
__u8 bulk_out_endpointaddr; /* The address of the bulk out endpoint * *
struct Kref kref;
};
Here we need to add some USB protocol specification details. USB can automatically monitor the device and call the appropriate driver processing equipment, so its specification is actually quite complex, fortunately, we do not have to ignore most of the details, because Linux has provided the corresponding solution. As far as I understand it now, the USB drive is divided into two pieces, a USB bus driver, this thing, the Linux kernel has been done, we can ignore, but we must at least understand his function. The image must say, the USB bus drive is equivalent to paving a road, so that all the information can be reached through this USB channel to the place, this part of the work by Usb_core to complete. When the USB device is connected to the USB controller, Usb_core detects some information about the device, such as the manufacturer ID and the product ID, or the class, subclass, and protocol that the device belongs to, to determine which driver should be invoked to handle the device. Inside the complex details we do not have to control, we have to do is another piece of work--usb device driver. In other words, we waited for Usb_core to tell us that we had to work before we could work.
From a developer's perspective, each USB device consists of several configurations (configuration), each configured with multiple interfaces (interface), and multiple settings per interface (not shown in the setting diagram). The interface itself may not have endpoints or multiple endpoints (end points). USB data exchange through the endpoint, the host and each endpoint to establish a one-way pipeline to transmit data. These interfaces can be grouped into four categories:
Controls (Control)
Used to configure a device, get device information, send a command, or obtain a status report on a device
Interrupt (interrupt)
When the USB host requires the device to transmit data, the interrupt endpoint transmits a small amount of data at a fixed rate and is used to send data to the USB device to control the device, which is generally not used to transmit large amounts of data.
Batch (BULK)
For reliable transmission of large amounts of data, if the space on the bus is insufficient to send the entire batch package, it is segmented into multiple packet transmissions.
Waiting Time (isochronous)
The unreliable transmission of large amounts of data does not guarantee the arrival of data, but it guarantees constant data flow and is used for data collection.
Linux uses struct usb_host_endpoint to describe USB endpoints, each usb_host_endpoint contains a struct usb_endpoint_descriptor structure, Contains information about the endpoint and various information about the device customization, including:
Bendpointaddress (b for Byte)
The 8-bit endpoint address, whose address also hides information about the direction of the endpoint (previously said that the endpoint is one-way), can be determined by masking usb_dir_out and usb_dir_in.
Bmattributes
The type of endpoint, combined with Usb_endpoint_xfertype_mask to determine whether the endpoint is Usb_endpoint_xfer_isoc (ET), usb_endpoint_xfer_bulk (batch), or Usb_endpoint_ Xfer_int (interrupted).
Wmaxpacketsize
The maximum number of bytes that the endpoint handles at a time. The bulk packet sent can be larger than this value, but will be split-routed.
Binterval
If the endpoint is an interrupt type, the value is the interval setting for the endpoint, in milliseconds.
Logically, the functional partition of a USB device is accomplished through an interface. For example, a USB speaker may include two interfaces: one for keyboard control and another for audio streaming. In fact, this device requires a different two drivers to operate, a control keyboard, a control of the audio stream. But there are exceptions, such as Bluetooth devices, which require two interfaces, one for ACLs and event transmissions, the other for SCO links, but both through a driver control. On Linux, interfaces are described using struct usb_interface, and the following are the more important fields in the structure:
struct Usb_host_interface *altsetting (note not usb_interface)
In fact, as far as I understand it, he should be setting each interface, though the name is a little strange. The field is an array of settings (an interface can have multiple settings), and each usb_host_interface contains a set of endpoint configurations defined by struct Usb_host_endpoint. However, these configuration orders are variable.
unsigned num_altstting
The number of optional settings, that is, the number of elements in the altsetting index group.
struct Usb_host_interface *cur_altsetting
The current active setting, pointing to one of the altsetting arrays.
int minor
The secondary device number assigned by USB core when the USB driver bundled to the interface uses the USB main device number. Valid only after successful invocation of the Usb_register_dev.
Except that it can be described with struct usb_host_config, I don't know much about configuration until now. The entire USB device can be described with struct usb_device, but basically it will only be used to initialize the interface of the function, and the actual use should be the custom structure we mentioned before.
Well, after you've learned some of the specifications for USB, let's look at the Linux driver framework. In fact, Linux device drivers, especially this hotplug USB device driver, are compiled into modules and then hooked up to the kernel when needed. To write a Linux module is not complicated, take a helloworld as an example:
#include <linux/init.h>
#include <linux/module.h>
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 you how to write a module, Module_license tells the kernel the copyright information of the module, in many cases, with the GPL or BSD, or two, because a private module is generally difficult to get help from the community. The Module_init and module_exit are used to register the module's initialization functions and module launch functions to the kernel. As the program shows, the initialization function is Hello_init, and the Exit function is Hello_exit.
In addition, to compile a module usually requires the use of makefile in the kernel source tree, so the makefile of the module can be written as:
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 that the module hangs with the uninstall, but you have to log in as root to the command line, with the normal user plus su or sudo on the Ubuntu test is not.
The following analysis of Usb-skeleton source code. This sample program can be found under the LINUX-2.6.17/DRIVERS/USB, other versions of the kernel program source may be different, but the difference is not large. We can find the source code to look at first, the overall impression.
As mentioned earlier, the module first registers initialization and destruction functions with the kernel:
static int __init usb_skel_init (void)
{
int result;
/* Register this driver with the USB subsystem * *
result = Usb_register (&skel_driver);
if (result)
Err ("Usb_register failed. Error number%d ", result);
return result;
}
static void __exit usb_skel_exit (void)
{
/* deregister This driver with the USB subsystem * *
Usb_deregister (&skel_driver);
}
Module_init (Usb_skel_init);
Module_exit (Usb_skel_exit);
Module_license ("GPL");
From the code, this init and exit function is used only to register the driver, this description of the driver structure is a system-defined standard structure struct usb_driver, registration and logoff method is simple, usb_register (struct *usb_ Driver), Usb_deregister (struct *usb_driver). So what does this structure need to do? He's going to provide the system with several function portals, with the driver's name:
static struct Usb_driver Skel_driver = {
. Name = "Skeleton",
. Probe = Skel_probe,
. Disconnect = Skel_disconnect,
. id_table = skel_table,
};
From the code's perspective, Usb_driver needs to initialize four things: the name of the module Skeleton,probe function Skel_probe,disconnect function Skel_disconnect, and id_table.
Before we explain the members of Skel_driver, let's look at another structure. The name of this structure is a developer customization that describes all the resources and statuses that the driver has:
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 * *
unsigned char * bulk_in_buffer; /* The buffer to receive data * *
size_t Bulk_in_size; /* The size of the receive buffer * *
__u8 bulk_in_endpointaddr; /* The address of the bulk in endpoint * *
__u8 bulk_out_endpointaddr; /* The address of the bulk out endpoint * *
struct Kref kref;
};
Let's start with a simple analysis of the Usb_skel, which has a structure that describes the USB device Udev, an interface interface, a semaphore (semaphore) Limit_sem for concurrent access control, a buffer for receiving data bulk_in_ The buffer and its dimensions are bulk_in_size, then the batch input output port address bulk_in_endpointaddr, BULK_OUT_ENDPOINTADDR, and finally a reference counter used by the kernel. Their role we will see in the code that follows.
Let's go back and see Skel_driver.
Name is used to tell the kernel module what the name is, this registration has a system to use, and we do not have a relationship.
Id_table is used to tell the kernel which devices the module supports. The USB subsystem identifies the device through a combination of the device's production ID and vendor ID, or the class, subclass, and protocol of the device, and invokes the relevant driver for processing. We can see what this id_table is:
/* Define These values to match your devices * *
#define USB_SKEL_VENDOR_ID 0XFFF0
#define USB_SKEL_PRODUCT_ID 0XFFF0
/* Table of devices that work with this driver * *
static struct usb_device_id skel_table [] = {
{Usb_device (usb_skel_vendor_id, usb_skel_product_id)},
{}/* Terminating entry * *
};
Module_device_table (USB, skel_table);
The first parameter of module_device_table is the type of device, if it is a USB device, it is 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, not to delve into this first. The next parameter is the device table, and the last element of the device table is empty to identify the end. The code defines usb_skel_vendor_id as 0XFFF0,USB_SKEL_PRODUCT_ID is 0xfff0, which means that when a device is connected to a hub, the USB subsystem checks the device's vendor ID and PRODUCT ID. If their value is 0xfff0, then the subsystem will call the skeleton module as the driver of the device.
Probe is a function automatically called by the USB subsystem, when a USB device is connected to a hardware hub, the USB subsystem is based on the combination of the production ID and vendor ID or the class of the device, The combination of subclass and protocol identifies the probe (probe) function that the device calls the corresponding driver, which is skel_probe for skeleton. The system passes to the probe function a usb_interface * with a struct usb_device_id * as an argument. They are the interface description of the USB device (typically the No. 0 interface of the device, the default setting of the interface is set NO. 0) and its Device ID description (including vendor ID, Production ID, etc.). The probe function is relatively long, and we 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 critical function call--interface_to_usbdev. He and UO a usb_interface to get the device description structure of the device on which the interface resides. Originally, to get a usb_device as long as the use of Interface_to_usbdev is enough, but because to increase the reference count on the Usb_device, we should do a usb_get_dev operation to increase the reference count, and in the release of the device with USB _put_dev to reduce the reference count. The explanation here is that the reference value is the count of the Usb_device, not the count of the module, and the count of this module is to be maintained by Kref. As a result, probe initially had initialization kref. In fact, the kref_init operation not only initializes kref, but also sets it to 1. So there is kref_put in the error handling code, which kref the count of 1, and if the kref count is 0, then Kref is released. The second argument to Kref_put is a function pointer to a cleanup function. Note that the pointer cannot be empty, or kfree. The function is invoked when the last reference to Kref is released (if my understanding is inaccurate, please correct me). Here is a section of comments and code in the kernel source:
/**
* Kref_put-decrement refcount for object.
* @kref: Object.
* @release: Pointer to the function which is the object when the
* Last reference to the object is released.
* This pointer is required, and it isn't acceptable to pass Kfree
* in as this function.
*
* Decrement the RefCount, and if 0, call Release ().
* Return 1 if the object is removed, otherwise return 0. Beware, if this
* function returns 0, still can not count on the kref from remaining in
* Memory. Use the ' return ' value if you are want to the kref are now
* Gone, not present.
*/
int kref_put (struct kref *kref, void (*release) (struct kref *kref))
{
warn_on (release = = NULL);
warn_on (release = = (struct Kref *)) kfree);
/*
* If current count is one, we are the "last user" and can release object
* Right now, avoiding a atomic operation on ' RefCount '
*/
if ((Atomic_read (&kref->refcount) = 1) | |
(Atomic_dec_and_test (&kref->refcount)) {
Release (KREF);
return 1;
}
return 0;
}
When we perform the open operation, we want to increase the count of kref, we can use Kref_get to complete. All operations on struct KREF have kernel code to ensure their atomicity.
After we get the Usb_device, we initialize the various states and resources of our custom Usb_skel. The main task of this section is to register the endpoint of the USB device with the Usb_skel. There may be some additional knowledge about usb_interface_descriptor, but because the kernel source has little comment on the structure, it can only be guessed by the individual. In a usb_host_interface structure there is a usb_interface_descriptor called the DESC member, which should be used to describe some of the attributes of the interface, where Bnumendpoints is a 8-bit (b For Byte, which represents the number of endpoints for the interface. Probe then traverses all the endpoints, checks their type and direction, and registers them in the Usb_skel.
/* Set up the endpoint information * *
/* Use only the bulk-in and bulk-out endpoints * *
Iface_desc = interface->cur_altsetting;
for (i = 0; i < iface_desc->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)) {
/* We found a bulk in endpoint * *
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 ("Could 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)) {
/* We found a bulk out endpoint * *
DEV->BULK_OUT_ENDPOINTADDR = endpoint->bendpointaddress;
}
}
if (!) ( DEV->BULK_IN_ENDPOINTADDR && dev->bulk_out_endpointaddr)) {
Err ("Could not find both bulk-in and bulk-out endpoints");
Goto error;
}