这里开始分析我们第一节贴的图中的class driver。class driver就是负责实现具体功能的driver,像usb-skeleton和usb鼠标的驱动。我认为class driver的功能应该是两个:一个是具体的操作usb device,实现具体的功能;二是像用户态提供dev的文件操作接口。
我们也用usb-skeleton为例进行分析。在这一层,还把usb device抽象成了一个设备 struct usb_skel dev。
/* Structure to hold all of our device specific stuff */ struct usb_skel { struct usb_device *udev; /* the usb device for this device */ struct usb_interface *interface; /* the interface for this device */ struct semaphore limit_sem; /* limiting the number of writes in progress */ struct usb_anchor submitted; /* in case we need to retract our submissions */ struct urb *bulk_in_urb; /* the urb to read data with */ unsigned char *bulk_in_buffer; /* the buffer to receive data */ size_t bulk_in_size; /* the size of the receive buffer */ size_t bulk_in_filled; /* number of bytes in the buffer */ size_t bulk_in_copied; /* already copied to user space */ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ int errors; /* the last request tanked */ int open_count; /* count the number of openers */ bool ongoing_read; /* a read is going on */ bool processed_urb; /* indicates we haven't processed the urb */ spinlock_t err_lock; /* lock for errors */ struct kref kref; struct mutex io_mutex; /* synchronize I/O with disconnect */ struct completion bulk_in_completion; /* to wait for an ongoing read */ };
我们看到,在class driver这一层,需要用到的gadget的信息就包含在udev、interface、bulk_in_endpointAddr、bulk_out_endpointAddr中,其实主要还是在interface中。
usb-skeleton.c中实现了两个driver。struct usb_driver skel_driver,struct usb_class_driver skel_class。
static struct usb_class_driver skel_class = { .name = "skel%d", .fops = &skel_fops, .minor_base = USB_SKEL_MINOR_BASE, };
从上面可以看到,对于usb_class_driver,其实就是向用户态提供了一套fops,这样用户态就可以通过devfs对device进行操作了。当然,usb鼠标的驱动不完全是这样。针对interface,实现了skel_driver
static struct usb_driver skel_driver = { .name = "skeleton", .probe = skel_probe, .disconnect = skel_disconnect, .suspend = skel_suspend, .resume = skel_resume, .pre_reset = skel_pre_reset, .post_reset = skel_post_reset, .id_table = skel_table, .supports_autosuspend = 1, };
先看skel_probe部分。
static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_skel *dev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; size_t buffer_size; int i; int retval = -ENOMEM; /* allocate memory for our device state and initialize it */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { err("Out of memory"); goto error; } kref_init(&dev->kref); sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); mutex_init(&dev->io_mutex); spin_lock_init(&dev->err_lock); init_usb_anchor(&dev->submitted); init_completion(&dev->bulk_in_completion); dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; /* set up the endpoint information */ /* use only the first bulk-in and bulk-out endpoints */ iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ buffer_size = 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; } dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->bulk_in_urb) { err("Could not allocate bulk_in_urb"); goto error; } } if (!dev->bulk_out_endpointAddr && usb_endpoint_is_bulk_out(endpoint)) { /* we found a bulk out endpoint */ dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; } } if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) { err("Could not find both bulk-in and bulk-out endpoints"); goto error; } /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); /* we can register the device now, as it is ready */ retval = usb_register_dev(interface, &skel_class); if (retval) { /* something prevented us from registering this driver */ err("Not able to get a minor for this device."); usb_set_intfdata(interface, NULL); goto error; } return 0; error: if (dev) /* this frees allocated memory */ kref_put(&dev->kref, skel_delete); return retval; }
上面所进行的工作,主要就是针对usb_skel成员变量的初始化。包括从interface中挑选了两个endpoint,一个作为bulk in endpoint,一个作为bulk out endpoin。两个interface的endpoint分别用作in和out。最后调用usb_reister_dev对设备进行注册。下面我们就可以通过skel_class中的函数对设备进行读写操作了。
static int skel_open(struct inode *inode, struct file *file) { struct usb_skel *dev; struct usb_interface *interface; int subminor; int retval = 0; subminor = iminor(inode); interface = usb_find_interface(&skel_driver, subminor); if (!interface) { err("%s - error, can't find device for minor %d", __func__, subminor); retval = -ENODEV; goto exit; } dev = usb_get_intfdata(interface); if (!dev) { retval = -ENODEV; goto exit; } /* increment our usage count for the device */ kref_get(&dev->kref); /* lock the device to allow correctly handling errors * in resumption */ mutex_lock(&dev->io_mutex); if (!dev->open_count++) { retval = usb_autopm_get_interface(interface); if (retval) { dev->open_count--; mutex_unlock(&dev->io_mutex); kref_put(&dev->kref, skel_delete); goto exit; } } /* else { //uncomment this block if you want exclusive open retval = -EBUSY; dev->open_count--; mutex_unlock(&dev->io_mutex); kref_put(&dev->kref, skel_delete); goto exit; } */ /* prevent the device from being autosuspended */ /* save our object in the file's private structure */ file->private_data = dev; mutex_unlock(&dev->io_mutex); exit: return retval; }
打开设备时,用imonor对设备进行区分。
static ssize_t skel_write(struct file *file, const char __user *user_buffer, size_t count, loff_t *ppos) { struct usb_skel *dev; int retval = 0; struct urb *urb = NULL; char *buf = NULL; dev = (struct usb_skel *)file->private_data; /* verify that we actually have some data to write */ if (count == 0) goto exit; /* create a urb, and a buffer for it, and copy the data to the urb */ urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { retval = -ENOMEM; goto error; } buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma); if (!buf) { retval = -ENOMEM; goto error; } if (copy_from_user(buf, user_buffer, count)) { retval = -EFAULT; goto error; } /* initialize the urb properly */ usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), buf, count, skel_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* send the data out the bulk port */ retval = usb_submit_urb(urb, GFP_KERNEL); if (retval) { err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); goto error; } /* release our reference to this urb, the USB core will eventually free it entirely */ usb_free_urb(urb); exit: return count; error: usb_buffer_free(dev->udev, count, buf, urb->transfer_dma); usb_free_urb(urb); kfree(buf); return retval; }
write的工作就是usb_fill_bulk_urb,然后usb_submit_urb。
static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct usb_skel *dev; int retval = 0; dev = (struct usb_skel *)file->private_data; /* do a blocking bulk read to get data from the device */ retval = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), dev->bulk_in_buffer, min(dev->bulk_in_size, count), &count, HZ*10); /* if the read was successful, copy the data to userspace */ if (!retval) { if (copy_to_user(buffer, dev->bulk_in_buffer, count)) retval = -EFAULT; else retval = count; } return retval; }
read的工作通过usb_bulk_msg来实现,这个函数是一个阻塞函数,不是异步的。读到数据后,copy_to_user。需要记录的是,我在进行实验时开始用的是内核源码自带的usb-skeleton.c,不知为何总是读不到数据。后来改用了ldd中的代码,就可以成功读到数据,所以贴在了上面。
到这里class driver的分析就结束了。有空再分析一个usb 鼠标的驱动。