一.USB理论部分
1.USB概述
USB1.0版本速度1.5Mbps(低速USB)、 USB1.1版本速度12Mbps(全速USB)、 USB2.0版本速度480Mbps(高速USB)、USB3.0版本速度5.0GMbps(超高速USB)。USB驱动由USB主机控制器驱动和USB设备驱动组成。USB主机控制器是用来控制USB设备和CPU之间通信的,USB主机控制器驱动主要用来驱动芯片上的主机控制器硬件。USB设备驱动主要是指具体的例如USB鼠标,USB键盘灯设备的驱动。
一般的通用的Linux设备,如U盘、USB鼠标、USB键盘,都不需要工程师再编写驱动,需要编写的是特定厂商、特定芯片的驱动,而且往往也可以参考内核中已经提供的驱动模板。 USB只是一个总线,真正的USB设备驱动的主体工作仍然是USB设备本身所属类型的驱动,如字符设备、tty设备、块设备、输入设备等。
2.USB主机控制器
USB主机控制器属于南桥芯片的一部分,通过PCI总线和处理器通信。USB主机控制器分为UHCI(英特尔提出)、OHCI(康柏和微软提出)、 EHCI。其中OHCI驱动程序用来为非PC系统上以及带有SiS和ALi芯片组的PC主办上的USB芯片提供支持。UHCI驱动程序多用来为大多数其他PC主板(包括Intel和Via)上的USB芯片提供支持。ENCI兼容OHCI和UHCI。UHCI的硬件线路比OHCI简单,所以成本较低,但需要较复杂的驱动程序,CPU负荷稍重。主机控制器驱动程序完成的功能主要包括:解析和维护URB,根据不同的端点进行分类缓存URB;负责不同USB传输类型的调度工作;负责USB数据的实际传输工作;实现虚拟跟HUB的功能。3.USB设备与USB驱动的匹配
USB设备与USB驱动怎么匹配的呢?实际上USB设备中有一个模块叫固件,是固件信息和USB驱动进行的匹配。固件是固化在集成电路内部的程序代码,USB固件中包含了USB设备的出厂信息,标识该设备的厂商ID、产品ID、主版本号和次版本号等。另外固件中还包含一组程序,这组程序主要完成USB协议的处理和设备的读写操作。USB设备固件和USB驱动之间通信的规范是通过USB协议来完成的。4.USB设备的逻辑结构和端点的传输方式
USB设备的逻辑结构包括设备、配置、接口和端点,分别用usb_device、usb_host_config、 usb_interface、usb_host_endpoint表示。端点的传输方式包括控制传输、中断传输、批量传输、等时传输。
控制传输主要用于向设备发送配置信息、获取设备信息、发送命令道设备,或者获取设备的状态报告。控制传输一般发送的数据量较小,当USB设备插入时,USB核心使用端点0对设备进行配置,另外,端口0与其他端点不一样,端点0可以双向传输。
中断传输就是中断端点以一个固定的速度来传输较少的数据, USB键盘和鼠标就是使用这个传输方式。这里说的中断和硬件上下文中的中断不一样,它不是设备主动发送一个中断请求,而是主机控制器在保证不大于某个时间间隔内安排一次传输。中断传输对时间要求比较严格,所以可以用中断传输来不断地检测某个设备,当条件满足后再使用批量传输传输大量的数据。
批量传输通常用在数据量大、对数据实时性要求不高的场合,例如 USB打印机、扫描仪、大容量存储设备、U盘等。
等时传输同样可以传输大批量数据,但是对数据是否到达没有保证,它对实时性的要求很高,例如 音频、视频等设备。
5.USB的URB请求块
USB请求块(USB request block,urb)是USB主机控制器和设备通信的主要数据结构,主机和设备之间通过urb进行数据传输。当主机控制器需要与设备交互时,只需要填充一个urb结构,然后将其提交给USB核心,由USB核心负责对其进行处理。URB处理流程:
Step1:创建一个URB结构体 usb_alloc_urb()
Step2:初始化,被安排一个特定的USB设备的特定端点。fill_int/bulk/control_urb()
Step3:被USB设备驱动提交给USB核心usb_submit_urb(),注意GPF_ATOMIC,GPF_NOIO,GPF_KERNEL的使用区别。
Step4:提交由USB核心指定的USB主机控制器驱动,被主机控制器驱动处理,进行一次到USB设备的传输,该过程由USB核心和主机控制器完成,不受USB设备驱动控制
Step5:当urb完成,USB主机控制器驱动通知USB设备驱动。
简单的批量与控制URB:
有时候USB驱动程序只是从USB设备上接收或发送一些简单的数据,这时候可以使用usb_bulk/control_msg()完成,这两个函数是同步的,因此不能在中断上下文和持有自旋锁的情况下使用。
6.USB的枚举过程
内核辅助线程khubd用来监视与该集线器连接的所有端口,通常情况下,该线程处于休眠状态,当集线器驱动程序检测到USB端口状态变化后,该内核线程立马唤醒。USB的枚举过程:USB的枚举过程是热插拔USB设备的起始步骤,该过程中,主机控制器获取设备的相关信息并配置好设备,集线器驱动程序负责该枚举过程。枚举过程主要分如下几步:
Step1:根集线器报告插入设备导致的端口电流变化,集线器驱动程序检测到这一状态变化后,唤醒khubd线程。
Step2:khubd识别出电流变化的那个端口。
Step3:khubd通过给控制端点0发送控制URB来实现从1-127中选出一个数作为插入设备的批量端点。
Step4:khubd利用端口0使用的控制URB从插入的设备那里获得设备描述符,然后获得配置描述符,并选择一个合适的。
Step5:khubd请求USB核心把对应的客户驱动程序和该USB设备挂钩。
二.USB驱动分析
内核代码分析包括USB驱动框架、鼠标驱动、键盘驱动、U盘驱动:USB驱动编写的主要框架/drivers/usb/usb-skeleton.c
USB鼠标驱动 /drivers/hid/usbhid/usbmouse.c
USB键盘驱动动 /drivers/hid/usbhid/usbkbd.c
USB Mass Storage是一类USB存储设备, U盘便是其中之一,主要分析的驱动文件是/drivers/usb/storage/usb.c
1.USB驱动框架usb-skeleton.c
USB骨架程序可以看做一个最简单的USB设备驱动的实例,其分析流程大致如下:module_init(usb_skel_init)---->usb_skel_init()中的usb_register(&skel_driver)---->(驱动中的id_table和设备配置能对上号)skel_driver中的 .probe = skel_probe
---->probe函数中usb_register_dev(interface, &skel_class)语句---->skel_class 中的.fops = &skel_fops---->skel_fops中就是系统调用对应的函数指针,对应的函数
实现了系统调用函数的具体功能,这些函数仅当进行系统调用的时候被执行。
首先看看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,
};
/* 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骨架程序的模块加载和卸载函数中。
static int __init usb_skel_init(void)
{
int result;
/* register this driver with the USB subsystem */
result = usb_register(&skel_driver); //将该驱动挂在USB总线上
if (result)
err("usb_register failed. Error number %d", result);
return result;
}
一个设备被安装或者有设备插入后,当USB总线上经过match匹配成功,就会调用设备驱动程序中的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)); //获取usb_device结构体
dev->interface = interface; //获取usb_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); //分配urb空间
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); //注册 USB设备
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;
}
/* 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_driver的probe函数中根据usb_interface的成员寻找第一个批量输入和输出的端点,将端点地址、缓冲区等信息存入USB骨架程序定义的usb_skel结构体中,并将usb_skel通过usb_set_intfdata传为USB接口的私有数据,最后注册USB设备。
我们来看看这个USB骨架程序定义的usb_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 */
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 */
};
好了看完了probe,我们再看看disconnect函数
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); //注销USB设备
/* 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);
}
我们在skel_probe中最后执行了usb_register_dev(interface, &skel_class)来注册了一个USB设备,我们看看skel_class的定义
static struct usb_class_driver skel_class = {
.name = "skel%d",
.fops = &skel_fops,
.minor_base = USB_SKEL_MINOR_BASE,
};
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,
};
根据上面代码我们知道,其实我们在probe中注册USB设备的时候使用的skel_class是一个包含file_operations的结构体,而这个结构体正是字符设备文件操作结构体。
我们先来看看这个file_operations中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和次设备号获取设备的接口
if (!interface) {
err("%s - error, can't find device for minor %d",
__func__, subminor);
retval = -ENODEV;
goto exit;
}
dev = usb_get_intfdata(interface); //获取接口的私有数据 usb_skel
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; //将 usb_skel设置为文件的私有数据
mutex_unlock(&dev->io_mutex);
exit:
return retval;
}
这个open函数实现非常简单,它根据usb_driver和次设备号通过usb_find_interface获取USB接口,然后通过usb_get_intfdata获得接口的私有数据并赋值给文件。好了,我们看看write函数,在write函数中,我们进行了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); //分配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)) { //将用户空间数据拷贝到缓冲区
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); //提交urb
/* 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_free_coherent(dev->udev, writesize, buf, urb->transfer_dma);
usb_free_urb(urb);
}
up(&dev->limit_sem);
exit:
return retval;
}
首先说明一个问题,填充urb后,设置了transfer_flags标志,当transfer_flags中的URB_NO_TRANSFER_DMA_MAP被设置,USB核心使用transfer_dma指向的缓冲区而不是使用transfer_buffer指向的缓冲区,这表明即将传输DMA缓冲区。当transfer_flags中的URB_NO_SETUP_DMA_MAP被设置,如果控制urb有DMA缓冲区,USB核心将使用setup_dma指向的缓冲区而不是使用setup_packet指向的缓冲区。
另外,通过上面这个write函数我们知道,当写函数发起的urb结束后,其完成函数skel_write_bulk_callback会被调用,我们继续跟踪
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_free_coherent(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma); //释放urb空间
up(&dev->limit_sem);
}
很明显,skel_write_bulk_callback主要对urb->status进行判断,根据错误提示显示错误信息,然后释放urb空间。
接着,我们看看USB骨架程序的字符设备的read函数
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) { //USB核正在读取数据中,数据没准备好
/* 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_for_completion_interruptible(&dev->bulk_in_completion); //等待
if (rv < 0)
goto exit;
/*
* by waiting we also semiprocessed the urb
* we must finish now
*/
dev->bulk_in_copied = 0; //拷贝到用户空间操作已成功
dev->processed_urb = 1; //目前已处理好 urb
}
if (!dev->processed_urb) { //目前还没已处理好 urb
/*
* the URB hasn't been processed
* do it now
*/
wait_for_completion(&dev->bulk_in_completion); //等待完成
dev->bulk_in_copied = 0;
dev->processed_urb = 1;
}
/* 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;
/* no data to deliver */
dev->bulk_in_filled = 0;
/* 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); //没可读数据则调用 IO操作
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); //调用 IO操作
} else {
/* no data in the buffer */
rv = skel_do_read_io(dev, count); //缓冲区没数据则调用 IO操作
if (rv < 0)
goto exit;
else if (!(file->f_flags & O_NONBLOCK))
goto retry;
rv = -EAGAIN;
}
exit:
mutex_unlock(&dev->io_mutex);
return rv;
}
通过上面read函数,我们知道,在读取数据时候,如果发现缓冲区没有数据,或者缓冲区的数据小于用户需要读取的数据量时,则会调用IO操作,也就是skel_do_read_io函数。
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); //填充 urb
/* tell everybody to leave the URB alone */
spin_lock_irq(&dev->err_lock);
dev->ongoing_read = 1; //标志正在读取数据中
spin_unlock_irq(&dev->err_lock);
/* do it */
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);
/* 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);
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的设计。
2.USB鼠标驱动usbmouse.c
下面我们分析下USB鼠标驱动,鼠标输入HID类型,其数据传输采用中断URB,鼠标端点类型为IN。好了,我们先看看这个驱动的模块加载部分。
static int __init usb_mouse_init(void)
{
int retval = usb_register(&usb_mouse_driver);
if (retval == 0)
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
return retval;
}
模块加载部分仍然是调用usb_register注册USB驱动,我们跟踪看看被注册的usb_mouse_drive。
static struct usb_driver usb_mouse_driver = {
.name = "usbmouse",
.probe = usb_mouse_probe,
.disconnect = usb_mouse_disconnect,
.id_table = usb_mouse_id_table,
};
关于设备支持项我们前面已经讨论过了
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
再细细看看USB_INTERFACE_INFO宏的定义
#define USB_INTERFACE_INFO(cl, sc, pr) \
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \
.bInterfaceClass = (cl), \
.bInterfaceSubClass = (sc), \
.bInterfaceProtocol = (pr)
根据宏,我们知道,我们设置的支持项包括接口类,接口子类,接口协议三个匹配项。
好了,我们主要看看usb_driver中定义的probe函数
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id){
struct usb_device *dev = interface_to_usbdev(intf);//由接口获取 usb_device
struct usb_host_interface *interface; //设置
struct usb_endpoint_descriptor *endpoint; //端点描述符
struct usb_mouse *mouse; //本驱动私有结构体
struct input_dev *input_dev; //输入结构体
int pipe, maxp;
int error = -ENOMEM;
interface = intf->cur_altsetting; // 获取设置
if (interface->desc.bNumEndpoints != 1) //鼠标端点只有 1个
return -ENODEV;
endpoint = &interface->endpoint[0].desc; //获得端点描述符
if (!usb_endpoint_is_int_in(endpoint)) //检查该端点是否是中断输入端点
return -ENODEV;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //建立中断输入端点
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); //返回端点能传输的最大的数据包,鼠标的返回的最大数据包为 4个字节
mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL); // 分配 mouse结构体
input_dev = input_allocate_device(); //分配 input设备空间
if (!mouse || !input_dev)
goto fail1;
mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma); //分配缓冲区
if (!mouse->data)
goto fail1;
mouse->irq = usb_alloc_urb(0, GFP_KERNEL); //分配 urb
if (!mouse->irq)
goto fail2;
mouse->usbdev = dev; // 填充 mouse的 usb_device结构体
mouse->dev = input_dev; //填充 mouse的 input结构体
if (dev->manufacturer) //拷贝厂商 ID
strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));
if (dev->product) { //拷贝产品 ID
if (dev->manufacturer)
strlcat(mouse->name, " ", sizeof(mouse->name));
strlcat(mouse->name, dev->product, sizeof(mouse->name));
}
if (!strlen(mouse->name)) //拷贝产品 ID
snprintf(mouse->name, sizeof(mouse->name),
"USB HIDBP Mouse %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
strlcat(mouse->phys, "/input0", sizeof(mouse->phys));
input_dev->name = mouse->name; //将鼠标名赋给内嵌 input结构体
input_dev->phys = mouse->phys; //将鼠标设备节点名赋给内嵌 input结构体
usb_to_input_id(dev, &input_dev->id); // 将 usb_driver的支持项拷贝给 input
input_dev->dev.parent = &intf->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); //evbit表明支持按键事件 (EV_KEY)和相对坐标事件 (EV_REL)
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); //keybit表明按键值包括左键、右键和中键
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); //relbit表明相对坐标事件值包括 X坐标和 Y坐标
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
BIT_MASK(BTN_EXTRA); //keybit表明除了左键、右键和中键,还支持其他按键
input_dev->relbit[0] |= BIT_MASK(REL_WHEEL); //relbit 表明除了 X坐标和 Y坐标,还支持中键滚轮的滚动值
input_set_drvdata(input_dev, mouse); //将 mouse设置为 input的私有数据
input_dev->open = usb_mouse_open; //input设备的 open
input_dev->close = usb_mouse_close;
usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
(maxp > 8 ? 8 : maxp),
usb_mouse_irq, mouse, endpoint->bInterval); //填充 urb
mouse->irq->transfer_dma = mouse->data_dma;
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /使用 transfer_dma
error = input_register_device(mouse->dev); //注册 input设备
if (error)
goto fail3;
usb_set_intfdata(intf, mouse);
return 0;
fail3:
usb_free_urb(mouse->irq);
fail2:
usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);
fail1:
input_free_device(input_dev);
kfree(mouse);
return error;
}
其实上面这个probe主要是初始化usb设备和input设备,终极目标是为了完成urb的提交和input设备的注册。由于注册为input设备类型,那么当用户层open打开设备时候,最终会调用input中的open实现打开,我们看看input中open的实现
static int usb_mouse_open(struct input_dev *dev)
{
struct usb_mouse *mouse = input_get_drvdata(dev); //获取私有数据
mouse->irq->dev = mouse->usbdev; //获取 urb指针
if (usb_submit_urb(mouse->irq, GFP_KERNEL)) //提交 urb
return -EIO;
return 0;
}
好了,当用户层open打开这个USB鼠标后,我们就已经将urb提交给了USB核心,那么根据USB数据处理流程知道,当处理完毕后,USB核心会通知USB设备驱动程序,这里我们是响应中断服务程序,这就相当于该URB的回调函数。我们在提交urb时候定义了中断服务程序usb_mouse_irq,我们跟踪看看
static void usb_mouse_irq(struct urb *urb)
{
struct usb_mouse *mouse = urb->context;
signed char *data = mouse->data;
struct input_dev *dev = mouse->dev;
int status;
switch (urb->status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
input_report_key(dev, BTN_LEFT, data[0] & 0x01);//鼠标左键
input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
input_report_key(dev, BTN_SIDE, data[0] & 0x08);
input_report_key(dev, BTN_EXTRA, data[0] & 0x10);
input_report_rel(dev, REL_X, data[1]);
input_report_rel(dev, REL_Y, data[2]);
input_report_rel(dev, REL_WHEEL, data[3]);
input_sync(dev);
resubmit:
status = usb_submit_urb (urb, GFP_ATOMIC);//再次提交urb,等待下次响应
if (status)
err ("can't resubmit intr, %s-%s/input0, status %d",
mouse->usbdev->bus->bus_name,
mouse->usbdev->devpath, status);
}
根据上面的中断服务程序,我们应该知道,系统是周期性地获取鼠标的事件信息,因此在URB回调函数的末尾再次提交URB请求块,这样又会调用新的回调函数,周而复始。在回调函数中提交URB只能是GFP_ATOMIC优先级,因为URB回调函数运行于中断上下文中禁止导致睡眠的行为。而在提交URB过程中可能会需要申请内存、保持信号量,这些操作或许会导致USB内核睡眠。
最后我们再看看这个驱动的私有数据mouse的定义
struct usb_mouse {
char name[128];//名字
char phys[64];//设备节点
struct usb_device *usbdev;//内嵌usb_device设备
struct input_dev *dev;//内嵌input_dev设备
struct urb *irq;//urb结构体
signed char *data;//transfer_buffer缓冲区
dma_addr_t data_dma;// transfer _dma缓冲区
};
在上面这个结构体中,每一个成员的作用都应该很清楚了,尤其最后两个的使用区别和作用,前面也已经说过。
如果最终需要测试这个USB鼠标驱动,需要在内核中配置USB支持、对HID接口的支持、对OHCI HCD驱动的支持。另外,将驱动移植到开发板之后,由于采用的是input设备模型,所以还需要开发板带LCD屏才能测试。
3.USB键盘驱动usbkbd.c
跟USB鼠标类型,USB键盘也属于HID类型,代码在/dirver/hid/usbhid/usbkbd.c下。USB键盘除了提交中断URB外,还需要提交控制URB。
static int __init usb_kbd_init(void)
{
int result = usb_register(&usb_kbd_driver);
if (result == 0)
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
return result;
}
static struct usb_driver usb_kbd_driver = {
.name = "usbkbd",
.probe = usb_kbd_probe,
.disconnect = usb_kbd_disconnect,
.id_table = usb_kbd_id_table,
};
static int usb_kbd_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(iface);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_kbd *kbd;
struct input_dev *input_dev;
int i, pipe, maxp;
int error = -ENOMEM;
interface = iface->cur_altsetting;
if (interface->desc.bNumEndpoints != 1)
return -ENODEV;
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);//建立中断输入端点
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));//获取返回字节大小
kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL); //分配私有数据空间
input_dev = input_allocate_device(); //分配input设备空间
if (!kbd || !input_dev)
goto fail1;
if (usb_kbd_alloc_mem(dev, kbd))//分配urb空间和其他缓冲空间
goto fail2;
kbd->usbdev = dev;//给内嵌结构体赋值
kbd->dev = input_dev;//给内嵌结构体赋值
if (dev->manufacturer)
strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
if (dev->product) {
if (dev->manufacturer)
strlcat(kbd->name, " ", sizeof(kbd->name));
strlcat(kbd->name, dev->product, sizeof(kbd->name));
}
if (!strlen(kbd->name))
snprintf(kbd->name, sizeof(kbd->name),
"USB HIDBP Keyboard %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
strlcat(kbd->phys, "/input0", sizeof(kbd->phys));
input_dev->name = kbd->name;
input_dev->phys = kbd->phys;
usb_to_input_id(dev, &input_dev->id);//复制usb_driver的支持项给input的支持项
input_dev->dev.parent = &iface->dev;
input_set_drvdata(input_dev, kbd);//将kbd设置为input的私有数据
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
BIT_MASK(EV_REP);
input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) |
BIT_MASK(LED_KANA);
for (i = 0; i < 255; i++)
set_bit(usb_kbd_keycode[i], input_dev->keybit);
clear_bit(0, input_dev->keybit);
input_dev->event = usb_kbd_event;//定义event函数
input_dev->open = usb_kbd_open;
input_dev->close = usb_kbd_close;
usb_fill_int_urb(kbd->irq, dev, pipe,
kbd->new, (maxp > 8 ? 8 : maxp),
usb_kbd_irq, kbd, endpoint->bInterval);//填充中断urb
kbd->irq->transfer_dma = kbd->new_dma;
kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;//dma方式传输
kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
kbd->cr->bRequest = 0x09;//设置控制请求的格式
kbd->cr->wValue = cpu_to_le16(0x200);
kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
kbd->cr->wLength = cpu_to_le16(1);
usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
(void *) kbd->cr, kbd->leds, 1,
usb_kbd_led, kbd);//填充控制urb
kbd->led->transfer_dma = kbd->leds_dma;
kbd->led->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //设置dma和setup_dma有效
error = input_register_device(kbd->dev);//注册input设备
if (error)
goto fail2;
usb_set_intfdata(iface, kbd);
device_set_wakeup_enable(&dev->dev, 1);
return 0;
fail2:
usb_kbd_free_mem(dev, kbd);
fail1:
input_free_device(input_dev);
kfree(kbd);
return error;
}
在上面的probe中,我们主要是初始化一些结构体,然后提交中断urb和控制urb,并注册input设备。其中有几个地方需要细看下,其一,usb_kbd_alloc_mem的实现。其二,设置控制请求的格式。
static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
{
if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))//分配中断urb
return -1;
if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))//分配控制urb
return -1;
if (!(kbd->new = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &kbd->new_dma)))
return -1;
if (!(kbd->cr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL))) //分配控制urb使用的控制请求描述符
return -1;
if (!(kbd->leds = usb_alloc_coherent(dev, 1, GFP_ATOMIC, &kbd->leds_dma))) //分配控制urb使用的缓冲区
return -1;
return 0;
}
这里我们需要明白中断urb和控制urb需要分配不同的urb结构体,同时在提交urb之前,需要填充的内容也不同,中断urb填充的是缓冲区和中断处理函数,控制urb填充的是控制请求描述符和回调函数。
好了,接着我们解决第二个问题,设置控制请求的格式。cr是struct usb_ctrlrequest结构的指针,USB协议中规定一个控制请求的格式为一个8个字节的数据包,其定义如下
struct usb_ctrlrequest {
__u8 bRequestType;//设定传输方向、请求类型等
__u8 bRequest;//指定哪个请求,可以是规定的标准值也可以是厂家定义的值
__le16 wValue;//即将写到寄存器的数据
__le16 wIndex;//接口数量,也就是寄存器的偏移地址
__le16 wLength;//数据传输阶段传输多少个字节
} __attribute__ ((packed));
USB协议中规定,所有的USB设备都会响应主机的一些请求,这些请求来自USB主机控制器,主机控制器通过设备的默认控制管道发出这些请求。默认的管道为0号端口对应的那个管道。
同样这个input设备首先由用户层调用open函数,所以先看看input中定义的open
static int usb_kbd_open(struct input_dev *dev)
{
struct usb_kbd *kbd = input_get_drvdata(dev);
kbd->irq->dev = kbd->usbdev;
if (usb_submit_urb(kbd->irq, GFP_KERNEL))//提交中断urb
return -EIO;
return 0;
}
因为这个驱动里面有一个中断urb一个控制urb,我们先看中断urb的处理流程。中断urb在input的open中被提交后,当USB核心处理完毕,会通知这个USB设备驱动,然后执行回调函数,也就是中断处理函数usb_kbd_irq
static void usb_kbd_irq(struct urb *urb)
{
struct usb_kbd *kbd = urb->context;
int i;
switch (urb->status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;//出错就再次提交中断urb
}
for (i = 0; i < 8; i++)
input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);//向input子系统报告
for (i = 2; i < 8; i++) {
if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
if (usb_kbd_keycode[kbd->old[i]])
input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
else
hid_info(urb->dev,
"Unknown key (scancode %#x) released.\n",
kbd->old[i]);
}
if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
if (usb_kbd_keycode[kbd->new[i]])
input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
else
hid_info(urb->dev,
"Unknown key (scancode %#x) released.\n",
kbd->new[i]);
}
}
input_sync(kbd->dev);
memcpy(kbd->old, kbd->new, 8);
resubmit:
i = usb_submit_urb (urb, GFP_ATOMIC);//再次提交中断urb
if (i)
hid_err(urb->dev, "can't resubmit intr, %s-%s/input0, status %d",
kbd->usbdev->bus->bus_name,
kbd->usbdev->devpath, i);
}
这个就是中断urb的处理流程,跟前面讲的的USB鼠标中断处理流程类似。好了,我们再来看看剩下的控制urb处理流程吧。
我们有个疑问,我们知道在probe中,我们填充了中断urb和控制urb,但是在input的open中,我们只提交了中断urb,那么控制urb什么时候提交呢?
我们知道对于input子系统,如果有事件被响应,我们会调用事件处理层的event函数,而该函数最终调用的是input下的event。所以,对于input设备,我们在USB键盘驱动中只设置了支持LED选项,也就是ledbit项,这是怎么回事呢?刚才我们分析的那个中断urb其实跟这个input基本没啥关系,中断urb并不是像讲键盘input实现的那样属于input下的中断。我们在USB键盘驱动中的input子系统中只设计了LED选项,那么当input子系统有按键选项的时候必然会使得内核调用事件处理层的event函数,最终调用input下的event。好了,那我们来看看input下的event干了些什么。
static int usb_kbd_event(struct input_dev *dev, unsigned int type,
unsigned int code, int value)
{
struct usb_kbd *kbd = input_get_drvdata(dev);
if (type != EV_LED) //不是LED事件就返回
return -1;
kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
(!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
(!!test_bit(LED_NUML, dev->led));//将当前的LED值保存在kbd->newleds中
if (kbd->led->status == -EINPROGRESS)
return 0;
if (*(kbd->leds) == kbd->newleds)
return 0;
*(kbd->leds) = kbd->newleds;
kbd->led->dev = kbd->usbdev;
if (usb_submit_urb(kbd->led, GFP_ATOMIC))//提交控制urb
pr_err("usb_submit_urb(leds) failed\n");
return 0;
}
当在input的event里提交了控制urb后,经过URB处理流程,最后返回给USB设备驱动的回调函数,也就是在probe中定义的usb_kbd_led
static void usb_kbd_led(struct urb *urb)
{
struct usb_kbd *kbd = urb->context;
if (urb->status)//提交失败显示
hid_warn(urb->dev, "led urb status %d received\n",
urb->status);
if (*(kbd->leds) == kbd->newleds)//比较kbd->leds和kbd->newleds,如果发生变化,则更新kbd->leds
return;
*(kbd->leds) = kbd->newleds;
kbd->led->dev = kbd->usbdev;
if (usb_submit_urb(kbd->led, GFP_ATOMIC))//再次提交控制urb
hid_err(urb->dev, "usb_submit_urb(leds) failed\n");
}
总结下,我们的控制urb走的是先由input的event提交,触发后由控制urb的回调函数再次提交。好了,通过USB鼠标,我们已经知道了控制urb和中断urb的设计和处理流程。
4.U盘驱动分析
USB Mass Storage是一类USB存储设备,这些设备包括USB磁盘、USB硬盘、USB磁带机、USB光驱、U盘、记忆棒、智能卡和一些USB摄像头等,这类设备由USB协议支持。
首先我想去看看/driver/usb/storage/Makefile
ccflags-y := -Idrivers/scsi
obj-$(CONFIG_USB_UAS) += uas.o
obj-$(CONFIG_USB_STORAGE) += usb-storage.o
usb-storage-y := scsiglue.o protocol.o transport.o usb.o
usb-storage-y += initializers.o sierra_ms.o option_ms.o
usb-storage-$(CONFIG_USB_STORAGE_DEBUG) += debug.o
这是Makefile中前几行代码,在此我进行一个说明。第一行,-I选项表示需要编译的目录。当本Makefile文件被编译器读取时,会先判断/driver/scsi目录下的文件是否已经被编译,如果没有被编译,则先编译该目录下的文件后,再转到该Makefile文件中。第三行就是USB Mass Storage选项,是总指挥。第四、五行说明了这个文件夹也就是usb-storage模块必须包含的文件,这些文件将是主要分析的对象。第六行是调试部分。目前我们分析USB驱动,所以重点去分析这些文件中的usb.c
同样,我们先看看usb.c中的模块加载部分
static int __init usb_stor_init(void)
{
int retval;
pr_info("Initializing USB Mass Storage driver...\n");
/* register the driver, return usb_register return code if error */
retval = usb_register(&usb_storage_driver);
if (retval == 0) {
pr_info("USB Mass Storage support registered.\n");
usb_usual_set_present(USB_US_TYPE_STOR);
}
return retval;
}
static struct usb_driver usb_storage_driver = {
.name = "usb-storage",
.probe = storage_probe,
.disconnect = usb_stor_disconnect,
.suspend = usb_stor_suspend,
.resume = usb_stor_resume,
.reset_resume = usb_stor_reset_resume,
.pre_reset = usb_stor_pre_reset,
.post_reset = usb_stor_post_reset,
.id_table = usb_storage_usb_ids,
.supports_autosuspend = 1,
.soft_unbind = 1,
};
下面重点我们来看看这个probe函数
/* The main probe routine for standard devices */
static int storage_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct us_data *us;
int result;
/*
* If libusual is configured, let it decide whether a standard
* device should be handled by usb-storage or by ub.
* If the device isn't standard (is handled by a subdriver
* module) then don't accept it.
*/
if (usb_usual_check_type(id, USB_US_TYPE_STOR) ||
usb_usual_ignore_device(intf))//检测匹配
return -ENXIO;
/*
* Call the general probe procedures.
*
* The unusual_dev_list array is parallel to the usb_storage_usb_ids
* table, so we use the index of the id entry to find the
* corresponding unusual_devs entry.
*/
result = usb_stor_probe1(&us, intf, id,
(id - usb_storage_usb_ids) + us_unusual_dev_list);//探测的第一部分
if (result)
return result;
/* No special transport or protocol settings in the main module */
result = usb_stor_probe2(us);//探测的第二部分
return result;
}
我们发现U盘驱动的探测分为两个部分,我们先来看看第一个部分usb_stor_probe1
/* First part of general USB mass-storage probing */
int usb_stor_probe1(struct us_data **pus,
struct usb_interface *intf,
const struct usb_device_id *id,
struct us_unusual_dev *unusual_dev)
{
struct Scsi_Host *host;
struct us_data *us;
int result;
US_DEBUGP("USB Mass Storage device detected\n");
/*
* Ask the SCSI layer to allocate a host structure, with extra
* space at the end for our private us_data structure.
*/
host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us));//分配Scsi_Host结构体
if (!host) {
dev_warn(&intf->dev,
"Unable to allocate the scsi host\n");
return -ENOMEM;
}
/*
* Allow 16-byte CDBs and thus > 2TB
*/
host->max_cmd_len = 16;
host->sg_tablesize = usb_stor_sg_tablesize(intf);
*pus = us = host_to_us(host); //从host结构体中提取出us_data结构体
memset(us, 0, sizeof(struct us_data));
mutex_init(&(us->dev_mutex));
init_completion(&us->cmnd_ready);//初始化完成量
init_completion(&(us->notify));
init_waitqueue_head(&us->delay_wait); //初始化等待队列头
init_completion(&us->scanning_done);
/* Associate the us_data structure with the USB device */
result = associate_dev(us, intf);//将us_data与USB设备相关联
if (result)
goto BadDevice;
/* Get the unusual_devs entries and the descriptors */
result = get_device_info(us, id, unusual_dev);//获取设备信息
if (result)
goto BadDevice;
/* Get standard transport and protocol settings */
get_transport(us);//获取传输方式
get_protocol(us);//获取传输协议
/* Give the caller a chance to fill in specialized transport
* or protocol settings.
*/
return 0;
BadDevice:
US_DEBUGP("storage_probe() failed\n");
release_everything(us);
return result;
}
我们再看看U盘驱动的探测的第二部分usb_stor_probe2
/* Second part of general USB mass-storage probing */
int usb_stor_probe2(struct us_data *us)
{
struct task_struct *th;
int result;
struct device *dev = &us->pusb_intf->dev;
/* Make sure the transport and protocol have both been set */
if (!us->transport || !us->proto_handler) {
result = -ENXIO;
goto BadDevice;
}
US_DEBUGP("Transport: %s\n", us->transport_name);
US_DEBUGP("Protocol: %s\n", us->protocol_name);
/* fix for single-lun devices */
if (us->fflags & US_FL_SINGLE_LUN)
us->max_lun = 0;
/* Find the endpoints and calculate pipe values */
result = get_pipes(us);//获得管道
if (result)
goto BadDevice;
/*
* If the device returns invalid data for the first READ(10)
* command, indicate the command should be retried.
*/
if (us->fflags & US_FL_INITIAL_READ10)
set_bit(US_FLIDX_REDO_READ10, &us->dflags);
/* Acquire all the other resources and add the host */
result = usb_stor_acquire_resources(us); //获取资源
if (result)
goto BadDevice;
snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s",
dev_name(&us->pusb_intf->dev));
result = scsi_add_host(us_to_host(us), dev);//添加scsi
if (result) {
dev_warn(dev,
"Unable to add the scsi host\n");
goto BadDevice;
}
/* Start up the thread for delayed SCSI-device scanning */
th = kthread_create(usb_stor_scan_thread, us, "usb-stor-scan");//创建线程
if (IS_ERR(th)) {
dev_warn(dev,
"Unable to start the device-scanning thread\n");
complete(&us->scanning_done);
quiesce_and_remove_host(us);
result = PTR_ERR(th);
goto BadDevice;
}
usb_autopm_get_interface_no_resume(us->pusb_intf);
wake_up_process(th);//唤醒usb_stor_scan_thread线程
return 0;
/* We come here if there are any problems */
BadDevice:
US_DEBUGP("storage_probe() failed\n");
release_everything(us);
return result;
}
好了,我们已经把probe大致阅读了一下,主要通过assocaite_dev(),get_device_info(),get_transport(),get_protocol(),get_pipes()五个函数来为us结构体赋值,然后调用usb_stor_acquire_resources()来得到设备需要的动态资源。最后创建扫描线程usb_stor_scan_thread,让用户能通过cat /proc/scsi/scsi看到U盘设备。现在我们一个个分析下这里提到了每个函数。
首先我们看看来为us结构体赋值的设备关联函数associate_dev的实现
/* Associate our private data with the USB device */
static int associate_dev(struct us_data *us, struct usb_interface *intf)
{
US_DEBUGP("-- %s\n", __func__);
/* Fill in the device-related fields */
us->pusb_dev = interface_to_usbdev(intf);//由接口获取设备
us->pusb_intf = intf;//接口赋值
us->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;//接口数量
US_DEBUGP("Vendor: 0x%04x, Product: 0x%04x, Revision: 0x%04x\n",
le16_to_cpu(us->pusb_dev->descriptor.idVendor),
le16_to_cpu(us->pusb_dev->descriptor.idProduct),
le16_to_cpu(us->pusb_dev->descriptor.bcdDevice));
US_DEBUGP("Interface Subclass: 0x%02x, Protocol: 0x%02x\n",
intf->cur_altsetting->desc.bInterfaceSubClass,
intf->cur_altsetting->desc.bInterfaceProtocol);
/* Store our private data in the interface */
usb_set_intfdata(intf, us);//把us设置为接口的私有数据
/* Allocate the control/setup and DMA-mapped buffers */
us->cr = kmalloc(sizeof(*us->cr), GFP_KERNEL);//分配控制urb的控制字符空间
if (!us->cr) {
US_DEBUGP("usb_ctrlrequest allocation failed\n");
return -ENOMEM;
}
us->iobuf = usb_alloc_coherent(us->pusb_dev, US_IOBUF_SIZE,
GFP_KERNEL, &us->iobuf_dma);//分配urb的缓冲区
if (!us->iobuf) {
US_DEBUGP("I/O buffer allocation failed\n");
return -ENOMEM;
}
return 0;
}
然后我们继续看获得设备信息函数get_device_info的实现
/* Get the unusual_devs entries and the string descriptors */
static int get_device_info(struct us_data *us, const struct usb_device_id *id,
struct us_unusual_dev *unusual_dev)
{
struct usb_device *dev = us->pusb_dev;
struct usb_interface_descriptor *idesc =
&us->pusb_intf->cur_altsetting->desc;
struct device *pdev = &us->pusb_intf->dev;
/* Store the entries */
us->unusual_dev = unusual_dev; //不常用的设备
//找到USB设备支持的子类和协议
us->subclass = (unusual_dev->useProtocol == USB_SC_DEVICE) ?
idesc->bInterfaceSubClass :
unusual_dev->useProtocol;
us->protocol = (unusual_dev->useTransport == USB_PR_DEVICE) ?
idesc->bInterfaceProtocol :
unusual_dev->useTransport;
us->fflags = USB_US_ORIG_FLAGS(id->driver_info);
adjust_quirks(us);
if (us->fflags & US_FL_IGNORE_DEVICE) { //USB设备不能被系统识别则退出
dev_info(pdev, "device ignored\n");
return -ENODEV;
}
/*
* This flag is only needed when we're in high-speed, so let's
* disable it if we're in full-speed
*/
if (dev->speed != USB_SPEED_HIGH) //USB设备不支持高速则改为低速
us->fflags &= ~US_FL_GO_SLOW;
if (us->fflags)
dev_info(pdev, "Quirks match for vid %04x pid %04x: %lx\n",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct),
us->fflags);
/* Log a message if a non-generic unusual_dev entry contains an
* unnecessary subclass or protocol override. This may stimulate
* reports from users that will help us remove unneeded entries
* from the unusual_devs.h table.
*/
//根据生产厂商和产品号来设置协议、传输类型等参数
if (id->idVendor || id->idProduct) {
static const char *msgs[3] = {
"an unneeded SubClass entry",
"an unneeded Protocol entry",
"unneeded SubClass and Protocol entries"};
struct usb_device_descriptor *ddesc = &dev->descriptor;
int msg = -1;
if (unusual_dev->useProtocol != USB_SC_DEVICE &&
us->subclass == idesc->bInterfaceSubClass)
msg += 1;
if (unusual_dev->useTransport != USB_PR_DEVICE &&
us->protocol == idesc->bInterfaceProtocol)
msg += 2;
if (msg >= 0 && !(us->fflags & US_FL_NEED_OVERRIDE))
dev_notice(pdev, "This device "
"(%04x,%04x,%04x S %02x P %02x)"
" has %s in unusual_devs.h (kernel"
" %s)\n"
" Please send a copy of this message to "
"<linux-usb@vger.kernel.org> and "
"<usb-storage@lists.one-eyed-alien.net>\n",
le16_to_cpu(ddesc->idVendor),
le16_to_cpu(ddesc->idProduct),
le16_to_cpu(ddesc->bcdDevice),
idesc->bInterfaceSubClass,
idesc->bInterfaceProtocol,
msgs[msg],
utsname()->release);
}
return 0;
}
我们继续看得到传输方式函数get_transport,这个函数主要获得USB设备支持的通信协议,并设置USB驱动的传输类型。对于U盘,USB协议规定它属于Bulk-only的传输方式,也就是它的us->protocot为US_PR_BULK
/* Get the transport settings */
static void get_transport(struct us_data *us)
{
switch (us->protocol) {
case USB_PR_CB:
us->transport_name = "Control/Bulk";
us->transport = usb_stor_CB_transport;
us->transport_reset = usb_stor_CB_reset;
us->max_lun = 7;
break;
case USB_PR_CBI:
us->transport_name = "Control/Bulk/Interrupt";
us->transport = usb_stor_CB_transport;
us->transport_reset = usb_stor_CB_reset;
us->max_lun = 7;
break;
case USB_PR_BULK:
us->transport_name = "Bulk";
us->transport = usb_stor_Bulk_transport;//传输函数
us->transport_reset = usb_stor_Bulk_reset;
break;
}
}
好了,接着我们看获得协议信息的get_protocol函数,该函数根据不同的协议,用来设置协议的传输函数。对于U盘,USB协议规定us->subclass为US_SC_SCSI
/* Get the protocol settings */
static void get_protocol(struct us_data *us)
{
switch (us->subclass) {
case USB_SC_RBC:
us->protocol_name = "Reduced Block Commands (RBC)";
us->proto_handler = usb_stor_transparent_scsi_command;//协议处理函数
break;
case USB_SC_8020:
us->protocol_name = "8020i";
us->proto_handler = usb_stor_pad12_command;
us->max_lun = 0;
break;
case USB_SC_QIC:
us->protocol_name = "QIC-157";
us->proto_handler = usb_stor_pad12_command;
us->max_lun = 0;
break;
case USB_SC_8070:
us->protocol_name = "8070i";
us->proto_handler = usb_stor_pad12_command;
us->max_lun = 0;
break;
case USB_SC_SCSI:
us->protocol_name = "Transparent SCSI";
us->proto_handler = usb_stor_transparent_scsi_command;
break;
case USB_SC_UFI:
us->protocol_name = "Uniform Floppy Interface (UFI)";
us->proto_handler = usb_stor_ufi_command;
break;
}
}
最后一个初始化us的函数是获得管道信息的get_pipes函数。
/* Get the pipe settings */
static int get_pipes(struct us_data *us)
{
struct usb_host_interface *altsetting =
us->pusb_intf->cur_altsetting; //获取设置
int i;
struct usb_endpoint_descriptor *ep; //定义端点描述符
struct usb_endpoint_descriptor *ep_in = NULL;//定义输入端点描述符
struct usb_endpoint_descriptor *ep_out = NULL;//定义输出端点描述符
struct usb_endpoint_descriptor *ep_int = NULL;//定义中断端点描述符
/*
* Find the first endpoint of each type we need.
* We are expecting a minimum of 2 endpoints - in and out (bulk).
* An optional interrupt-in is OK (necessary for CBI protocol).
* We will ignore any others.
*/
for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
ep = &altsetting->endpoint[i].desc;//获取端点描述符
if (usb_endpoint_xfer_bulk(ep)) { //是否是批量传输端点
if (usb_endpoint_dir_in(ep)) { //是否是输入端点
if (!ep_in)
ep_in = ep;//设置为批量传输输入端点
} else {
if (!ep_out)
ep_out = ep;/设置为批量传输输出端点
}
}
else if (usb_endpoint_is_int_in(ep)) { //是否是中断端点
if (!ep_int)
ep_int = ep;//设置为中断端点
}
}
if (!ep_in || !ep_out || (us->protocol == USB_PR_CBI && !ep_int)) {
US_DEBUGP("Endpoint sanity check failed! Rejecting dev.\n");
return -EIO;
}
/* Calculate and store the pipe values */
us->send_ctrl_pipe = usb_sndctrlpipe(us->pusb_dev, 0);//建立输出控制端点
us->recv_ctrl_pipe = usb_rcvctrlpipe(us->pusb_dev, 0);//建立输入控制端点
us->send_bulk_pipe = usb_sndbulkpipe(us->pusb_dev,
usb_endpoint_num(ep_out));//建立输出批量传输端点
us->recv_bulk_pipe = usb_rcvbulkpipe(us->pusb_dev,
usb_endpoint_num(ep_in));//建立输入批量传输端点
if (ep_int) {
us->recv_intr_pipe = usb_rcvintpipe(us->pusb_dev,
usb_endpoint_num(ep_int));//建立中断传输端点
us->ep_bInterval = ep_int->bInterval;//设置中断间隔时间
}
return 0;
}
析完上面get_pipes的代码,需要补充说明的是,在我们的U盘中只有输入批量传输和输出批量传输两个端点,不存在控制端点,如果出现控制端点,那么设备支持CBI协议,即Control/Bulk/Interrupt协议,另外U盘也没有中断端点。
分析完上面五个对cr初始化的函数后,我们接着需要看usb_stor_acquire_resources了,这个函数主要功能是初始化设备,并创建数据传输的控制线程。
/* Initialize all the dynamic resources we need */
static int usb_stor_acquire_resources(struct us_data *us)
{
int p;
struct task_struct *th;
us->current_urb = usb_alloc_urb(0, GFP_KERNEL); //申请urb
if (!us->current_urb) {
US_DEBUGP("URB allocation failed\n");
return -ENOMEM;
}
/* Just before we start our control thread, initialize
* the device if it needs initialization */
if (us->unusual_dev->initFunction) { //特殊设备的初始化函数
p = us->unusual_dev->initFunction(us);
if (p)
return p;
}
/* Start up our control thread */
th = kthread_run(usb_stor_control_thread, us, "usb-storage"); //创建并执行控制线程
if (IS_ERR(th)) {
dev_warn(&us->pusb_intf->dev,
"Unable to start control thread\n");
return PTR_ERR(th);
}
us->ctl_thread = th; //保存线程号
return 0;
}
在上面这个usb_stor_acquire_resources函数中,我们创建并执行了usb_stor_control_thread这个内核线程,这个控制线程用来完成数据的接收和发送,它会一直运行,直到驱动程序退出。
我们来看看这个控制线程。
static int usb_stor_control_thread(void * __us)
{
struct us_data *us = (struct us_data *)__us;
struct Scsi_Host *host = us_to_host(us);
for(;;) {
US_DEBUGP("*** thread sleeping.\n");
if (wait_for_completion_interruptible(&us->cmnd_ready))//等待用户层SCSI命令唤醒
break;
US_DEBUGP("*** thread awakened.\n");
/* lock the device pointers */
mutex_lock(&(us->dev_mutex));
/* lock access to the state */
scsi_lock(host);
/* When we are called with no command pending, we're done */
if (us->srb == NULL) {//为循环中超时后的退出
scsi_unlock(host);
mutex_unlock(&us->dev_mutex);
US_DEBUGP("-- exiting\n");
break;
}
/* has the command timed out *already* ? */
if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { //直接跳到超时判断去
us->srb->result = DID_ABORT << 16;
goto SkipForAbort;
}
scsi_unlock(host);
/* reject the command if the direction indicator
* is UNKNOWN
*/
if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) {//方向
US_DEBUGP("UNKNOWN data direction\n");
us->srb->result = DID_ERROR << 16;
}
/* reject if target != 0 or if LUN is higher than
* the maximum known LUN
*/
else if (us->srb->device->id &&
!(us->fflags & US_FL_SCM_MULT_TARG)) {
US_DEBUGP("Bad target number (%d:%d)\n",
us->srb->device->id, us->srb->device->lun);
us->srb->result = DID_BAD_TARGET << 16;
}
else if (us->srb->device->lun > us->max_lun) {
US_DEBUGP("Bad LUN (%d:%d)\n",
us->srb->device->id, us->srb->device->lun);
us->srb->result = DID_BAD_TARGET << 16;
}
/* Handle those devices which need us to fake
* their inquiry data */
else if ((us->srb->cmnd[0] == INQUIRY) &&
(us->fflags & US_FL_FIX_INQUIRY)) {//如果SCSI是请求命令的处理
unsigned char data_ptr[36] = {
0x00, 0x80, 0x02, 0x02,
0x1F, 0x00, 0x00, 0x00};
US_DEBUGP("Faking INQUIRY command\n");
fill_inquiry_response(us, data_ptr, 36); //填充一个请求命令
us->srb->result = SAM_STAT_GOOD;
}
/* we've got a command, let's do it! */
else {
US_DEBUG(usb_stor_show_command(us->srb));
us->proto_handler(us->srb, us); //数据传输
usb_mark_last_busy(us->pusb_dev);
}
/* lock access to the state */
scsi_lock(host);
/* indicate that the command is done */
if (us->srb->result != DID_ABORT << 16) {
US_DEBUGP("scsi cmd done, result=0x%x\n",
us->srb->result);
us->srb->scsi_done(us->srb);
} else {
SkipForAbort:
US_DEBUGP("scsi command aborted\n");
}
/* If an abort request was received we need to signal that
* the abort has finished. The proper test for this is
* the TIMED_OUT flag, not srb->result == DID_ABORT, because
* the timeout might have occurred after the command had
* already completed with a different result code. */
if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {//超时处理
complete(&(us->notify));
/* Allow USB transfers to resume */
clear_bit(US_FLIDX_ABORTING, &us->dflags);
clear_bit(US_FLIDX_TIMED_OUT, &us->dflags);
}
/* finished working on this command */
us->srb = NULL;
scsi_unlock(host);
/* unlock the device pointers */
mutex_unlock(&us->dev_mutex);
} /* for (;;) */
/* Wait until we are told to stop */
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
break;
schedule();
}
__set_current_state(TASK_RUNNING);
return 0;
}
对于上面这个控制线程,首先该函数执行了一个for(;;),这是一个死循环,也就是这个函数作为一些线程用可以不停息的运行。同时,根据刚开始wait_for_completion_interruptible代码,我们知道开始就进入睡眠状态了,只有唤醒us->cmnd_ready这个控制线程才能继续执行下去,那我们要知道什么时候释放这把锁来唤醒下面的程序呢?
其实有两个地方,一个是模块卸载的时候,另一个就是有SCSI命令发过来。每一次应用层发过来SCSI命令了,比如你去读写/dev/sda,最终SCSI核心层就会调用与该主机对应的queuecommand函数,这个函数是scsi_host_template结构体成员,在probe中scsi_host_alloc时候注册的。下面是queuecommand函数的实现。
static int queuecommand(struct scsi_cmnd *srb,void (*done)(struct scsi_cmnd *))
{
struct us_data *us = host_to_us(srb->device->host);
US_DEBUGP("%s called\n", __func__);
if (us->srb != NULL) {
printk(KERN_ERR USB_STORAGE "Error in %s: us->srb = %p\n",__func__, us->srb);
return SCSI_MLQUEUE_HOST_BUSY;
}
if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
US_DEBUGP("Fail command during disconnect\n");
srb->result = DID_NO_CONNECT << 16;
done(srb);
return 0;
}
srb->scsi_done = done;
us->srb = srb;
complete(&us->cmnd_ready); //释放锁,唤醒控制线程
return 0;
}
好了,用户层有了SCSI命令,就会执行我们驱动中这个控制线程。这个死循环首先会做一些判断,然后一直进行数据通信。那么这个死循环也是会退出的,什么时候呢?当执行这个死循环的最后一个if语句,会进行超时处理,如果超时会将us->srb=NULL,而我们在这个控制线程的死循环中发现,当获取us->cmnd_ready锁后,第一个执行的代码就是判断us->srb是否为NULL,如果us->srb=NULL就会执行break语句,从而跳出第一个死循环。接下来进入第二个死循环,这个死循环首先判断是否真的该结束了,如果真的结束了,那么就break,彻底退出这个控制线程,如果不是应该彻底结束,那进行schedule重新调度控制子线程。
到目前为止,我们的控制线程就已经分析完了,不过我们发现,这个控制线程是在usb_stor_acquire_resources中定义的,在usb_stor_acquire_resources之后,我们还创建了usb_stor_scan_thread线程,这是一个扫描线程。
/* Thread to carry out delayed SCSI-device scanning */
static int usb_stor_scan_thread(void * __us)
{
struct us_data *us = (struct us_data *)__us;
struct device *dev = &us->pusb_intf->dev;
dev_dbg(dev, "device found\n");
set_freezable();//设备在一定时间内没有响应,会挂起
/* Wait for the timeout to expire or for a disconnect */
if (delay_use > 0) { // delay_use秒后如果U盘没拔出则继续执行,否则执行disconnect
dev_dbg(dev, "waiting for device to settle "
"before scanning\n");
wait_event_freezable_timeout(us->delay_wait,
test_bit(US_FLIDX_DONT_SCAN, &us->dflags),
delay_use * HZ);
}
/* If the device is still connected, perform the scanning */
if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) {
/* For bulk-only devices, determine the max LUN value */
if (us->protocol == USB_PR_BULK &&
!(us->fflags & US_FL_SINGLE_LUN)) {
mutex_lock(&us->dev_mutex);
us->max_lun = usb_stor_Bulk_max_lun(us);//询问设备支持多少个LUN
mutex_unlock(&us->dev_mutex);
}
scsi_scan_host(us_to_host(us));
dev_dbg(dev, "scan complete\n");
/* Should we unbind if no devices were detected? */
}
usb_autopm_put_interface(us->pusb_intf);
complete_and_exit(&us->scanning_done, 0);//本进程结束,唤醒disconnect中的进程
}
对于上面这个扫描线程,里面的usb_stor_Bulk_max_lun函数完成了主机控制器与设备之间的第一次通信。USB驱动程序首先发送一个命令,然后设备根据命令返回一些信息,这里显示的是一个表示LUN个数的数字,usb_stor_Bulk_max_lun完成的是一次控制传输。
/* Determine what the maximum LUN supported is */
int usb_stor_Bulk_max_lun(struct us_data *us)
{
int result;
/* issue the command */
us->iobuf[0] = 0; //默认只有0个LUN
result = usb_stor_control_msg(us, us->recv_ctrl_pipe,
US_BULK_GET_MAX_LUN,
USB_DIR_IN | USB_TYPE_CLASS |
USB_RECIP_INTERFACE,
0, us->ifnum, us->iobuf, 1, 10*HZ);//向设备发送一个命令
US_DEBUGP("GetMaxLUN command result is %d, data is %d\n",
result, us->iobuf[0]);
/* if we have a successful request, return the result */
if (result > 0)
return us->iobuf[0];
/*
* Some devices don't like GetMaxLUN. They may STALL the control
* pipe, they may return a zero-length result, they may do nothing at
* all and timeout, or they may fail in even more bizarrely creative
* ways. In these cases the best approach is to use the default
* value: only one LUN.
*/
return 0;
}
我们看看里面usb_stor_control_msg的实现
/*
* Transfer one control message, with timeouts, and allowing early
* termination. Return codes are usual -Exxx, *not* USB_STOR_XFER_xxx.
*/
int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
u8 request, u8 requesttype, u16 value, u16 index,
void *data, u16 size, int timeout)
{
int status;
US_DEBUGP("%s: rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n",
__func__, request, requesttype,
value, index, size);
/* fill in the devrequest structure */
us->cr->bRequestType = requesttype; //初始化us->cr
us->cr->bRequest = request;
us->cr->wValue = cpu_to_le16(value);
us->cr->wIndex = cpu_to_le16(index);
us->cr->wLength = cpu_to_le16(size);
/* fill and submit the URB */
usb_fill_control_urb(us->current_urb, us->pusb_dev, pipe,
(unsigned char*) us->cr, data, size,
usb_stor_blocking_completion, NULL);//填充控制urb
status = usb_stor_msg_common(us, timeout);//继续填充控制urb并提交
/* return the actual length of the data transferred if no error */
if (status == 0)
status = us->current_urb->actual_length;
return status;
}
继续往下看usb_stor_msg_common的实现
/* This is the common part of the URB message submission code
*
* All URBs from the usb-storage driver involved in handling a queued scsi
* command _must_ pass through this function (or something like it) for the
* abort mechanisms to work properly.
*/
static int usb_stor_msg_common(struct us_data *us, int timeout)
{
struct completion urb_done;
long timeleft;
int status;
/* don't submit URBs during abort processing */
if (test_bit(US_FLIDX_ABORTING, &us->dflags))//设备处于放弃状态则结束
return -EIO;
/* set up data structures for the wakeup system */
init_completion(&urb_done); //初始化完成量
/* fill the common fields in the URB */
us->current_urb->context = &urb_done;
us->current_urb->transfer_flags = 0;
/* we assume that if transfer_buffer isn't us->iobuf then it
* hasn't been mapped for DMA. Yes, this is clunky, but it's
* easier than always having the caller tell us whether the
* transfer buffer has already been mapped. */
if (us->current_urb->transfer_buffer == us->iobuf)
us->current_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
us->current_urb->transfer_dma = us->iobuf_dma;
/* submit the URB */
status = usb_submit_urb(us->current_urb, GFP_NOIO);//提交控制urb
if (status) {
/* something went wrong */
return status;
}
/* since the URB has been submitted successfully, it's now okay
* to cancel it */
set_bit(US_FLIDX_URB_ACTIVE, &us->dflags);
/* did an abort occur during the submission? */
if (test_bit(US_FLIDX_ABORTING, &us->dflags)) {//当前还没取消urb时,取消urb请求
/* cancel the URB, if it hasn't been cancelled already */
if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) {
US_DEBUGP("-- cancelling URB\n");
usb_unlink_urb(us->current_urb);
}
}
/* wait for the completion of the URB */
//等待直到urb完成,如果1秒时间到进程没有被信号唤醒,则自动唤醒
timeleft = wait_for_completion_interruptible_timeout(
&urb_done, timeout ? : MAX_SCHEDULE_TIMEOUT);
clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags);
if (timeleft <= 0) {
US_DEBUGP("%s -- cancelling URB\n",
timeleft == 0 ? "Timeout" : "Signal");
usb_kill_urb(us->current_urb);
}
/* return the URB status */
return us->current_urb->status;
}
通过对上面这个usb_stor_msg_common函数的分析,我们现在已经把控制urb提交给USB内核了,当处理完,就会通知USB设备驱动,调用其回调函数,该回调函数在填充控制urb时已经说明,也就是usb_stor_blocking_completion函数
/* This is the completion handler which will wake us up when an URB
* completes.
*/
static void usb_stor_blocking_completion(struct urb *urb)
{
struct completion *urb_done_ptr = urb->context;
complete(urb_done_ptr);
}
当一次通信完成,执行了回调函数后,就会释放锁,这样stor_msg_common函数中的wait_for_completion_interruptible_timeout处就会被唤醒,至此一次通信完毕。
最后需要补充说明一个问题,在上面提交控制urb时,flag标志使用的是GFP_NOIO。GFP_NOIO标志的意思是不能在申请内存的时候进行I/O操作,原因是usb_submit_urb()提交之后,会读取磁盘或者U盘中的数据,这种情况下,由于虚拟内存的原因,申请内存的函数还需要读取磁盘。所以不允许在usb_submit_urb()提交urb时进行I/O操作。
总结下,USB设备驱动主要围绕URB请求块,也就是控制URB、中断URB、批量URB、等时URB。USB骨骼框架是批量URB的例子。USB鼠标是一个中断URB和input子系统结合的例子。USB键盘是一个控制URB和中断URB结合input子系统的例子。USB的U盘是批量URB和控制URB结合的例子。不幸的是,等时URB没有填充函数,因此等时URB在被提交给USB核心之前,需要手动进行初始化。
U盘驱动测试:
本Mini2440开发板具有两种USB 接口,一个是USB Host,它和普通PC的USB接口是一样的,可以接USB 摄像头、USB 键盘、USB 鼠标、优盘等常见的USB外设,另外一种是USB Slave,我们一般使用它来下载程序到目标板。对于U盘的测试,要配置内核后才能进行测试。
实验环境:内核linux2.6.32.2,arm-linux-gcc交叉编译器,mini2440开发板。
内核配置:(1)因为优盘用到了 SCSI 命令,所以我们先增加SCSI 支持。在 Device Drivers 菜单里面,选择SCSI device support。(2)选择 USB support,按回车进入USB support 菜单,找到并选中USB Mass Storage support。(3)另外,现在的优盘等移动存储器使用的大都是FAT/FAT32 格式的,因此我们还需要添加FAT32 文件系统的支持,在内核配置主菜单下进入FAT32 文件系统配置子菜单,为了支持中英文的编码,在File systems菜单下选择Native language support 。
接上面的步骤,在内核源代码根目录下执行:make zImage,把生成的新内核烧写到开发板中,先不要插入优盘(这样做是为了看插入时的打印信息),等系统启动后,进入命令行控制台,此时优盘,可以看到其厂家ID和产品ID,以及存储设备uba1信息。
(1) 执行cat /proc/partitions查看磁盘分区信息
(2) 挂载U盘,在mnt目录下建立usb目录
执行mkdir /mnt/usb ;mount /dev/uba1 /mnt/usb/
(3) 查看U盘信息 ls /mnt/usb –l
(4) 查看挂载后的分区信息 df –h