static int skel_do_read_io(struct usb_skel *dev, size_t count)
{
int rv;
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); //填充urb
spin_lock_irq(&dev->err_lock);
dev->ongoing_read = 1; //标志正在读取数据中
spin_unlock_irq(&dev->err_lock);
rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL); //提交urb
if (rv < 0) {
err("%s - failed submitting read urb, error %d",
__func__, rv);
dev->bulk_in_filled = 0;
rv = (rv == -ENOMEM) ? rv : -EIO;
spin_lock_irq(&dev->err_lock);
dev->ongoing_read = 0;
spin_unlock_irq(&dev->err_lock);
}
return rv;
}
好了,其实skel_do_read_io只是完成了urb的填充和提交,USB核心读取到了数据后,会调用填充urb时设置的回调函数skel_read_bulk_callback。
static void skel_read_bulk_callback(struct urb *urb)
{
struct usb_skel *dev;
dev = urb->context;
spin_lock(&dev->err_lock);
if (urb->status) { //根据返回状态判断是否出错
if (!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN))
err("%s - nonzero write bulk status received: %d",
__func__, urb->status);
dev->errors = urb->status;
} else {
dev->bulk_in_filled = urb->actual_length; //记录缓冲区的大小
}
dev->ongoing_read = 0; //已经读取数据完毕
spin_unlock(&dev->err_lock);
complete(&dev->bulk_in_completion); //唤醒skel_read函数
}
好了,到目前为止,我们已经把USB驱动框架usb-skeleton.c分析完了,总结下,其实很简单,在模块加载里面注册usb_driver,然后在probe函数里初始化一些参数,最重要的是注册了USB设备,这个USB设备相当于一个字符设备,提供file_operations接口。然后设计open,close,read,write函数,这个open里基本没做什么事情,在write中,通过分配urb、填充urb和提交urb。注意读的urb的分配在probe里申请空间,写的urb的分配在write里申请空间。在这个驱动程序中,我们重点掌握usb_fill_bulk_urb的设计。