第16章 USB主机、设备与Gadget驱动之USB设备驱动(二)

3.简单的批量与控制URB

    有时USB设备驱动程序只是从USB设备上接收(读取)向USB设备发送(写入)简单的数据,没有必要将URB(USB请求块)创建、初始化、提交、完成处理的整个流程走一遍,可以使用两个更简单的函数,如下所示。

(1)usb_bulk_msg()

该函数创建一个USB批量URB并将它发送到特定设备,这个函数是同步的,它一直等待URB完成后才返回。其函数原型:

linux/usb.h

int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length,

        int timeout);

usb_dev:批量消息要发送的USB设备的指针

pipe:批量消息要发送到的USB设备的端点

data:指向要发送或接收的数据缓冲区的指针

len:data参数所指向的缓冲区的长度

actual_length:用于返回实际发送或接收的字节数

timeout:发送超时,以jiffies为单位,0意味着永远等待

返回值:如果函数调用成功,返回0;否则,返回1个负的错误值

drivers/usb/core/message.c

/**
 * usb_bulk_msg - Builds a bulk urb, sends it off and waits for completion
 * @usb_dev: pointer to the usb device to send the message to
 * @pipe: endpoint "pipe" to send the message to
 * @data: pointer to the data to send
 * @len: length in bytes of the data to send
 * @actual_length: pointer to a location to put the actual length transferred
 *      in bytes
 * @timeout: time in msecs to wait for the message to complete before
 *      timing out (if 0 the wait is forever)
 *
 * Context: !in_interrupt ()
 *
 * This function sends a simple bulk message to a specified endpoint
 * and waits for the message to complete, or timeout.
 *
 * Don't use this function from within an interrupt context, like a bottom half
 * handler.  If you need an asynchronous message, or need to send a message
 * from within interrupt context, use usb_submit_urb() If a thread in your
 * driver uses this call, make sure your disconnect() method can wait for it to
 * complete.  Since you don't have a handle on the URB used, you can't cancel
 * the request.
 *
 * Because there is no usb_interrupt_msg() and no USBDEVFS_INTERRUPT ioctl,
 * users are forced to abuse this routine by using it to submit URBs for
 * interrupt endpoints.  We will take the liberty of creating an interrupt URB
 * (with the default interval) if the target is an interrupt endpoint.
 *
 * Return:
 * If successful, 0. Otherwise a negative error number. The number of actual
 * bytes transferred will be stored in the @actual_length parameter.
 *
 */
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
                 void *data, int len, int *actual_length, int timeout)
{
        struct urb *urb;
        struct usb_host_endpoint *ep;

        ep = usb_pipe_endpoint(usb_dev, pipe);
        if (!ep || len < 0)
                return -EINVAL;

        urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!urb)
                return -ENOMEM;

        if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
                        USB_ENDPOINT_XFER_INT) {
                pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
                usb_fill_int_urb(urb, usb_dev, pipe, data, len,
                                usb_api_blocking_completion, NULL,
                                ep->desc.bInterval);
        } else
                usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
                                usb_api_blocking_completion, NULL);

        return usb_start_wait_urb(urb, timeout, actual_length);
}
EXPORT_SYMBOL_GPL(usb_bulk_msg);

(2)usb_control_msg()函数

该函数提供给驱动发送和结束USB控制信息而不是批量信息的能力,该函数的原型为:

linux/usb.h

int usb_control_msg(struct usb_device *dev, unsigned int pipe,  __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout);

dev:指向控制消息发往的USB设备

pipe:控制消息要发往的USB设备的端点

request:这个控制消息的USB请求值

requesttype:这个控制消息的USB请求类型

value:这个控制消息的USB消息值

index:这个控制消息的USB消息索引值

data:指向要发送或接收的数据缓冲区

size:data参数所指向的缓冲区的大小

timeout:发送超时,以毫秒为单位,0意味着永远等待

参数request、requesttype、value和index与USB规范中定义的USB控制消息直接对应

如果函数调用成功,该函数返回发送到设备或从设备接收到的字节数;否则,返回一个负的错误值。

备注:

    由于usb_bulk_msg()和usb_control_msg()是同步的,不能在中断上下文和持有自旋锁的情况下使用。而且,该函数也不能被任何其他函数取消,务必要使得驱动程序的disconnect()函数掌握足够的信息,以判断和等待该调用的结束。

drivers/usb/core/message.c

/**
 * usb_control_msg - Builds a control urb, sends it off and waits for completion
 * @dev: pointer to the usb device to send the message to
 * @pipe: endpoint "pipe" to send the message to
 * @request: USB message request value
 * @requesttype: USB message request type value
 * @value: USB message value
 * @index: USB message index value
 * @data: pointer to the data to send
 * @size: length in bytes of the data to send
 * @timeout: time in msecs to wait for the message to complete before timing
 *      out (if 0 the wait is forever)
 *
 * Context: !in_interrupt ()
 *
 * This function sends a simple control message to a specified endpoint and
 * waits for the message to complete, or timeout.
 *
 * Don't use this function from within an interrupt context, like a bottom half
 * handler.  If you need an asynchronous message, or need to send a message
 * from within interrupt context, use usb_submit_urb().
 * If a thread in your driver uses this call, make sure your disconnect()
 * method can wait for it to complete.  Since you don't have a handle on the
 * URB used, you can't cancel the request.
 *
 * Return: If successful, the number of bytes transferred. Otherwise, a negative
 * error number.
 */
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
                    __u8 requesttype, __u16 value, __u16 index, void *data,
                    __u16 size, int timeout)
{
        struct usb_ctrlrequest *dr;
        int ret;

        dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
        if (!dr)
                return -ENOMEM;

        dr->bRequestType = requesttype;
        dr->bRequest = request;
        dr->wValue = cpu_to_le16(value);
        dr->wIndex = cpu_to_le16(index);
        dr->wLength = cpu_to_le16(size);

        ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
        kfree(dr);

        return ret;
}
EXPORT_SYMBOL_GPL(usb_control_msg);

16.3.3 探测和断开函数

在USB设备驱动usb_driver结构体的探测probe()函数中,应该完成如下工作。

1、探测设备的端点地址、缓冲区大小,初始化任何可能用于控制USB设备的数据结构

2、把已初始化的数据结构的指针保存到接口设备中

usb_set_intfdata()设置usb_interface的私有数据,这个函数的原型为:

linux/usb.h

static inline void usb_set_intfdata(struct usb_interface *intf, void *data)
{
        dev_set_drvdata(&intf->dev, data);
}

得到usb_interface的私有数据

static inline void *usb_get_intfdata(struct usb_interface *intf)
{
        return dev_get_drvdata(&intf->dev);
}  

3、注册USB设备

如果是简单的字符设备,则可调用usb_register_dev(),这个函数的原型为:

linux/usb.h

int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);

上述函数中的第二个参数为usb_class_driver结构体,这个结构体的定义如代码清单16.15所示。

代码清单16.15 usb_class_driver结构体

linux/usb.h

/**
 * struct usb_class_driver - identifies a USB driver that wants to use the USB major number
 * @name: the usb class device name for this driver.  Will show up in sysfs.
 * @devnode: Callback to provide a naming hint for a possible
 *      device node to create.
 * @fops: pointer to the struct file_operations of this driver.
 * @minor_base: the start of the minor range for this driver.
 *
 * This structure is used for the usb_register_dev() and
 * usb_unregister_dev() functions, to consolidate a number of the
 * parameters used for them.
 */
struct usb_class_driver {
        char *name;
        char *(*devnode)(struct device *dev, umode_t *mode);
        const struct file_operations *fops;
        int minor_base;
};

      对于字符设备,usb_class_driver结构体的fops成员中的write()、read()、ioctl()等函数的地位等同于第6章中的file_operations成员函数。

    如果是其他类型的设备,如tty设备,则调用对应设备的注册函数。

    在USB设备驱动usb_driver结构体的断开disconnect()函数中,应该完成如下工作。

1、释放所有为设备分配的资源

2、设置接口设备的数据指针为NULL

3、注销USB设备

对于字符设备,可以直接调用usb_register_dev()函数的“反函数”,如下所示:

linux/usb.h

void usb_deregister_dev(struct usb_interface *intf,

                        struct usb_class_driver *class_driver);

对于其他类型的设备,如tty设备,则调用对应设备的注销函数。

16.3.4 USB骨架程序

    Linux内核源代码中的driver/usb/usb-skeleton.c提供一个最基础的USB驱动程序,即USB骨架程序,

usb-skeleton.c可被看作一个最简单的USB设备驱动实例。

首先看USB骨架程序的usb_driver(USB外设)结构体定义,如代码清单16.16所示。

代码清单16.16 USB骨架程序的usb_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,
};

从结构体可看出,usb_driver 支持的USB设备的列表数组为skel_table[],其定义如代码清单16.17所示。

代码清单16.17 USB骨架程序的id_table

/* 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 const struct usb_device_id skel_table[] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, skel_table);

    usb_driver的注册和注销发生在USB骨架程序的模块加载与卸载函数内,其分别调用了usb_register()和usb_deregister(),注册和注销的代码不用写出来,可直接用一个快捷宏module_usb_driver,如代码清单16.18所示。

代码清单16.18 USB骨架程序的模块加载

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);

    在usb_driver的probe成员函数中,会根据usb_interface的成员寻找第一个批量输入和输出端点,将端点地址、缓冲区等信息存入为USB骨架程序定义的usb_skel结构体中,并将usb_skel实例的指针传入usb_set_intfdata中以作为USB接口的私有数据,最后,注册USB设备,如代码清单16.19所示。

代码清单16.19 USB骨架程序的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) {
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);

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(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 */
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_skel结构体被看作是一个私有数据结构体,其定义如代码清单16.20所示,根据具体的设备量身定制。

代码清单16.20 USB骨架程序的自定义数据结构usb_skel

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

       USB骨架程序的断开函数会完成与probe函数相反的工作,即设置接口数据为NULL,注销USB设备,如代码清单16.21所示。

代码清单16.21 USB骨架程序的断开函数

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

    usb_register_dev(interface,&skel_class)中的第二个参数包含字符设备的file_operations结构体指针,而这个结构体中的成员实现也是USB字符设备的另一个组成成分。代码清单16.22给出USB骨架程序的字符设备文件操作file_operations结构体的定义。

代码清单16.22 USB骨架程序的字符设备文件操作结构体

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

备注:

由于只是一个象征性的骨架程序,open()成员函数的实现简单,该函数根据usb_driver和次设备号通过usb_find_interface()获得USB接口,之后通过usb_get_intfdata()获得接口的私有数据并赋予

file->private_data,如代码清单16.23所示。

代码清单16.23 USB骨架程序的字符设备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);//根据usb_driver和次设备号找到USB接口
if (!interface) {
pr_err("%s - error, can't find device for minor %d\n",
__func__, subminor);
retval = -ENODEV;
goto exit;
}

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; //将接口的私有数据赋值给file->private_data 

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

       读写函数,在访问USB设备时,贯穿其中的“中枢神经”是URB(USB请求块)结构体。在skel_write()函数中进行的关于URB的操作:URB的分配(调用usb_alloc_urb())、初始化(调用usb_fill_bulk_urb())和提交(调用usb_submit_urb())的操作,如代码清单16.24所示。

    代码清单16.24 USB骨架程序的字符设备写函数

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);// 分配URB
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)) { // user->kernel
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,注册写操作完成函数
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);// 提交URB
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); // 释放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;
}

    在写函数中发起的URB结束后,初始化URB时填入的完成函数skel_write_bulk_callback()将被调用,skel_write_bulk_callback()会进行urb->status的判断,如代码清单16.25所示。

代码清单16.25 USB骨架程序的字符设备写操作完成函数

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


猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80589781