一,USB基本概念
USB协议是非常复杂的,好在Linux内核为我们做了太多的工作,使得USB驱动的开发相对容易很多。
USB基于树形的拓扑结构设计,支持多种传输模式和传输速率。硬件上,USB分为USB控制器和USB设备,USB控制器负责同USB设备通信和协议协商,以便USB控制器能很好的控制和管理挂接在其下的USB设备。USB控制器逻辑复杂,不在本篇分析,本篇主要分析USB设备驱动架构。
1,USB控制器
USB控制器通常是USB模块的核心,通过根集线器连接各外接USB设备。USB控制器类型分为EHCI, UHCI和OHCI等,支持各种不同的usb总线协议和总线速率标准。各种USB控制器类型在kernel中都有实现,usb控制器驱动在加载过程中,会枚举/轮询下挂在该usb控制器下的所有usb设备,并提供操作下挂usb设备的接口。
2,USB设备
USB设备分为hub和功能部件。一个hub可以下挂多个设备或者hub,从而形成一个树状的拓扑结构。
3,USB传输模式
USB控制器支持4种传输模式:批量传输模式(bulk),控制传输模式(control),中断传输模式(interrupt),等时传输模式(iso),每种模式的适用场景不尽相同。
批量传输模式:USB控制器使用批量传输模式用于数据量大,非实时数据传输。常用于U盘,打印机等块设备数据传输。
控制传输模式:USB控制器使用控制传输模式同设备交换设备描述符、ID、Product等信息,并发送各种控制命令。这是USB控制器必须支持的传输模式,每个usb控制器都有一个0号端点,工作在控制传输模式,用来传输控制信息,传输数据量小。
中断传输模式:USB控制器定时查看是否有中断数据传输请求,传输数据量小。常用于鼠标,键盘,游戏控制器等。
等时传输模式:USB控制器使用等时传输模式来定时传输数据量大,实时性高的数据。常用于音频,mic等设备。
二,USB设备驱动框架
以Linux内核的usb skel驱动为例,分析usb设备驱动框架。首先看一下skel驱动的框图:
USB驱动框架遵循Linux设备驱动模型框架(usb_bus, usb_device 和 usb_driver),skel_driver使用module_usb_driver(skel_driver);注册进usb总线,调用usb总线的match方法usb_device_match(),如果匹配完成,调用usb_probe_interface(),最终调用到skel_driver的probe方法skel_probe()。这个过程太常见,就不再分析。
我们要分析的是skel_driver的驱动框架,从而掌握通用usb驱动的编写。我们知道,usb控制器驱动提供了设备的枚举和通信的通道,要实现一个基于USB的设备驱动,我们需要完成两个方面的事情:
(1)如何通过usb找到特定的数据传输端点,以便跟实际硬件设备通信?
(2)如何向上层提供接口,以便用户可以访问该usb设备?
解决上述两点,我们就完成了一个usb设备驱动的功能。
1,USB子系统驱动初始化:
我们先从用户接口说起。USB设备在初始化的过程中,通过在/dev下创建字符设备节点,给用户提供操作usb设备的接口:
int usb_major_init(void)
{
int error;
error = register_chrdev(USB_MAJOR, "usb", &usb_fops); // 注册/dev/usb*设备驱动
if (error)
printk(KERN_ERR "Unable to get major %d for usb devices\n",
USB_MAJOR);
return error;
}
#define MAX_USB_MINORS 256 // 最大支持256个usb子设备
static const struct file_operations *usb_minors[MAX_USB_MINORS]; //
static const struct file_operations usb_fops = {
.owner = THIS_MODULE,
.open = usb_open,
.llseek = noop_llseek,
};
static int usb_open(struct inode *inode, struct file *file)
{
int err = -ENODEV;
const struct file_operations *new_fops;
down_read(&minor_rwsem);
new_fops = fops_get(usb_minors[iminor(inode)]); // 根据从设备号,获取从设备的文件操作
if (!new_fops)
goto done;
replace_fops(file, new_fops); // 替换文件操作
/* Curiouser and curiouser... NULL ->open() as "no device" ? */
if (file->f_op->open)
err = file->f_op->open(inode, file); // 调用实际从设备的open()方法
done:
up_read(&minor_rwsem);
return err;
}
从上面可以看出,usb skel 驱动预先定义了 usb_minors[] 数组用于存储usb从设备的文件操作符。针对usb驱动,注册文件操作是 usb_fops。当用户通过/dev/usb*设备节点打开usb字符设备时,通过def_chr_fops调用到usb_fops->open()方法,即usb_open()方法,该方法从usb_minors[]数组中获取相应的文件操作,最终完成特定设备的打开操作。
那么,usb_minors[]是何时初始化的?是usb skel驱动注册时初始化的。
2,USB skel驱动注册
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); // 分配 usb_skel 实例
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)); // 指向 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) { // 轮询当前altsetting下的所有端点
endpoint = &iface_desc->endpoint[i].desc;
if (!dev->bulk_in_endpointAddr &&
usb_endpoint_is_bulk_in(endpoint)) { // 找到bulk in 端点
/* 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); // 分配urb缓存
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;
} // 找到 bulk out 端点
}
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); //interface->dev.driver_data指向dev
/* we can register the device now, as it is ready */
retval = usb_register_dev(interface, &skel_class); // 注册该usb接口设备,关联相关的 fops
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;
}
int usb_register_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
{
int retval;
int minor_base = class_driver->minor_base;
int minor;
char name[20];
char *temp;
#ifdef CONFIG_USB_DYNAMIC_MINORS
/*
* We don't care what the device tries to start at, we want to start
* at zero to pack the devices into the smallest available space with
* no holes in the minor range.
*/
minor_base = 0;
#endif
if (class_driver->fops == NULL)
return -EINVAL;
if (intf->minor >= 0)
return -EADDRINUSE;
retval = init_usb_class();
if (retval)
return retval;
dev_dbg(&intf->dev, "looking for a minor, starting at %d\n", minor_base);
down_write(&minor_rwsem);
for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {
if (usb_minors[minor])
continue;
usb_minors[minor] = class_driver->fops;
intf->minor = minor;
break;
} // 从 usb_minors[] 数组寻找空位,分配一个从设备号并注册相应的文件操作符
up_write(&minor_rwsem);
if (intf->minor < 0)
return -EXFULL;
/* create a usb class device for this usb interface */
snprintf(name, sizeof(name), class_driver->name, minor - minor_base);
temp = strrchr(name, '/');
if (temp && (temp[1] != '\0'))
++temp;
else
temp = name;
intf->usb_dev = device_create(usb_class->class, &intf->dev,
MKDEV(USB_MAJOR, minor), class_driver,
"%s", temp); // 在设备模型中注册该设备
if (IS_ERR(intf->usb_dev)) {
down_write(&minor_rwsem);
usb_minors[minor] = NULL;
intf->minor = -1;
up_write(&minor_rwsem);
retval = PTR_ERR(intf->usb_dev);
}
return retval;
}
从上面的注册流程可以看出,usb skel首先从usb interface中找到相应的端点,并分配数据接收缓存,然后从usb_minors[]数组中分配一个从设备号,并注册相应的设备文件操作。最后将该设备注册进设备模型。
这里需要注意一点,设备注册到设备模型时(device_create),会通过devtmpfs_create_node(dev) 在 /dev下注册相应的设备节点,这样用户就可以通过操作 /dev/usb* 设备节点访问usb设备了。
到此为止,usb skel 驱动框架成型:usb_skel驱动注册时,匹配usb interface,探测interface的过程中,找到bulk in/ bulk out 端点信息,分配数据交换缓存,注册fops到usb_minors[]数组,设备注册进Linux设备模型,并创建 /dev/usb*设备节点。当用户通过设备节点 /dev/usb*打开usb设备时,查找usb_minors[]数组,找到相应的fops,替换到file结构中,最终通过注册的fops操作usb设备。
3,USB skel驱动操作分析
USB skel驱动注册的操作结构如下:
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,
};
(1)skel_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); // 根据minor查找interface
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); // 获取 usb_skel
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初始化为 usb_skel
exit:
return retval;
}
(2)skel_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; // 获取 usb_skel
/* 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) { // 设备busy
/* 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); // 缓存中没有可读的数据,调度usb控制器从设备读取数据到缓存
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); // 数据没读完,调度usb控制器读取实际数据
} else {
/* no data in the buffer */
rv = skel_do_read_io(dev, count);
if (rv < 0)
goto exit;
else if (!(file->f_flags & O_NONBLOCK))
goto retry;
rv = -EAGAIN;
} // 缓存中没有数据,调度usb控制器读取实际数据
exit:
mutex_unlock(&dev->io_mutex);
return rv;
}
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);
/* 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); // 提交urb读请求,调用usb控制器异步读取数据
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;
}
(3)skel_write()
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; // 获取usb_skel
/* 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); // 分配一致性DMS缓存
if (!buf) {
retval = -ENOMEM;
goto error;
}
if (copy_from_user(buf, user_buffer, writesize)) {
retval = -EFAULT;
goto error;
} // 用户态数据拷贝到dms缓存中
/* 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); // 填充 bulk out 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请求, 调用usb控制器异步写数据
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;
}