Linux USB驱动程序(4)----usb-skeleton.c分析

usb_driver里面最重要的3个要素都说完了,接下来就是skel_class了。

第一步,看其结构体定义

/*

 * usb class driver info in order to get a minor number from the usb core,

 * and to have the device registered with the driver core

 */

static struct usb_class_driver skel_class = {

       .name =          "skel%d",

       .fops =           &skel_fops,

       .minor_base = USB_SKEL_MINOR_BASE,

};

Name就是驱动名字了,fops就是这个驱动的文件操作结构体了,minor_base表示次设备的基础设备号,也就是192.比如我使用的时候,产生的文件/dev/skel0 ,主设备号是180,次设备号就是192了。

第二步,就是看skel_fops了,定义如下

static const struct file_operations skel_fops = {

       .owner = THIS_MODULE,

       .read =           skel_read,

       .write =   skel_write,   

       .open =          skel_open,

       .release = skel_release, 

       .flush =   skel_flush,

};

由于只是一个象征性的骨架程序,因此其open函数比较简单,如下

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); // 通过skel_driver及次设备号找到USB接口指针

       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;  // 接口数据保存在file的私有数据里

       mutex_unlock(&dev->io_mutex);

 

exit:

       return retval;

}

对应的release()函数也就是减少在open()中增加的引用计数,如下

static int skel_release(struct inode *inode, struct file *file)

{

       struct usb_skel *dev;

 

       dev = (struct usb_skel *)file->private_data;

       if (dev == NULL)

              return -ENODEV;

 

       /* allow the device to be autosuspended */

       mutex_lock(&dev->io_mutex);

       if (!--dev->open_count && dev->interface)

              usb_autopm_put_interface(dev->interface);

       mutex_unlock(&dev->io_mutex);

 

       /* decrement the count on our device */

       kref_put(&dev->kref, skel_delete);

       return 0;

}

接下来要分析的是读写函数,在访问USB设备时,贯穿于其中的是urb结构体。先看读函数

static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos)

{

       struct usb_skel *dev;

       int retval;

       int bytes_read;

 

       dev = (struct usb_skel *)file->private_data; //得到usb_skel指针

 

       mutex_lock(&dev->io_mutex);

       if (!dev->interface) {            /* disconnect() was called */

              retval = -ENODEV;

              goto exit;

       }

 

       /* 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),              

                           &bytes_read, 10000); 

 

       /* if the read was successful, copy the data to userspace */

       if (!retval) {  // 若读成功,则将数据复制到用户空间

              if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read))

                     retval = -EFAULT;

              else

                     retval = bytes_read;

       }

 

exit:

       mutex_unlock(&dev->io_mutex);

       return retval;

}

在skel_write()函数中进行的关于urb的操作与前面对urb的描述完全对应,即进行了urb的分配、初始化和提交操作。代码如下:

static ssize_t skel_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos)

{

       struct usb_skel *dev;

       int retval = 0;

       struct urb *urb = NULL;

       char *buf = NULL;

       size_t writesize = min(count, (size_t)MAX_TRANSFER);

 

       dev = (struct usb_skel *)file->private_data;

 

       /* verify that we actually have some data to write */

       if (count == 0)  // 验证实际上有数据要写入USB设备

              goto exit;

 

       /* limit the number of URBs in flight to stop a user from using up all RAM */

       if (down_interruptible(&dev->limit_sem)) { // 限制urb的数量,防止一个用户用光所有的RAM

              retval = -ERESTARTSYS;

              goto exit;

       }

 

       spin_lock_irq(&dev->err_lock);

       if ((retval = dev->errors) < 0) {

              /* any error is reported once */

              dev->errors = 0;

              /* to preserve notifications about reset */

              retval = (retval == -EPIPE) ? retval : -EIO;

       }

       spin_unlock_irq(&dev->err_lock);

       if (retval < 0)

              goto error;

 

       /* create a urb, and a buffer for it, and copy the data to the urb */

       urb = usb_alloc_urb(0, GFP_KERNEL);//创建urb的缓存区,将数据复制给urb

       if (!urb) {

              retval = -ENOMEM;

              goto error;

       }

   // 申请DMA内存

       buf = usb_buffer_alloc(dev->udev, writesize, GFP_KERNEL, &urb->transfer_dma);

       if (!buf) {

              retval = -ENOMEM;

              goto error;

       }

 

       if (copy_from_user(buf, user_buffer, writesize)) { // 从用户空间复制数据

              retval = -EFAULT;

              goto error;

       }

 

       /* this lock makes sure we don't submit URBs to gone devices */

       mutex_lock(&dev->io_mutex);

       if (!dev->interface) {            /* disconnect() was called */

              mutex_unlock(&dev->io_mutex);

              retval = -ENODEV;

              goto error;

       }

 

       /* initialize the urb properly */ // 初始化urb

       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;

       usb_anchor_urb(urb, &dev->submitted);//

 

       /* send the data out the bulk port */

       retval = usb_submit_urb(urb, GFP_KERNEL);

       mutex_unlock(&dev->io_mutex);

       if (retval) {

              err("%s - failed submitting write urb, error %d", __func__, retval);

              goto error_unanchor;

       }

 

       /* release our reference to this urb, the USB core will eventually free it entirely */

       usb_free_urb(urb);

 

       return writesize;

 

error_unanchor:

       usb_unanchor_urb(urb);

error:

       if (urb) {

              usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);

              usb_free_urb(urb);

       }

       up(&dev->limit_sem);

 

exit:

       return retval;

}

写函数发起的urb结束后,其完成函数skel_write_bulk_callback()将被调用,它会进行urb->status的判断,如下所示:

static void skel_write_bulk_callback(struct urb *urb)

{

       struct usb_skel *dev;

 

       dev = urb->context;

 

       /* sync/async unlink faults aren't errors */ //去除链路故障

       if (urb->status) {

              if(!(urb->status == -ENOENT ||

                  urb->status == -ECONNRESET ||

                  urb->status == -ESHUTDOWN))

                     err("%s - nonzero write bulk status received: %d",

                         __func__, urb->status);

 

              spin_lock(&dev->err_lock);

              dev->errors = urb->status;

              spin_unlock(&dev->err_lock);

       }

 

       /* free up our allocated buffer */释放被分配的内存

       usb_buffer_free(urb->dev, urb->transfer_buffer_length,

                     urb->transfer_buffer, urb->transfer_dma);

       up(&dev->limit_sem);

}

到这里,整个程序就分析完了。大部分工作内核都帮你做了,USB驱动程序就是这么简单,呵呵。

作者: zhanghonghu84   发布时间: 2010-09-02