文章目录
- [1. 前言](#1. 前言)
- [2. Linux USB 设备驱动](#2. Linux USB 设备驱动)
-
- [2.1 USB 设备驱动 的 注册](#2.1 USB 设备驱动 的 注册)
- [2.2 USB 设备驱动 的 加载](#2.2 USB 设备驱动 的 加载)
- [2.3 USB 设备驱动 的 数据收发](#2.3 USB 设备驱动 的 数据收发)
-
- [2.3.1 将 URB 请求添加到 EP](#2.3.1 将 URB 请求添加到 EP)
- [2.3.2 处理 EP 上的 URB 请求](#2.3.2 处理 EP 上的 URB 请求)
1. 前言
限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
2. Linux USB 设备驱动
这里讨论的 Linux USB 设备驱动,是指 USB 从设的主机侧驱动。即下图中左侧最顶端的 USB 设备驱动:
bash
USB 主机(如带 Linux 系统的设备) USB Slave 设备
---------------------------------------- ------------------------------
| USB 设备驱动 | | |
| ^ | | |
| | | | |
| v | | |
| USB Core | | |
| ^ | | |
| | | | 如U盘、USB鼠标键盘 驱动固件 |
| v | | |
| USB主机控制器驱动(OHCI/UHCI/EHCI/xHCI) | | |
| ^ | | |
| | | | |
| v | | |
| USB主机控制器 | | |
---------------------------------------- ------------------------------
^ ^
| |
| USB 总线 |
------------------------------------------------
如上图中右侧的 USB 键盘从设,其自身内部的驱动我们通常称之为固件,而在 Linux Host 一侧,也有一个对应 USB 鼠标的驱动,本文称之为 USB 设备驱动。当然,Host 不一定是 Linux,还可以是 Windows 等其它操作系统,但本文是讨论 Linux Host 侧的 USB 设备驱动。
2.1 USB 设备驱动 的 注册
注意,Linux 下的 USB 设备驱动驱动的是 USB interface,而不是整个 USB device。Linux 将 USB interface 的驱动抽象 为 struct usb_driver:
c
/* include/linux/usb.h */
struct usbdrv_wrap {
struct device_driver driver; /* USB interface 驱动,在 driver core 体现为一个 device_driver */
int for_devices; /* 0: USB interface 驱动;1: USB device 驱动 */
};
struct usb_driver {
const char *name;
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id);
void (*disconnect) (struct usb_interface *intf);
int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
void *buf);
int (*suspend) (struct usb_interface *intf, pm_message_t message);
int (*resume) (struct usb_interface *intf);
int (*reset_resume)(struct usb_interface *intf);
int (*pre_reset)(struct usb_interface *intf);
int (*post_reset)(struct usb_interface *intf);
const struct usb_device_id *id_table;
...
struct usbdrv_wrap drvwrap;
...
};
struct usb_driver 既可以作为 USB device 驱动,也可以作为 USB interface 驱动,主要依靠 struct usb_driver::drvwrap.for_devices 来标识:0 表示 USB interface 驱动;1 表示 USB device 驱动。
其它的成员,主要看下面两个:
usb_driver::probe
USB interface 驱动入口usb_driver::id_table
驱动支持的 USB interface 设备PID,VID
其它的 usb_driver 成员在此不做说明,读者可自行分析。
可以看到,struct usb_driver 中定义的驱动回调,传入的第一个参数都是 struct usb_interface * 类型,这暗示着,struct usb_driver 驱动的对象是 USB interface,而不是整个 USB device。如果一个 USB device 包含多个不同功能的 interface,则需要在 Linux Host 侧为每种类型的 USB interface 定义一个驱动。
Linux Host 侧的 USB interace 抽象为 struct usb_interface,其定义如下:
c
/* include/linux/usb.h */
struct usb_interface {
/* array of alternate settings for this interface,
* stored in no particular order */
struct usb_host_interface *altsetting;
struct usb_host_interface *cur_altsetting; /* the currently
* active alternate setting */
unsigned num_altsetting; /* number of alternate settings */
/* If there is an interface association descriptor then it will list
* the associated interfaces */
struct usb_interface_assoc_descriptor *intf_assoc;
int minor; /* minor number this interface is
* bound to */
enum usb_interface_condition condition; /* state of binding */
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 resetting_device:1; /* true: bandwidth alloc after reset */
unsigned authorized:1; /* used for interface authorization */
/* USB interace 在 driver core 里对应一个 device */
struct device dev; /* interface specific device info */
struct device *usb_dev;
atomic_t pm_usage_cnt; /* usage counter for autosuspend */
struct work_struct reset_ws; /* for resets in atomic context */
};
以 RealTek 的 RTL8150 USB 网卡为例,来说明 USB 驱动的注册过程:
c
/* drivers/net/usb/rtl8150.c */
static struct usb_driver rtl8150_driver = {
.name = driver_name,
.probe = rtl8150_probe,
.disconnect = rtl8150_disconnect,
.id_table = rtl8150_table,
.suspend = rtl8150_suspend,
.resume = rtl8150_resume,
.disable_hub_initiated_lpm = 1,
};
module_usb_driver(rtl8150_driver);
module_usb_driver(rtl8150_driver); 注册 USB 驱动到系统,看下细节:
c
#define usb_register(driver) \
usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
#define module_usb_driver(__usb_driver) \
module_driver(__usb_driver, usb_register, \
usb_deregister)
通过接口 usb_register_driver() 注册 USB interface 驱动:
c
/* drivers/usb/core/driver.c */
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
const char *mod_name)
{
int retval = 0;
if (usb_disabled())
return -ENODEV;
new_driver->drvwrap.for_devices = 0; /* 0 标记为 interface 级别驱动 */
new_driver->drvwrap.driver.name = new_driver->name;
new_driver->drvwrap.driver.bus = &usb_bus_type;
new_driver->drvwrap.driver.probe = usb_probe_interface;
new_driver->drvwrap.driver.remove = usb_unbind_interface;
new_driver->drvwrap.driver.owner = owner;
new_driver->drvwrap.driver.mod_name = mod_name;
spin_lock_init(&new_driver->dynids.lock);
INIT_LIST_HEAD(&new_driver->dynids.list);
/* 注册 USB 驱动到 driver core */
retval = driver_register(&new_driver->drvwrap.driver);
if (retval)
goto out;
retval = usb_create_newid_files(new_driver);
if (retval)
goto out_newid;
pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name);
out:
return retval;
out_newid:
driver_unregister(&new_driver->drvwrap.driver);
printk(KERN_ERR "%s: error %d registering interface "
" driver %s\n",
usbcore_name, retval, new_driver->name);
goto out;
}
EXPORT_SYMBOL_GPL(usb_register_driver);
最终,通过 driver_register() 将 USB 设备驱动注册到 driver core。
2.2 USB 设备驱动 的 加载
在 USB 设备插入到 HUB port 时,产生中断信号,触发 HUB 中断处理:
c
/* drivers/usb/core/hub.c */
/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb)
{
hub->nerrors = 0;
/* Something happened, let hub_wq figure it out */
kick_hub_wq(hub); /* 调度 work: 触发 hub_event() 进一步处理设备插入事件 */
...
}
hub_event()
port_event()
hub_port_connect_change()
hub_port_connect()
/* 创建 usb 设备对象 */
udev = usb_alloc_dev(hdev, hdev->bus, port1);
...
/* 复位设备, 为设备分配地址, 获取设备描述符(usb_device_descriptor) */
status = hub_port_init(hub, udev, port1, i);
...
/* 匹配设备到驱动,然后加载设备驱动 */
status = usb_new_device(udev);
...
announce_device(udev); /* usb usb2: New USB device found, idVendor=1d6b, idProduct=0002 */
...
err = device_add(&udev->dev); /* 添加设备到 driver core,将触发 USB 设备和驱动配对 */
bus_add_device(dev)
bus_probe_device(dev)
...
bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver)
driver_match_device(drv, dev) /* 配对 USB 设备 和 驱动 */
usb_device_match() /* 所有的设备,第一轮先匹配到 USB 设备级别的驱动 */
driver_probe_device(drv, dev) /* 加载 USB USB 设备级别通用驱动 usb_generic_driver */
...
usb_probe_device()
generic_probe()
是不是有点出乎意料?这里的驱动匹配流程,为新插入设备创建对象 usb_device 并读取设备描述符后,并没有进入到 RTL8150 USB 网卡 的入口 rtl8150_probe(),而是进入 Linux USB 子系统通用 USB 设备驱动 usb_generic_driver 的入口 generic_probe()。不着急,看看 generic_probe() 都做了什么工作:
c
/* drivers/usb/core/generic.c */
static int generic_probe(struct usb_device *udev)
{
int err, c;
...
if (udev->authorized == 0)
...
else {
c = usb_choose_configuration(udev);
if (c >= 0) {
err = usb_set_configuration(udev, c);
...
}
}
...
return 0;
}
c
/* drivers/usb/core/message.c */
int usb_set_configuration(struct usb_device *dev, int configuration)
{
...
/* Allocate memory for new interfaces before doing anything else,
* so that if we run out then nothing will have changed. */
n = nintf = 0;
if (cp) {
/* 为 USB 设备 的 所有 interface 创建对象 */
nintf = cp->desc.bNumInterfaces;
new_interfaces = kmalloc(nintf * sizeof(*new_interfaces), GFP_NOIO);
...
for (; n < nintf; ++n) {
new_interfaces[n] = kzalloc(sizeof(struct usb_interface), GFP_NOIO);
...
}
...
}
...
/* 初始化 USB 设备的 所有 interface 设备 */
for (i = 0; i < nintf; ++i) {
struct usb_interface_cache *intfc;
struct usb_interface *intf;
struct usb_host_interface *alt;
cp->interface[i] = intf = new_interfaces[i];
...
alt = usb_altnum_to_altsetting(intf, 0);
...
intf->cur_altsetting = alt;
usb_enable_interface(dev, intf, true);
intf->dev.parent = &dev->dev;
intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type;
intf->dev.type = &usb_if_device_type;
...
device_initialize(&intf->dev);
...
}
kfree(new_interfaces);
/* 发送 SET_CONFIGURATION 请求 */
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
NULL, 0, USB_CTRL_SET_TIMEOUT);
...
dev->actconfig = cp;
...
usb_set_device_state(dev, USB_STATE_CONFIGURED);
...
for (i = 0; i < nintf; ++i) {
struct usb_interface *intf = cp->interface[i];
...
device_enable_async_suspend(&intf->dev);
/*
* 添加 USB interface 设备到 driver core ,
* 触发 USB interface 设备驱动匹配流程 。
*/
ret = device_add(&intf->dev);
...
create_intf_ep_devs(intf);
}
usb_autosuspend_device(dev);
return 0;
}
可以看到,generic_probe() 首先调用 usb_choose_configuration() 选择配置描述符,然后调用 usb_set_configuration() 向 USB 从设(如 USB 键盘)发送 SET_CONFIGURATION 请求,然后初始化配置描述符包含的所有 interface,并为 interface 创建设备,然后调用 device_add() 将新建设备添加到 driver core,从而触发 USB interface 驱动加载:
c
device_add(&udev->dev)
...
driver_match_device(drv, dev) /* 配对 USB 设备 和 驱动 */
usb_device_match() /* 不同于第一次,这次匹配设备到 interface 驱动 */
driver_probe_device(drv, dev) /* 加载 USB 设备 interface 驱动 rtl8150_driver,rtl8150_probe() */
...
rtl8150_probe()
USB interface 驱动匹配过程,可以总结为:首先匹配到通用 USB 设备驱动 usb_generic_driver,并通过该驱动入口 generic_probe() 选择配置描述符,并添加配置描述符包含的 interface 设备,从而触发 interface 驱动的加载。
2.3 USB 设备驱动 的 数据收发
USB 设备的通信,通过 struct urb 来进行,先看下其定义(只看几个主要核心成员):
c
/* include/linux/usb.h */
struct urb {
...
struct list_head urb_list; /* list head for use by the urb's
* current owner */
...
struct usb_device *dev; /* (in) pointer to associated device */
struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */
unsigned int pipe; /* (in) pipe information */
...
void *transfer_buffer; /* (in) associated data buffer */
...
void *context; /* (in) context for completion (struct usb_hub *, ...) */
usb_complete_t complete; /* (in) completion routine (hub_irq(), ...) */
...
};
- dev
关联的 USB 设备 - ep
数据通信 USB endpoint,通过 pipe 来确定 - complete
USB 数据处理回调,在 HUB 中断中被触发
2.3.1 将 URB 请求添加到 EP
以 RealTek 的 RTL8150 USB 网卡为例,来说明 USB 数据的处理流程(这里仅看数据接收流程)。首先注册 URB 请求到 USB 子系统:
c
static int rtl8150_open(struct net_device *netdev)
{
...
/* 注册用于 urb 收取网络包, 回调 read_bulk_callback() 在 HCD 的中断 usb_hcd_irq() 中被触发 */
usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
if ((res = usb_submit_urb(dev->rx_urb, GFP_KERNEL))) {
if (res == -ENODEV)
netif_device_detach(dev->netdev);
dev_warn(&netdev->dev, "rx_urb submit failed: %d\n", res);
return res;
}
/* 注册用于 urb 处理连接状态、错误状态等, 回调 intr_callback() 在 HCD 的中断 usb_hcd_irq() 中被触发 */
usb_fill_int_urb(dev->intr_urb, dev->udev, usb_rcvintpipe(dev->udev, 3),
dev->intr_buff, INTBUFSIZE, intr_callback,
dev, dev->intr_interval);
if ((res = usb_submit_urb(dev->intr_urb, GFP_KERNEL))) {
if (res == -ENODEV)
netif_device_detach(dev->netdev);
dev_warn(&netdev->dev, "intr_urb submit failed: %d\n", res);
usb_kill_urb(dev->rx_urb);
return res;
}
...
}
辅助函数 usb_fill_int_urb() 初始化 urb,然后通过 usb_submit_urb() 将 urb 请求提交到 USB 子系统:
c
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
int xfertype, max;
struct usb_device *dev;
struct usb_host_endpoint *ep;
int is_out;
unsigned int allowed;
...
dev = urb->dev;
...
ep = usb_pipe_endpoint(dev, urb->pipe); /* 通过 urb->pipe 信息,确认通信 EP */
...
urb->ep = ep; /* 设定 urb 关联的 EP */
...
xfertype = usb_endpoint_type(&ep->desc); /* 确定传输数据类型: CONTROL, ISOC, BULK, INT */
/* 确定传输方向 */
if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
struct usb_ctrlrequest *setup =
(struct usb_ctrlrequest *) urb->setup_packet;
if (!setup)
return -ENOEXEC;
is_out = !(setup->bRequestType & USB_DIR_IN) ||
!setup->wLength;
} else {
is_out = usb_endpoint_dir_out(&ep->desc);
}
...
return usb_hcd_submit_urb(urb, mem_flags); /* 提交 URB 给主机控制器设备(HCD: Host Controller Device) */
}
int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
int status;
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus); /* urb 关联的 HCD */
...
if (is_root_hub(urb->dev)) { /* ROOT HUB */
status = rh_urb_enqueue(hcd, urb); /* urb 入队 */
} else { /* 非 ROOT HUB */
status = map_urb_for_dma(hcd, urb, mem_flags);
if (likely(status == 0)) {
/* ehci_urb_enqueue(), ... */
status = hcd->driver->urb_enqueue(hcd, urb, mem_flags); /* urb 入队 */
...
}
}
...
}
上面流程来到了 ROOT HUB 和非 ROOT HUB 的 urb 请求入队,本文只看 非 ROOT HUB urb 请求入队这种典型的情形(假定我们设备插在 EHCI HCD 控制器上):
c
static int ehci_urb_enqueue (
struct usb_hcd *hcd,
struct urb *urb,
gfp_t mem_flags
) {
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
struct list_head qtd_list;
INIT_LIST_HEAD (&qtd_list);
switch (usb_pipetype (urb->pipe)) {
case PIPE_CONTROL:
/* qh_completions() code doesn't handle all the fault cases
* in multi-TD control transfers. Even 1KB is rare anyway.
*/
if (urb->transfer_buffer_length > (16 * 1024))
return -EMSGSIZE;
/* FALLTHROUGH */
/* case PIPE_BULK: */
default:
if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
return -ENOMEM;
return submit_async(ehci, urb, &qtd_list, mem_flags);
case PIPE_INTERRUPT:
if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
return -ENOMEM;
return intr_submit(ehci, urb, &qtd_list, mem_flags);
case PIPE_ISOCHRONOUS:
if (urb->dev->speed == USB_SPEED_HIGH)
return itd_submit (ehci, urb, mem_flags);
else
return sitd_submit (ehci, urb, mem_flags);
}
}
RTL8150 驱动分别在 endpoint 1 申请了 PIPE_BULK 类型的 urb 请求,在 endpoint 3 上申请了 PIPE_INTERRUPT 类型的 urb 请求,这里看一下 PIPE_BULK 类型的 urb 请求入队的过程:
c
/* drivers/usb/host/ehci-q.c */
static int
submit_async (
struct ehci_hcd *ehci,
struct urb *urb,
struct list_head *qtd_list,
gfp_t mem_flags
) {
...
spin_lock_irqsave (&ehci->lock, flags);
...
rc = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb);
...
done:
spin_unlock_irqrestore (&ehci->lock, flags);
...
}
int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)
{
...
if (HCD_RH_RUNNING(hcd)) {
urb->unlinked = 0;
list_add_tail(&urb->urb_list, &urb->ep->urb_list); /* 添加到 EP 的 URB 请求列表 */
} else {
...
}
...
}
好的,我们已经将 URB 请求添加到了对应的 Endpoint,接下来看怎么处理这些 URB 请求。
2.3.2 处理 EP 上的 URB 请求
在 USB 收到数据时,触发 HUB 中断,在中断中处理这些添加到 EP 上的 URB 请求(还是假定使用 EHCI USB 控制器通信):
c
usb_hcd_irq()
ehci_irq()
ehci_work()
scan_isoc()
...
ehci_urb_done()
usb_hcd_giveback_urb(ehci_to_hcd(ehci), urb, status);
...
if (!hcd_giveback_urb_in_bh(hcd) && !is_root_hub(urb->dev)) {
__usb_hcd_giveback_urb(urb);
return;
}
...
list_add_tail(&urb->urb_list, &bh->head);
...
/* 调度 tasklet 处理 URB 请求: usb_giveback_urb_bh() */
if (running)
;
else if (high_prio_bh)
tasklet_hi_schedule(&bh->bh);
else
tasklet_schedule(&bh->bh);
// 在 tasklet 中处理 URB 请求
static void usb_giveback_urb_bh(unsigned long param)
{
struct giveback_urb_bh *bh = (struct giveback_urb_bh *)param;
struct list_head local_list;
spin_lock_irq(&bh->lock);
bh->running = true;
restart:
list_replace_init(&bh->head, &local_list);
spin_unlock_irq(&bh->lock);
while (!list_empty(&local_list)) {
struct urb *urb;
urb = list_entry(local_list.next, struct urb, urb_list);
list_del_init(&urb->urb_list);
bh->completing_ep = urb->ep;
__usb_hcd_giveback_urb(urb);
bh->completing_ep = NULL;
}
/* check if there are new URBs to giveback */
spin_lock_irq(&bh->lock);
if (!list_empty(&bh->head))
goto restart;
bh->running = false;
spin_unlock_irq(&bh->lock);
}
__usb_hcd_giveback_urb()
urb->complete(urb)
read_bulk_callback() // rtl8150.c
进入 RTL8150 网卡收包流程:
c
static void read_bulk_callback(struct urb *urb)
{
rtl8150_t *dev;
unsigned pkt_len, res;
struct sk_buff *skb;
struct net_device *netdev;
u16 rx_stat;
int status = urb->status;
int result;
dev = urb->context;
...
res = urb->actual_length;
rx_stat = le16_to_cpu(*(__le16 *)(urb->transfer_buffer + res - 4)); /* skb 包来自 urb->transfer_buffer */
pkt_len = res - 4;
skb_put(dev->rx_skb, pkt_len);
dev->rx_skb->protocol = eth_type_trans(dev->rx_skb, netdev);
netif_rx(dev->rx_skb); /* 收取网络包,进入网络协议栈 */
netdev->stats.rx_packets++;
netdev->stats.rx_bytes += pkt_len;
spin_lock(&dev->rx_pool_lock);
skb = pull_skb(dev);
spin_unlock(&dev->rx_pool_lock);
if (!skb)
goto resched;
dev->rx_skb = skb;
...
}