linux usb设备驱动一

drivers/usb/中的文件usb-skeleton.c,usb-skeleton是usb驱动的框架,可以通过修改一下部分代码就可一轻松的实现一个简单的设备驱驱动

/*
 * USB Skeleton driver - 2.2
 *
 * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License as
 *  published by the Free Software Foundation, version 2.
 *
 * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c
 * but has been rewritten to be easier to read and use.
 *
 */

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kref.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/mutex.h>

/*内核所用到的头文件都保存在include/目录下。为了方便使用和兼容性,Linus在编制内核程序头文件时所使用的命名方式与标准C库头文件的命名方式相似,在一个Linux系统中,它们与标准库的头文件并存。通常的做法是将这些头文件放置在标准库头文件目录中的子目录下,以让需要用到内核数据结构或常数的程序使用。在Linux系统中,asm/、linux/和sys/三个子目录下的内核头文件通常需要复制到标准C库头文件所在的目录(/usr/include)中,而其他一些文件若与标准库的头文件没有冲突则可以直接放到标准库头文件目录下,或者改放到这里的三个子目录中*/

/* Define these values to match your devices */
//设备的厂商ID
#define USB_SKEL_VENDOR_ID  0xfff0
#define USB_SKEL_PRODUCT_ID 0xfff0

/* table of devices that work with this driver */
//使用本驱动的设备列表
static const struct usb_device_id skel_table[] = {
    { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
    /* USB_DEVICE 宏利用厂商ID 和设备ID 提供了设备的唯一标识。当系统插入一个与ID匹配的USB设备到USB总线时,驱动会在USB core 中注册。, */
    { }                 /* Terminating entry */
};
//描述特定驱动支持的设备
MODULE_DEVICE_TABLE(usb, skel_table); 

/*  MODULE_DEVICE_TABLE的第一个参数是设备的类型,如果是USB设备,那自然是usb(如果是PCI设备,那将是pci,这两个子系统用同一个宏来注册所支持的设备。这涉及PCI设备的驱动了,在此先不深究)。后面一个参数是设备表,这个设备表的最后一个元素是空的,用于标识结束。代码定义了USB_SKEL_VENDOR_ID是0xfff0,USB_SKEL_PRODUCT_ID是0xfff0,也就是说,当有一个设备接到集线器时,usb子系统就会检查这个设备的vendor ID和product ID,如果它们的值是0xfff0时,那么子系统就会调用这个skeleton模块作为设备的驱动。*/


/* Get a minor range for your devices from the usb maintainer */
#define USB_SKEL_MINOR_BASE 192

/* our private defines. if this grows any larger, use your own .h file */
#define MAX_TRANSFER        (PAGE_SIZE - 512)
/* MAX_TRANSFER is chosen so that the VM is not stressed by
   allocations > PAGE_SIZE and the number of packets in a page
   is an integer 512 is the largest possible packet on EHCI */
#define WRITES_IN_FLIGHT    8
/* arbitrarily chosen */

/* Structure to hold all of our device specific stuff */
//定义了设备的数据结构,后面会对该数据结构进行初始化以及赋值调用。
//sub_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 */
    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 */
    bool            ongoing_read;       /* a read is going on */
    spinlock_t      err_lock;       /* lock for errors */
    struct kref     kref;
    struct mutex        io_mutex;       /* synchronize I/O with disconnect */
    wait_queue_head_t   bulk_in_wait;       /* to wait for an ongoing read */
};
#define to_skel_dev(d) container_of(d, struct usb_skel, kref)

static struct usb_driver skel_driver;
static void skel_draw_down(struct usb_skel *dev);

static void skel_delete(struct kref *kref)
{
    struct usb_skel *dev = to_skel_dev(kref);

    usb_free_urb(dev->bulk_in_urb);
    usb_put_dev(dev->udev);
    kfree(dev->bulk_in_buffer);
    kfree(dev);
}

/*由于只是一个象征性的骨架程序,open()成员函数的实现非常简单,它根据usb_driver和次设备号通过 usb_find_interface()获得USB接口,之后通过usb_get_intfdata()获得接口的私有数据并赋予file-> private_data*/
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) {
        pr_err("%s - error, can't find device for minor %d\n",
            __func__, subminor);
        retval = -ENODEV;
        goto exit;
    }
    //得到,则用usb_get_intfdata保存的设备
    dev = usb_get_intfdata(interface);
    if (!dev) {
        retval = -ENODEV;
        goto exit;
    }

    retval = usb_autopm_get_interface(interface);
    if (retval)
        goto exit;

    /* increment our usage count for the device */
    //增加引用计数
    kref_get(&dev->kref);

    /* save our object in the file's private structure */
    //保存设备到文件私有数据
    file->private_data = dev;

exit:
    return retval;
}

/*由于在open()函数中并没有申请任何软件和硬件资源,因此与open()函数对应的release()函数不用进行资源的释放,它会减少在open()中增加的引用计数*/
static int skel_release(struct inode *inode, struct file *file)
{
    struct usb_skel *dev;

    dev = file->private_data;
    if (dev == NULL)
        return -ENODEV;

    /* allow the device to be autosuspended */
    //允许设备自动挂起
    mutex_lock(&dev->io_mutex);
    if (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;
}

static int skel_flush(struct file *file, fl_owner_t id)
{
    struct usb_skel *dev;
    int res;

     //从私有数据中获得设备信息
    dev = file->private_data;
    if (dev == NULL)
        return -ENODEV;

    /* wait for io to stop */
    mutex_lock(&dev->io_mutex);
    skel_draw_down(dev);

    /* read out errors, leave subsequent opens a clean slate */
    spin_lock_irq(&dev->err_lock);
    res = dev->errors ? (dev->errors == -EPIPE ? -EPIPE : -EIO) : 0;
    dev->errors = 0;
    spin_unlock_irq(&dev->err_lock);

    mutex_unlock(&dev->io_mutex);

    return res;
}

static void skel_read_bulk_callback(struct urb *urb)
{
    struct usb_skel *dev;

    dev = urb->context;

    spin_lock(&dev->err_lock);
    /* sync/async unlink faults aren't errors */
    if (urb->status) {
        if (!(urb->status == -ENOENT ||
            urb->status == -ECONNRESET ||
            urb->status == -ESHUTDOWN))
            dev_err(&dev->interface->dev,
                "%s - nonzero write bulk status received: %d\n",
                __func__, urb->status);

        dev->errors = urb->status;
    } else {
        dev->bulk_in_filled = urb->actual_length;
    }
    dev->ongoing_read = 0;
    spin_unlock(&dev->err_lock);

    wake_up_interruptible(&dev->bulk_in_wait);
}

static int skel_do_read_io(struct usb_skel *dev, size_t count)
{
    int rv;

    /* prepare a read */
    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);
    /* tell everybody to leave the URB alone */
    spin_lock_irq(&dev->err_lock);
    dev->ongoing_read = 1;
    spin_unlock_irq(&dev->err_lock);

    /* submit bulk in urb, which means no data to deliver */
    dev->bulk_in_filled = 0;
    dev->bulk_in_copied = 0;

    /* do it */
    rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);
    if (rv < 0) {
        dev_err(&dev->interface->dev,
            "%s - failed submitting read urb, error %d\n",
            __func__, rv);
        rv = (rv == -ENOMEM) ? rv : -EIO;
        spin_lock_irq(&dev->err_lock);
        dev->ongoing_read = 0;
        spin_unlock_irq(&dev->err_lock);
    }

    return rv;
}

static ssize_t skel_read(struct file *file, char *buffer, size_t count,
             loff_t *ppos)
{
    struct usb_skel *dev;
    int rv;
    bool ongoing_io;

    dev = file->private_data;

    /* if we cannot read at all, return EOF */
    if (!dev->bulk_in_urb || !count)
        return 0;

    /* no concurrent readers */
    rv = mutex_lock_interruptible(&dev->io_mutex);
    if (rv < 0)
        return rv;

    if (!dev->interface) {      /* disconnect() was called */
        rv = -ENODEV;
        goto exit;
    }

    /* if IO is under way, we must not touch things */
retry:
    spin_lock_irq(&dev->err_lock);
    ongoing_io = dev->ongoing_read;
    spin_unlock_irq(&dev->err_lock);

    if (ongoing_io) {
        /* nonblocking IO shall not wait */
        if (file->f_flags & O_NONBLOCK) {
            rv = -EAGAIN;
            goto exit;
        }
        /*
         * IO may take forever
         * hence wait in an interruptible state
         */
        rv = wait_event_interruptible(dev->bulk_in_wait, (!dev->ongoing_read));
        if (rv < 0)
            goto exit;
    }

    /* errors must be reported */
    rv = dev->errors;
    if (rv < 0) {
        /* any error is reported once */
        dev->errors = 0;
        /* to preserve notifications about reset */
        rv = (rv == -EPIPE) ? rv : -EIO;
        /* report it */
        goto exit;
    }

    /*
     * if the buffer is filled we may satisfy the read
     * else we need to start IO
     */

    if (dev->bulk_in_filled) {
        /* we had read data */
        size_t available = dev->bulk_in_filled - dev->bulk_in_copied;
        size_t chunk = min(available, count);

        if (!available) {
            /*
             * all data has been used
             * actual IO needs to be done
             */
            rv = skel_do_read_io(dev, count);
            if (rv < 0)
                goto exit;
            else
                goto retry;
        }
        /*
         * data is available
         * chunk tells us how much shall be copied
         */

        if (copy_to_user(buffer,
                 dev->bulk_in_buffer + dev->bulk_in_copied,
                 chunk))
            rv = -EFAULT;
        else
            rv = chunk;

        dev->bulk_in_copied += chunk;

        /*
         * if we are asked for more than we have,
         * we start IO but don't wait
         */
        if (available < count)
            skel_do_read_io(dev, count - chunk);
    } else {
        /* no data in the buffer */
        rv = skel_do_read_io(dev, count);
        if (rv < 0)
            goto exit;
        else
            goto retry;
    }
exit:
    mutex_unlock(&dev->io_mutex);
    return rv;
}

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))
            dev_err(&dev->interface->dev,
                "%s - nonzero write bulk status received: %d\n",
                __func__, urb->status);

        spin_lock(&dev->err_lock);
        dev->errors = urb->status;
        spin_unlock(&dev->err_lock);
    }

    /* free up our allocated buffer */
    usb_free_coherent(urb->dev, urb->transfer_buffer_length,
              urb->transfer_buffer, urb->transfer_dma);
    up(&dev->limit_sem);
}

/***write函数现分配一个urb,并把用户空间的数据复制到驱动中,然后填充刚刚分配的批量urb结构,最后向usb子系统提交该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 = file->private_data;

    /* verify that we actually have some data to write */
    if (count == 0)
        goto exit;

    /*
     * limit the number of URBs in flight to stop a user from using up all
     * RAM
     */
    if (!(file->f_flags & O_NONBLOCK)) {
        if (down_interruptible(&dev->limit_sem)) {
            retval = -ERESTARTSYS;
            goto exit;
        }
    } else {
        if (down_trylock(&dev->limit_sem)) {
            retval = -EAGAIN;
            goto exit;
        }
    }

    spin_lock_irq(&dev->err_lock);
    retval = dev->errors;
    if (retval < 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);
    if (!urb) {
        retval = -ENOMEM;
        goto error;
    }

    buf = usb_alloc_coherent(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 */
    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) {
        dev_err(&dev->interface->dev,
            "%s - failed submitting write urb, error %d\n",
            __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_free_coherent(dev->udev, writesize, buf, urb->transfer_dma);
        usb_free_urb(urb);
    }
    up(&dev->limit_sem);

exit:
    return retval;
}


/***大多数usb驱动都会涉及另外一个驱动系统,如scsi,网络或者tty子系统,这些驱动程序在其他驱动系统中注册,同时任何用户空间的交互操作都有那些接口提供。如把usb的驱动同时向csi注册,那么usb的read,write函数的操作就会调用scsi的read,write函数进行操作,但是对于扫描仪等驱动程序来说,并没有一个合适的驱动系统可以使用,那么就要自己处理于用户空间交互的read,write函数。usb子系统提供一种方法去注册一个次设备号和一个file_operation函数指针,这样就可以与用户空间实现交互的访问。这个方法就是usb_class_driver,他提供了次设备号,文件接口,devfs接口等。而usb_class_driver中的file_operations中就存放着内核与用户空间进行交互的函数指针。*/
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,
    .llseek =   noop_llseek,
};

/*
 * 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,
};


/***skel_probe 这是非常重要的一个函数。这个函数实现了设备于驱动的绑定。USB_DEVICE 宏利用厂商ID 和设备ID 提供了设备的唯一标识。当系统插入一个与ID匹配的USB设备到USB总线时,驱动会在USB core中注册。驱动程序的probe函数也就会被调用。usb_device 结构指针,接口号和接口ID都会被传递到函数中。probe是usb子系统自动调用的一个函数,有USB设备接到硬件集线器时,usb子系统会根据production ID和vendor ID的组合或者设备的class、subclass跟protocol的组合来识别设备调用相应驱动程序的probe(探测)函数,对于skeleton来说,就是skel_probe。系统会传递给探测函数一个usb_interface *跟一个struct usb_device_id *作为参数。他们分别是该USB设备的接口描述(一般会是该设备的第0号接口,该接口的默认设置也是第0号设置)跟它的设备ID描述(包括Vendor ID、Production ID等。probe()会根据usb_interface的成员寻找第一个批量输入和输出端点,将端点地址、缓冲区等信息存入 为USB骨架程序定义的usb_skel结构体,并将usb_skel实例的指针传入usb_set_intfdata()作为USB接口的私有数据,最后,它会注册USB设备***/

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 */
    /*为设备驱动分配内存并初始化,请回头看一看struct usb_skel,其实就是为该设备驱动分配内存并进行//初始化。*/
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev) {
        dev_err(&interface->dev, "Out of memory\n");
        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_waitqueue_head(&dev->bulk_in_wait);


/***根据接口获取设备在初始化了一些资源之后,可以看到第一个关键的函数调用——interface_to_usbdev。他同uo一个usb_interface来得到该接口所在设备的设备描述结构。本来,要得到一个usb_device只要用interface_to_usbdev就够了,但因为要增加对该usb_device的引用计数,我们应该在做一个usb_get_dev的操作,来增加引用计数,并在释放设备时用usb_put_dev来减少引用计数。这里要解释的是,该引用计数值是对该usb_device的计数,并不是对本模块的计数,本模块的计数要由kref来维护。所以,probe一开始就有初始化kref。事实上,kref_init操作不单只初始化kref,还将其置设成1。所以在出错处理代码中有kref_put,它把kref的计数减1,如果kref计数已经为0,那么kref会被释放。kref_put的第二个参数是一个函数指针,指向一个清理函数。注意,该指针不能为空,或者kfree。该(skel_delete)函数会在最后一个对kref的引用释放时被调用***/
    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 = usb_endpoint_maxp(endpoint);
            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) {
                dev_err(&interface->dev,
                    "Could not allocate bulk_in_buffer\n");
                goto error;
            }
            dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
            if (!dev->bulk_in_urb) {
                dev_err(&interface->dev,
                    "Could not allocate bulk_in_urb\n");
                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)) {
        dev_err(&interface->dev,
            "Could not find both bulk-in and bulk-out endpoints\n");
        goto error;
    }

    /* save our data pointer in this interface device */
    /***将设备信息报存在接口结构中,(就是向系统注册一些以后会用的的信息。首先我们来说明一下usb_set_intfdata(),他向内核注册一个data,这个data的结构可以是任意的,这段程序向内核注册了一个usb_skel结构,就是我们刚刚看到的被初始化的那个,这个data可以在以后用usb_get_intfdata来得到,)***/
    usb_set_intfdata(interface, dev);

    /* we can register the device now, as it is ready */
    /**一切就绪,注册次设备即向这个interface注册一个skel_class结构此处是重点**/
    retval = usb_register_dev(interface, &skel_class);
    if (retval) {
        /* something prevented us from registering this driver */
        dev_err(&interface->dev,
            "Not able to get a minor for this device.\n");
        usb_set_intfdata(interface, NULL);
        goto error;
    }

    /* let the user know what node this device is now attached to */
    dev_info(&interface->dev,
         "USB Skeleton device now attached to USBSkel-%d",
         interface->minor);
    return 0;

error:
    if (dev)
        /* this frees allocated memory */
        kref_put(&dev->kref, skel_delete);
    return retval;
}

/**当设备被拔出集线器时,usb子系统会自动地调用disconnect,他做的事情不多,最重要的是注销class_driver(交还次设备号)和interface的data,然后他会用kref_put(&dev->kref, skel_delete)进行清理**/
static void skel_disconnect(struct usb_interface *interface)
{
    struct usb_skel *dev;
    int minor = interface->minor;

    dev = usb_get_intfdata(interface);
    usb_set_intfdata(interface, NULL);

    /* give back our minor */
    usb_deregister_dev(interface, &skel_class);

    /* prevent more I/O from starting */
    mutex_lock(&dev->io_mutex);
    dev->interface = NULL;
    mutex_unlock(&dev->io_mutex);

    usb_kill_anchored_urbs(&dev->submitted);

    /* decrement our usage count */
    kref_put(&dev->kref, skel_delete);

    dev_info(&interface->dev, "USB Skeleton #%d now disconnected", minor);
}

static void skel_draw_down(struct usb_skel *dev)
{
    int time;

    time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
    if (!time)
        usb_kill_anchored_urbs(&dev->submitted);
    usb_kill_urb(dev->bulk_in_urb);
}

static int skel_suspend(struct usb_interface *intf, pm_message_t message)
{
    struct usb_skel *dev = usb_get_intfdata(intf);

    if (!dev)
        return 0;
    skel_draw_down(dev);
    return 0;
}

static int skel_resume(struct usb_interface *intf)
{
    return 0;
}

static int skel_pre_reset(struct usb_interface *intf)
{
    struct usb_skel *dev = usb_get_intfdata(intf);

    mutex_lock(&dev->io_mutex);
    skel_draw_down(dev);

    return 0;
}

static int skel_post_reset(struct usb_interface *intf)
{
    struct usb_skel *dev = usb_get_intfdata(intf);

    /* we are sure no URBs are active - no locking needed */
    dev->errors = -EPIPE;
    mutex_unlock(&dev->io_mutex);

    return 0;
}

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,
};

module_usb_driver(skel_driver);
MODULE_LICENSE("GPL");

usb相关结构说明

1.设备描述符

struct usb_device_descriptor {  
 __u8  bLength;              --描述符长度  
 __u8  bDescriptorType;      --描述符类型:设备描述符0x01  
 __le16 bcdUSB;              --usb规范版本号  
 __u8  bDeviceClass;         --类代码  
 __u8  bDeviceSubClass;      --子类代码  
 __u8  bDeviceProtocol;      --协议代码  
 __u8  bMaxPacketSize0;      --端点0支持最大数  
 __le16 idVendor;            --供应商ID  
 __le16 idProduct;           --产品ID  
 __le16 bcdDevice;           --设备版本号  
 __u8  iManufacturer;        --供应商字符串描述符的索引值  
 __u8  iProduct;             --产品字符串描述符的索引值  
 __u8  iSerialNumber;        --设备序列号  
 __u8  bNumConfigurations;   --所支持的配置数  
} __attribute__ ((packed));   --结构体字符类型对齐

2.配置描述符

struct usb_config_descriptor {  
 __u8  bLength;              --描述符长度  
 __u8  bDescriptorType;      --描述符类型  
 __le16 wTotalLength;        --配置信息的总长度  
 __u8  bNumInterfaces;       --所支持的接口数  
 __u8  bConfigurationValue;  --配置值  
 __u8  iConfiguration;       --字符串描述符的索引值  
 __u8  bmAttributes;         --配置特征  
 __u8  bMaxPower;            --所需最大的总线电流  
} __attribute__ ((packed));  

3.接口描述符

struct usb_interface_descriptor {  
 __u8  bLength;  
 __u8  bDescriptorType;  
 __u8  bInterfaceNumber;     --接口编号  
 __u8  bAlternateSetting;    --备用接口标号  
 __u8  bNumEndpoints;        --接口数目  
 __u8  bInterfaceClass;      --接口类型  
 __u8  bInterfaceSubClass;   --接口子类型  
 __u8  bInterfaceProtocol;   --接口所用协议  
 __u8  iInterface;           --接口索引字符串数值  
} __attribute__ ((packed));  

4.端点描述符

struct usb_endpoint_descriptor {  
 __u8  bLength;  
 __u8  bDescriptorType;  
 __u8  bEndpointAddress;      --端点号包括传输方向  
 __u8  bmAttributes;          --端点属性  
 __le16 wMaxPacketSize;       --最大数据包长度  
 __u8  bInterval;             --访问间隔  
 __u8  bRefresh;                
 __u8  bSynchAddress;  
} __attribute__ ((packed));  

usb总线驱动中对于设备和设备驱动的匹配函数,其实就是上述1和3的匹配过程

USB设备其实很复杂,但是Linux内核提供了一个称为USB core的子系统来处理了大部分的复杂工作,所以这里所描述的是驱动程序和USB core之间的接口。

在USB设备组织结构中,从上到下分为设备(device)、配置(config)、接口(interface)和端点(endpoint)四个层次。对于这四个层次的简单描述如下:
设备通常具有一个或多个的配置
配置经常具有一个或多个的接口
接口通常具有一个或多个的设置
接口没有或具有一个以上的端点

设备

很明显,地代表了一个插入的USB设备,在内核使用数据结构 struct usb_device来描述整个USB设备。(include/linux/usb.h)

 struct usb_device {
   int devnum; //设备号,是在USB总线的地址
   char devpath [16]; //用于消息的设备ID字符串
   enum usb_device_state state; //设备状态:已配置、未连接等等
   enum usb_device_speed speed; //设备速度:高速、全速、低速或错误
  
   struct usb_tt *tt; //处理传输者信息;用于低速、全速设备和高速HUB
   int ttport; //位于tt HUB的设备口
  
   unsigned int toggle[2]; //每个端点的占一位,表明端点的方向([0] = IN, [1] = OUT)  
   struct usb_device *parent; //上一级HUB指针
   struct usb_bus *bus; //总线指针
   struct usb_host_endpoint ep0; //端点0数据

   struct device dev; //一般的设备接口数据结构
 
   struct usb_device_descriptor descriptor; //USB设备描述符
   struct usb_host_config *config; //设备的所有配置

   struct usb_host_config *actconfig; //被激活的设备配置
   struct usb_host_endpoint *ep_in[16]; //输入端点数组
   struct usb_host_endpoint *ep_out[16]; //输出端点数组
  
   char **rawdescriptors; //每个配置的raw描述符
  
   unsigned short bus_mA; //可使用的总线电流


   u8 portnum;//父端口号
   u8 level; //USB HUB的层数
  
   unsigned can_submit:1; //URB可被提交标志
   unsigned discon_suspended:1; //暂停时断开标志
   unsigned persist_enabled:1; //USB_PERSIST使能标志
   unsigned have_langid:1; //string_langid存在标志
   unsigned authorized:1; 
   unsigned authenticated:1;
   unsigned wusb:1; //无线USB标志

   int string_langid; //字符串语言ID

  
   /* static strings from the device */ //设备的静态字符串
   char *product; //产品名
   char *manufacturer; //厂商名
   char *serial; //产品串号
  
   struct list_head filelist; //此设备打开的usbfs文件

  #ifdef CONFIG_USB_DEVICE_CLASS
   struct device *usb_classdev; //用户空间访问的为usbfs设备创建的USB类设备
  #endif
  #ifdef CONFIG_USB_DEVICEFS
   struct dentry *usbfs_dentry; //设备的usbfs入口
  #endif
  
   int maxchild; //(若为HUB)接口数
   struct usb_device *children[USB_MAXCHILDREN];//连接在这个HUB上的子设备
   int pm_usage_cnt; //自动挂起的使用计数
   u32 quirks; 
   atomic_t urbnum; //这个设备所提交的URB计数
  
   unsigned long active_duration; //激活后使用计时

  #ifdef CONFIG_PM //电源管理相关
   struct delayed_work autosuspend; //自动挂起的延时
   struct work_struct autoresume; //(中断的)自动唤醒需求
   struct mutex pm_mutex; //PM的互斥锁 

  
   unsigned long last_busy; //最后使用的时间
   int autosuspend_delay; 
   unsigned long connect_time; //第一次连接的时间
  
   unsigned auto_pm:1; //自动挂起/唤醒
   unsigned do_remote_wakeup:1; //远程唤醒
   unsigned reset_resume:1; //使用复位替代唤醒
   unsigned autosuspend_disabled:1; //挂起关闭
   unsigned autoresume_disabled:1; //唤醒关闭
   unsigned skip_sys_resume:1; //跳过下个系统唤醒
  #endif
   struct wusb_dev *wusb_dev; //(如果为无线USB)连接到WUSB特定的数据结构

  };

配置

一个USB设备可以有多个配置,并可在它们之间转换以改变设备的状态。比如一个设备可以通过下载固件(firmware)的方式改变设备的使用状态,那么USB设备就要切换配置,来完成这个工作。一个时刻只能有一个配置可以被激活。Linux使用结构 struct usb_host_config 来描述USB配置。我们编写的USB设备驱动通常不需要读写这些结构的任何值。可在内核源码的文件include/linux/usb.h中找到对它们的描述。


struct usb_host_config {
    struct usb_config_descriptor desc; //配置描述符

    char *string; /* 配置的字符串指针(如果存在) */
    struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS]; //配置的接口联合描述符链表
    struct usb_interface *interface[USB_MAXINTERFACES]; //接口描述符链表
    struct usb_interface_cache *intf_cache[USB_MAXINTERFACES]; 
    unsigned char *extra; /* 额外的描述符 */
    int extralen;
};

接口

USB端点被绑为接口,USB接口只处理一种USB逻辑连接。一个USB接口代表一个基本功能,每个USB驱动控制一个接口。所以一个物理上的硬件设备可能需要一个以上的驱动程序。这可以windows系统中看出,有时插入一个USB设备后,系统会识别出多个设备,并安装相应多个的驱动。
USB 接口可以有其他的设置,它是对接口参数的不同选择. 接口的初始化的状态是第一个设置,编号为0。 其他的设置可以以不同方式控制独立的端点。

USB接口在内核中使用struct usb_interface来描述。USB 核心将其传递给USB驱动,并由USB驱动负责后续的控制。

struct usb_interface {
    struct usb_host_interface *altsetting; /* 包含所有可用于该接口的可选设置的接口结构数组。每个 struct usb_host_interface 包含一套端点配置(即struct usb_host_endpoint结构所定义的端点配置。这些接口结构没有特别的顺序。*/

    struct usb_host_interface *cur_altsetting; /* 指向altsetting内部的指针,表示当前激活的接口配置*/
    unsigned num_altsetting; /* 可选设置的数量*/

    /* If there is an interface association descriptor then it will list the associated interfaces */
    struct usb_interface_assoc_descriptor *intf_assoc;

    int minor; /* 如果绑定到这个接口的 USB 驱动使用 USB 主设备号, 这个变量包含由 USB 核心分配给接口的次设备号. 这只在一个成功的调用 usb_register_dev后才有效。*/
    /*以下的数据在我们写的驱动中基本不用考虑,系统会自动设置*/
    enum usb_interface_condition condition; /* state of binding */
    unsigned is_active:1; /* the interface is not suspended */
    unsigned sysfs_files_created:1; /* the sysfs attributes exist */
    unsigned ep_devs_created:1; /* endpoint "devices" exist */
    unsigned unregistering:1; /* unregistration is in progress */
    unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
    unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */
    unsigned needs_binding:1; /* needs delayed unbind/rebind */
    unsigned reset_running:1;

    struct device dev; /* 接口特定的设备信息 */
    struct device *usb_dev;
    int pm_usage_cnt; /* usage counter for autosuspend */
    struct work_struct reset_ws; /* for resets in atomic context */
};

struct usb_host_interface {
    struct usb_interface_descriptor desc; //接口描述符

    struct usb_host_endpoint *endpoint; /* 这个接口的所有端点结构体的联合数组*/
    char *string; /* 接口描述字符串 */
    unsigned char *extra; /* 额外的描述符 */
    int extralen;
};

端点

USB 通讯的最基本形式是通过端点来实现。一个USB端点只能向一个方向传输数据(从主机到设备(称为输出端点)或者从设备到主机(称为输入端点))。端点可被看作一个单向的管道。

一个 USB 端点有 4 种不同类型, 分别具有不同的数据传送方式:

控制CONTROL
控制端点被用来控制对 USB 设备的不同部分访问. 通常用作配置设备、获取设备信息、发送命令到设备或获取设备状态报告。这些端点通常较小。每个 USB 设备都有一个控制端点称为”端点 0”, 被 USB 核心用来在插入时配置设备。USB协议保证总有足够的带宽留给控制端点传送数据到设备.

中断INTERRUPT
每当 USB 主机向设备请求数据时,中断端点以固定的速率传送小量的数据。此为USB 键盘和鼠标的主要的数据传送方法。它还用以传送数据到 USB 设备来控制设备。通常不用来传送大量数据。USB协议保证总有足够的带宽留给中断端点传送数据到设备.

批量BULK
批量端点用以传送大量数据。这些端点常比中断端点大得多. 它们普遍用于不能有任何数据丢失的数据。USB 协议不保证传输在特定时间范围内完成。如果总线上没有足够的空间来发送整个BULK包,它被分为多个包进行传输。这些端点普遍用于打印机、USB Mass Storage和USB网络设备上。

等时ISOCHRONOUS
等时端点也批量传送大量数据, 但是这个数据不被保证能送达。这些端点用在可以处理数据丢失的设备中,并且更多依赖于保持持续的数据流。如音频和视频设备等等。

控制和批量端点用于异步数据传送,而中断和同步端点是周期性的。这意味着这些端点被设置来在固定的时间连续传送数据,USB 核心为它们保留了相应的带宽。

端点在内核中使用结构struct usb_host_endpoint 来描述,它所包含的真实端点信息在另一个结构中:struct usb_endpoint_descriptor(端点描述符,包含所有的USB特定数据)。

struct usb_host_endpoint {
    struct usb_endpoint_descriptor desc; //端点描述符
    struct list_head urb_list; //此端点的URB对列,由USB核心维护
    void *hcpriv;
    struct ep_device *ep_dev; /* For sysfs info */
    unsigned char *extra; /* Extra descriptors */
    int extralen;
    int enabled;
};

/*-------------------------------------------------------------------------*/

/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {
    __u8 bLength;
    __u8 bDescriptorType;

    __u8 bEndpointAddress; /*这个特定端点的 USB 地址,这个8位数据包含端点的方向,结合位掩码 USB_DIR_OUT 和 USB_DIR_IN 使用, 确定这个端点的数据方向。*/

    __u8 bmAttributes; //这是端点的类型,位掩码如下

    __le16 wMaxPacketSize; /*端点可以一次处理的最大字节数。驱动可以发送比这个值大的数据量到端点, 但是当真正传送到设备时,数据会被分为 wMaxPakcetSize 大小的块。对于高速设备, 通过使用高位部分几个额外位,可用来支持端点的高带宽模式。*/
    __u8 bInterval; //如果端点是中断类型,该值是端点的间隔设置,即端点的中断请求间的间隔时间,以毫秒为单位

    /* NOTE: these two are _only_ in audio endpoints. */
    /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
    __u8 bRefresh;
    __u8 bSynchAddress;
} __attribute__ ((packed));

#define USB_DT_ENDPOINT_SIZE 7
#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */

/*
 * Endpoints
 */
#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress 端点的 USB 地址掩码 */
#define USB_ENDPOINT_DIR_MASK 0x80 /* in bEndpointAddress 数据方向掩码 */

#define USB_DIR_OUT 0 /* to device */
#define USB_DIR_IN 0x80 /* to host */

#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* bmAttributes 的位掩码*/
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
#define USB_ENDPOINT_XFER_BULK 2
#define USB_ENDPOINT_XFER_INT 3
#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80
/*-------------------------------------------------------------------------*/
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页