Linux usb subsystem (2) _usb-skeleton.c analysis

Keywords: Linux

". / drivers/usb/usb-skeleton.c" is a template program provided by the kernel to USB device driver developers for mass storage of USB devices. The program is not long, but it has strong versatility and is very classic. A thorough understanding of this file can help us better understand the USB subsystem and USB device driver framework, and write a better USB mass storage device driver.

Before matching

Since it is a USB device-driven template, it is necessary to construct a usb_driver object and register it in the kernel.

650 static struct usb_driver skel_driver = {
651         .name =         "skeleton",
652         .probe =        skel_probe,
653         .disconnect =   skel_disconnect,
654         .suspend =      skel_suspend,
655         .resume =       skel_resume,
656         .pre_reset =    skel_pre_reset,
657         .post_reset =   skel_post_reset,
658         .id_table =     skel_table,
659         .supports_autosuspend = 1,
660 };
661 
662 module_usb_driver(skel_driver);

The domain of this object has been explained in the previous article. Here, our main concern is skel_table, which determines which device the driver matches. From the following definition, it can be seen that the driver matches according to device.

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

After matching

Resource class

Next, look at the driver's definition of resource class, which is the link of the whole driver and manages the resources shared by the functions and interfaces of the whole driver. It has to be said that this annotation is really a rare detail in the kernel. skeleton is mainly for mass storage devices, so its resource object encapsulates a lot of buffer information VS interrupt devices as long as one. One urb can solve the problem of data transmission

 49 struct usb_skel {
 50         struct usb_device       *udev;                  /* the usb device for this device */
 51         struct usb_interface    *interface;             /* the interface for this device */
 52         struct semaphore        limit_sem;              /* limiting the number of writes in progress
 53         struct usb_anchor       submitted;              /* in case we need to retract our submission
 54         struct urb              *bulk_in_urb;           /* the urb to read data with */
 55         unsigned char           *bulk_in_buffer;        /* the buffer to receive data */
 56         size_t                  bulk_in_size;           /* the size of the receive buffer */
 57         size_t                  bulk_in_filled;         /* number of bytes in the buffer */
 58         size_t                  bulk_in_copied;         /* already copied to user space */
 59         __u8                    bulk_in_endpointAddr;   /* the address of the bulk in endpoint */
 60         __u8                    bulk_out_endpointAddr;  /* the address of the bulk out endpoint */
 61         int                     errors;                 /* the last request tanked */
 62         bool                    ongoing_read;           /* a read is going on */
 63         spinlock_t              err_lock;               /* lock for errors */
 64         struct kref             kref;
 65         struct mutex            io_mutex;               /* synchronize I/O with disconnect */
 66         wait_queue_head_t       bulk_in_wait;           /* to wait for an ongoing read */
 67 };

struct usb_skel
--50-->Driving operation usb_device object
--51-->Driving operation usb_interface object, Both are device information., VS i2c-s3c2410.c By putting the device information in the ___________ probe Copy it out to save it in the driver resource object, That's the same idea here.. struct usb_interface->dev Domain to usb_skel And other interface functions, Amount to struct device Domain to s3c24xx_i2c And other interface functions, They all flow through various interface functions.
--54-->Used urb object
--55-->Used to receive data. buf Pointer
--56-->Identify the domain of the length of the data to be received
--57-->Domains that identify how much valid data is in the current buffer
--58-->Identify how many data domains the current buffer has been copied to. skeleton It does not empty the buffer, but uses various length representations to determine how much has been occupied. It does not matter whether the parts exceeding the length are cleared or not. The relationship between them is shown below.
--59-->bulk Input Endpoint of Equipment
--60-->bulk Output Point of Equipment
--62-->Device readable marker,0 Represents readable, 1 represents unreadable
--64-->kref For kernel reference counting

usb_skeleton also encapsulates a to_skel_dev with reference to the existing to_platform_device structure in the kernel, which is worth learning from.

 68 #define to_skel_dev(d) container_of(d, struct usb_skel, kref)

probe

After the match is successful, it's time to ask probe to play according to the routine.

490 static int skel_probe(struct usb_interface *interface,
491                       const struct usb_device_id *id)
492 {
493         struct usb_skel *dev;
494         struct usb_host_interface *iface_desc;
495         struct usb_endpoint_descriptor *endpoint;
496         size_t buffer_size;
497         int i;
498         int retval = -ENOMEM;
499 
500         /* allocate memory for our device state and initialize it */
501         dev = kzalloc(sizeof(*dev), GFP_KERNEL);
506         kref_init(&dev->kref);
507         sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
508         mutex_init(&dev->io_mutex);
509         spin_lock_init(&dev->err_lock);
510         init_usb_anchor(&dev->submitted);
511         init_waitqueue_head(&dev->bulk_in_wait);
512 
513         dev->udev = usb_get_dev(interface_to_usbdev(interface));
514         dev->interface = interface;
515 
516         /* set up the endpoint information */
517         /* use only the first bulk-in and bulk-out endpoints */
518         iface_desc = interface->cur_altsetting;
519         for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
520                 endpoint = &iface_desc->endpoint[i].desc;
521 
522                 if (!dev->bulk_in_endpointAddr &&
523                     usb_endpoint_is_bulk_in(endpoint)) {
524                         /* we found a bulk in endpoint */
525                         buffer_size = usb_endpoint_maxp(endpoint);
526                         dev->bulk_in_size = buffer_size;
527                         dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
528                         dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
534                         dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
540                 }
542                 if (!dev->bulk_out_endpointAddr &&
543                     usb_endpoint_is_bulk_out(endpoint)) {
544                         /* we found a bulk out endpoint */
545                         dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
546                 }
547         }
553 
554         /* save our data pointer in this interface device */
555         usb_set_intfdata(interface, dev);
556 
557         /* we can register the device now, as it is ready */
558         retval = usb_register_dev(interface, &skel_class);
566 
567         /* let the user know what node this device is now attached to */
568         dev_info(&interface->dev,
569                  "USB Skeleton device now attached to USBSkel-%d",
570                  interface->minor);
571         return 0;
578 }
579 

skel_probe
--501-->Apply for space for resource objects, Pay attention to the writing here.: dev = kzalloc(sizeof(*dev), GFP_KERNEL);
--506-->Initialization usb_skel->kref
--507-->Initialization usb_skel->limit_sem
--508-->Initialization usb_skel->io_mutex);
--509-->Initialization usb_skel->err_lock);
--510-->Initialization usb_skel->submitted);
--511-->Initialization usb_skel->bulk_in_wait
--513-->Initialization usb_skel->udev,To be matched usb_device Address Storage
--514-519-->Initialization usb_skel Other domains of objects.
--555-->Hide our resource objects interface->dev->p->driver_data in
--558-->Register one usb_device Object to the kernel, apply for a secondary device number, and create device files, ==>intf->usb_dev = device_create(usb_class->class, &intf->dev,MKDEV(USB_MAJOR, minor), class_driver,"%s", temp);

Through the above analysis, we find a difference between skeleton and usbmouse: skeleton constructs usb_class_driver object and registers a USB device with usb_register_dev. As an input subsystem, usbmouse only needs input_register(input_dev) instead of USB device registration. The reason for this difference is that skeleton is for bulk urb device, while USBM is for bulk urb device. Ouse is for interrupt urb devices. For bulk devices, we will read and write devices, not just read and write operations, so in bulk urb device driver to achieve the corresponding set of operation methods and bind to the device file to register with the kernel, this work is done by usb_register_dev. To use this function, we need to construct a usb_class_driver object, the most important of which is skel_fops discussed in this section. This domain is also struct file_operations type, and all implementation of read and write methods are registered in this domain.
Since fops is mentioned, we are mainly concerned with the implementation of three methods: open, read and write. Considering that read and write are similar in operation logic, this article only discusses open and read.

open

 83 static int skel_open(struct inode *inode, struct file *file)
 84 {
 85         struct usb_skel *dev;
 86         struct usb_interface *interface;
 87         int subminor;
 88         int retval = 0;
 89 
 90         subminor = iminor(inode);
 91 
 92         interface = usb_find_interface(&skel_driver, subminor);
100         dev = usb_get_intfdata(interface);
106         retval = usb_autopm_get_interface(interface);
110         /* increment our usage count for the device */
111         kref_get(&dev->kref);
112 
113         /* save our object in the file's private structure */
114         file->private_data = dev;
115 
117         return retval;
118 }

skel_open()
--90-->from inode Get the secondary device number in
--92-->according to skel_driver Object and Subdevice Number Acquisition usb_interface Object, so far the device has been found
--100-->from interface->dev->p->driver_data Gets the address of the resource object in the probe--555--Hidden here
--110-->Reference Count Plus One
--114-->The key is to hide the address of the previously acquired resource object in file->private_data In this way, in all of the cdev Resource objects can be used between interfaces, and resource object addresses can be hidden to interface In order to usb_driver The idea of flow between interface functions is the same.

read

Open the device, then you can read and write. skeleton's key function calls for read operations are as follows. We analyze them in turn according to the call tree.

skel_read()
skel_do_read_io(dev, count)
usb_fill_bulk_urb(...);
usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);

The first is skel_read(), which is a function called back when the application layer reads the device. It attempts to implement the function of copying the appropriate data to the application layer if there is data in the kernel buffer, and calling skel_do_read_io to request data from the device if there is no data in the kernel buffer.

226 static ssize_t skel_read(struct file *file, char *buffer, size_t count,
227                          loff_t *ppos)
228 {
229         struct usb_skel *dev;
230         int rv;
231         bool ongoing_io;
232 
233         dev = file->private_data;
255         if (ongoing_io) {
256                 /* nonblocking IO shall not wait */
257                 if (file->f_flags & O_NONBLOCK) {
258                         rv = -EAGAIN;
259                         goto exit;
260                 }
265                 rv = wait_event_interruptible(dev->bulk_in_wait, (!dev->ongoing_read));
266                 if (rv < 0)
267                         goto exit;
268         }
269 
270         /* errors must be reported */
271         rv = dev->errors;
272         if (rv < 0) {
273                 /* any error is reported once */
274                 dev->errors = 0;
275                 /* to preserve notifications about reset */
276                 rv = (rv == -EPIPE) ? rv : -EIO;
277                 /* report it */
278                 goto exit;
279         }
286         if (dev->bulk_in_filled) {
287                 /* we had read data */
288                 size_t available = dev->bulk_in_filled - dev->bulk_in_copied;
289                 size_t chunk = min(available, count);
290 
291                 if (!available) {
296                         rv = skel_do_read_io(dev, count);
297                         if (rv < 0)
298                                 goto exit;
299                         else
300                                 goto retry;
301                 }
307                 if (copy_to_user(buffer,
308                                  dev->bulk_in_buffer + dev->bulk_in_copied,
309                                  chunk))
310                         rv = -EFAULT;
311                 else
312                         rv = chunk;
313 
314                 dev->bulk_in_copied += chunk;
320                 if (available < count)
321                         skel_do_read_io(dev, count - chunk);
322         } else {
323                 /* no data in the buffer */
324                 rv = skel_do_read_io(dev, count);
325                 if (rv < 0)
326                         goto exit;
327                 else
328                         goto retry;
329         }
330 exit:
331         mutex_unlock(&dev->io_mutex);
332         return rv;
333 }

skel_read()
--233-->They're all routines. First hide them in file_private_data Out of the resource objects
--255-268-->Readable markers in resource objects, when unreadable, judge IO Whether blocking is allowed, if not returned directly,Allow blocking to use the waiting queue header in the resource object to join the waiting queue,Used is interruptible Versions wait,If the process of sleep is interrupted and awakened, then rv==-1,The function returns directly.
--286-->There is only one case of execution to this line: the device is readable! If the buffer is full, execute the first block, otherwise execute the following block
--288-->Buffer full time, Get the size of the copiable data.
--289-->The smaller of the copiable size and the expected copy size. chunk
--291-->Copiable data is 0, and usb_skel->bulk_in_filled Placed in order to enter here, So there's only one case.: The data in the buffer has been copied out
--292-->Now that the data has been copied, call skel_do_read_io Initiate request
--300-->Request data, device feedback, but no data, retry
307-->From the Kernel Buffer usb_skel->bulk_in_buffer + usb_skel->bulk_in_copied start(Is the first address of the remaining unprinted data)Copy chunk byte Data to Application Layer
--314-->To update usb_skel->bulk_in_copied Value
--320-->If the size of the data can be copied<Expected copy size, So obviously just now. chunk=availible, All data has been copied to the application layer, But it can not meet the needs of application layer., call skel_do_read_io To continue requesting data from equipment, Of course, The size of the request is an unsatisfactory part, Namely count-chunk --324-->usb_skel->bulk_in_filled Not positioned, Represents that there is no data in the kernel buffer, call skel_do_read_io Request data, Of course, The size of the request is the total data, Namely count

As I said earlier, if the buffer can not meet the needs of the application layer, the following function will be called to request data from bulk usb device. After obtaining the data, the data will be placed in the buffer and the corresponding flag position will be 1/0.

189 static int skel_do_read_io(struct usb_skel *dev, size_t count)
190 {
191         int rv;
193         /* prepare a read */
194         usb_fill_bulk_urb(dev->bulk_in_urb,dev->udev,usb_rcvbulkpipe(dev->udev,dev->bulk_in_endpointAddr),dev->bulk_in_buffer, min(dev->bulk_in_size, count),skel_read_bulk_callback,dev);
204         dev->ongoing_read = 1;
206 
207         /* submit bulk in urb, which means no data to deliver */
208         dev->bulk_in_filled = 0;
209         dev->bulk_in_copied = 0;
210 
211         /* do it */
212         rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);
223         return rv;
224 }

skel_do_read_io()
--194-->towards usb Core submission urb, Target resources dev Hide in urb->context In the middle urb Parametric incoming callback function, and usb_fill_int_urb difference, usb_fill_bulk_urb When registering, you need to sum the size of the buffer header address and the requested data urb Bind to submit together, Only in this way can we know where to go. bulk Size of data requested by the device, bulk You don't know where to put it until the device has data returned..
--204-->take usb_skel->ongoing_read Set 1, Represents that there is no data to read
--208-->take usb_skel->bulk_in_filled Set 0, Represents that the kernel buffer has no data to read
--209-->take usb_skel->bulk_in_copied Set 0, Represents that no data has been copied
--212-->After getting ready, command usb Core sending urb

When the request is sent, the usb bus will wait for the feedback from the device. When the device has feedback, it will call back the registration function of urb. Let's see what this callback function does.

163 static void skel_read_bulk_callback(struct urb *urb)
164 {
165         struct usb_skel *dev;
166 
167         dev = urb->context;
168 
169         spin_lock(&dev->err_lock);
170         /* sync/async unlink faults aren't errors */
181                 dev->bulk_in_filled = urb->actual_length;
183         dev->ongoing_read = 0;
184         spin_unlock(&dev->err_lock);
185 
186         wake_up_interruptible(&dev->bulk_in_wait);
187 }

skel_read_bulk_callback
--167-->Tricks, Put out the resource objects first
--181-->The length of data that will represent device feedback urb->actual_length Assign to usb_skel->bulk_in_filled, Represents that the buffer has data
--183-->take usb_skel->ongoing_read Set 0, Represents readable!
--186-->The process of waking up and falling asleep because there is no data to read

Analyzing this, the application layer can get data from USB mass storage device by usb_skeleton driver!!!! The idea of writing data is the same, so I won't talk about it here.

The use of locks

In addition to clever buffer management, usb_skeleton.c's use of concurrency control technology is also worth learning. When constructing usb_skel, the driver uses semaphore, spinlock and mutex, three commonly used concurrency control lock mechanisms. Next, we discuss how the kernel bulls use these technologies in different application scenarios.

semaphore

Semaphore is a process unit. Its typical feature is that when a process can't get semaphore, it will fall asleep and give up CPU, so semaphore can't be used in interrupt context. In usb_skeleton.c, semaphore is used in the following scenarios

335 static void skel_write_bulk_callback(struct urb *urb)
336 {
358         up(&dev->limit_sem);                                                                        
359 }

361 static ssize_t skel_write(struct file *file, const char *user_buffer,
362                           size_t count, loff_t *ppos)
363 {
376         /*
377          * limit the number of URBs in flight to stop a user from using up all
378          * RAM
379          */
380         if (!(file->f_flags & O_NONBLOCK)) {
381                 if (down_interruptible(&dev->limit_sem)) {
382                         retval = -ERESTARTSYS;
383                         goto exit;
384                 }
385         } else {
386                 if (down_trylock(&dev->limit_sem)) {
387                         retval = -EAGAIN;
388                         goto exit;
389                 }
390         }
467         return retval;
468 }

spinlock

When critical resources are not available, the process using spinlock will not fall asleep, but be busy, so spinlock can be used in interrupt context, but if resources are not available and CPU is not sold, system resources will be wasted, so the critical area protected by spinlock can not be too long. usb_skeleton uses spinlock mainly in the following scenarios

226 static ssize_t skel_read(struct file *file, char *buffer, size_t count,
227                          loff_t *ppos)
228 {
250 retry:
251         spin_lock_irq(&dev->err_lock);
252         ongoing_io = dev->ongoing_read;
253         spin_unlock_irq(&dev->err_lock);
332         return rv;
333 }

mutex

Mutex is only used to ensure mutual exclusion. When trylock is not used, like semaphore, it falls asleep when lock is not available. usb_skeleton uses mutex in the following scenarios

226 static ssize_t skel_read(struct file *file, char *buffer, size_t count,
227                          loff_t *ppos)
228 {
239         /* no concurrent readers */
240         rv = mutex_lock_interruptible(&dev->io_mutex);
330 exit:
331         mutex_unlock(&dev->io_mutex);
332         return rv;
333 }

Posted by KiwiDave on Wed, 17 Apr 2019 18:27:33 -0700