记一次libusb库函数“libusb_bulk_transfer”的调用关系追踪
一次使用libusb库函数“libusb_bulk_transfer”对外部USB设备进行读取数据的时候,出现了无数据返回的情况,但是使用USB分析仪,看到USB总线是进行了一次完整的IN令牌的transfer,如下图,感觉像是中间某一个节点没有将收到的device数据包返回上来,所以对代码进行下追踪,定位下问题大致位置。
这里记录下追踪到的函数调用关系:
libusb库内部调用关系
先看下大致调用流程:
> libusb_bulk_transfer(sync.c)
----> do_sync_bulk_transfer(sync.c)
--------> libusb_submit_transfer(io.c)
------------> usbi_backend->submit_transfer(core.c)
------------> linux_usbfs_backend.submit_transfer(linux_usbfs.c)
------------> op_submit_transfer(linux_usbfs.c)
----------------> submit_bulk_transfer(linux_usbfs.c)
--------------------> ioctl
可以看到,最终实际是调用到了系统的“ioctl”函数。
简单分析一下:
- do_sync_bulk_transfer(sync.c)中有两个重要的函数,如下:
static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle,
unsigned char endpoint, unsigned char *buffer, int length,
int *transferred, unsigned int timeout, unsigned char type)
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
int completed = 0;
int r;
if (!transfer)
return LIBUSB_ERROR_NO_MEM;
//这个函数用来填充一个用于bulk传输的struct libusb_transfer
libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length,
bulk_transfer_cb, &completed, timeout);
transfer->type = type;
//这里提交上面填充好的transfer
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
return r;
}
......
libusb_free_transfer(transfer);
return r;
}
- libusb_submit_transfer(io.c)会调用不同平台的usbi_backend->submit_transfer,如下:
int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer)
{
struct libusb_context *ctx = TRANSFER_CTX(transfer);
struct usbi_transfer *itransfer =
LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
int r;
int first;
......
first = add_to_flying_list(itransfer);
//根据编译平台的不同,调用对应平台的 struct usbi_os_backend 成员 submit_transfer
r = usbi_backend->submit_transfer(itransfer);
if (r) {
usbi_mutex_lock(&ctx->flying_transfers_lock);
list_del(&itransfer->list);
usbi_mutex_unlock(&ctx->flying_transfers_lock);
}
......
out:
usbi_mutex_unlock(&itransfer->lock);
return r;
}
不同平台的区分在core.c文件,如下:
#if defined(OS_LINUX)
const struct usbi_os_backend * const usbi_backend = &linux_usbfs_backend;
#elif defined(OS_DARWIN)
const struct usbi_os_backend * const usbi_backend = &darwin_backend;
#elif defined(OS_OPENBSD)
const struct usbi_os_backend * const usbi_backend = &openbsd_backend;
#elif defined(OS_WINDOWS)
const struct usbi_os_backend * const usbi_backend = &windows_backend;
#else
#error "Unsupported OS"
#endif
我们使用的是linux,那就是用这个结构体“linux_usbfs_backend”,在文件linux_usbfs.c,如下:
const struct usbi_os_backend linux_usbfs_backend = {
.name = "Linux usbfs",
.init = op_init,
.exit = NULL,
......
.open = op_open,
.close = op_close,
......
.submit_transfer = op_submit_transfer, //这个就是被调用到的函数
.cancel_transfer = op_cancel_transfer,
.clear_transfer_priv = op_clear_transfer_priv,
......
};
- op_submit_transfer(linux_usbfs.c)比较简单,就是根据不同的transfer类型,调用不同的函数,这里我们是“bulk”类型,调用的是 submit_bulk_transfer(linux_usbfs.c),如下:
/*
** 这个函数主要做了3件事情:
** 1.根据 transfer 创建并初始化 urb
** 2.通过 ioctl 提交 urb
** 3.判断 urb 提交结果
*/
static int submit_bulk_transfer(struct usbi_transfer *itransfer,
unsigned char urb_type)
{
struct libusb_transfer *transfer =
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
struct linux_device_handle_priv *dpriv =
_device_handle_priv(transfer->dev_handle);
struct usbfs_urb *urbs;
int is_out = (transfer->endpoint & LIBUSB_ENDPOINT_DIR_MASK)
== LIBUSB_ENDPOINT_OUT;
int r;
int i;
size_t alloc_size;
if (tpriv->urbs)
return LIBUSB_ERROR_BUSY;
if (is_out && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET &&
!supports_flag_zero_packet)
return LIBUSB_ERROR_NOT_SUPPORTED;
/* usbfs places a 16kb limit on bulk URBs. we divide up larger requests
* into smaller units to meet such restriction, then fire off all the
* units at once. it would be simpler if we just fired one unit at a time,
* but there is a big performance gain through doing it this way. */
int num_urbs = transfer->length / MAX_BULK_BUFFER_LENGTH;
int last_urb_partial = 0;
if (transfer->length == 0) {
num_urbs = 1;
} else if ((transfer->length % MAX_BULK_BUFFER_LENGTH) > 0) {
last_urb_partial = 1;
num_urbs++;
}
usbi_dbg("need %d urbs for new transfer with length %d", num_urbs,
transfer->length);
alloc_size = num_urbs * sizeof(struct usbfs_urb);
urbs = malloc(alloc_size); //创建urb
if (!urbs)
return LIBUSB_ERROR_NO_MEM;
memset(urbs, 0, alloc_size);
tpriv->urbs = urbs;
tpriv->num_urbs = num_urbs;
tpriv->num_retired = 0;
tpriv->reap_action = NORMAL;
tpriv->reap_status = LIBUSB_TRANSFER_COMPLETED;
for (i = 0; i < num_urbs; i++) {
struct usbfs_urb *urb = &urbs[i];
urb->usercontext = itransfer; //初始化urb
urb->type = urb_type;
urb->endpoint = transfer->endpoint;
urb->buffer = transfer->buffer + (i * MAX_BULK_BUFFER_LENGTH);
if (supports_flag_bulk_continuation && !is_out)
urb->flags = USBFS_URB_SHORT_NOT_OK;
if (i == num_urbs - 1 && last_urb_partial)
urb->buffer_length = transfer->length % MAX_BULK_BUFFER_LENGTH;
else if (transfer->length == 0)
urb->buffer_length = 0;
else
urb->buffer_length = MAX_BULK_BUFFER_LENGTH;
if (i > 0 && supports_flag_bulk_continuation)
urb->flags |= USBFS_URB_BULK_CONTINUATION;
/* we have already checked that the flag is supported */
if (is_out && i == num_urbs - 1 &&
transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET)
urb->flags |= USBFS_URB_ZERO_PACKET;
r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb); //提交urb
if (r < 0) { //判断urb提交是否成功
if (errno == ENODEV) {
r = LIBUSB_ERROR_NO_DEVICE;
} else {
usbi_err(TRANSFER_CTX(transfer),
"submiturb failed error %d errno=%d", r, errno);
r = LIBUSB_ERROR_IO;
}
......
return 0;
}
}
return 0;
}
到这里,应用发起的的一次transfer,就通过USB设备文件节点的ioctl系统调用,进入了kernel层。
USB设备文件节点
当device设备插入之后,host端主机会创建一个“USB设备文件节点”,提供给应用层使用,这个节点位置如下图:
可以看到,这些节点的主设备号为189,创建这些节点的源码在:kernel/drivers/usb/core/devio.c,如下:
#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0)
int __init usb_devio_init(void)
{
int retval;
retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,
"usb_device");
if (retval) {
printk(KERN_ERR "Unable to register minors for usb_device\n");
goto out;
}
cdev_init(&usb_device_cdev, &usbdev_file_operations);
retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
if (retval) {
printk(KERN_ERR "Unable to get usb_device major %d\n",
USB_DEVICE_MAJOR);
goto error_cdev;
}
usb_register_notify(&usbdev_nb);
out:
return retval;
error_cdev:
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
goto out;
}
这里面的宏 USB_DEVICE_MAJOR 的值就是189,另外文件节点对应的驱动 struct file_operations 就是 usbdev_file_operations,如下:
const struct file_operations usbdev_file_operations = {
.owner = THIS_MODULE,
.llseek = usbdev_lseek,
.read = usbdev_read,
.poll = usbdev_poll,
.unlocked_ioctl = usbdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = usbdev_compat_ioctl,
#endif
.open = usbdev_open,
.release = usbdev_release,
};
所以,上一节 libusb 最后调用的 ioctl ,会直接进到驱动的“usbdev_ioctl”,然后进一步调用“usbdev_do_ioctl”,在这个函数里,会使用 libusb 里面调用时候传入的参数“IOCTL_USBFS_SUBMITURB”来做判断,在kernel里面相同的定义是“USBDEVFS_SUBMITURB”,然后再进一步调用“proc_submiturb”,将用户空间的urb,传递给内核,代码如下:
static int proc_submiturb(struct dev_state *ps, void __user *arg)
{
struct usbdevfs_urb uurb;
if (copy_from_user(&uurb, arg, sizeof(uurb)))
return -EFAULT;
return proc_do_submiturb(ps, &uurb,
(((struct usbdevfs_urb __user *)arg)->iso_frame_desc),
arg);
}
接下来调用“proc_do_submiturb”,这个函数主要就是对用户空间给过来的urb进行分析,然后组织成 usb core 需要的urb,再调用“usb_submit_urb”,将urb传递给 usb core,如下代码:
static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
struct usbdevfs_iso_packet_desc __user *iso_frame_desc,
void __user *arg)
{
......
if (usb_endpoint_xfer_bulk(&ep->desc)) {
spin_lock_irq(&ps->lock);
......
/* Don't accept continuation URBs if the endpoint is
* disabled because of an earlier error.
*/
if (ps->disabled_bulk_eps & (1 << as->bulk_addr))
ret = -EREMOTEIO;
else
ret = usb_submit_urb(as->urb, GFP_ATOMIC);
spin_unlock_irq(&ps->lock);
} else {
ret = usb_submit_urb(as->urb, GFP_KERNEL);
}
......
}
usb驱动层调用关系
“usb_submit_urb”是USB驱动里面一个非常重要的函数,所有的urb,最终都是要通过它提交到usb core来处理,源码在:kernel/drivers/usb/core/urb.c
“urb”是“USB request block”的缩写,与USB设备通讯要用的所有数据,都由urb来传递。
usb core
这里的调用关系比较简单,如下:
> usb_submit_urb(urb.c)
----> usb_hcd_submit_urb(hcd.c)
--------> hcd->driver->urb_enqueue
最后调用主机控制器驱动“struct usb_hcd”结构体的,“struct hc_driver”类型driver成员的,urb_enqueue函数指针所对应的函数。
这里“struct usb_hcd”描述了一个USB主机控制器驱动信息,就是 Host Controller Driver(HCD);“struct hc_driver”描述了这个主机控制器驱动的硬件操作相关的信息;而 urb_enqueue 就是控制器硬件IO请求的一个函数指针。
HCD
每个主机控制器都需要创建个HCD,不同的硬件平台,注册不同的HCD,通过使用函数“usb_create_hcd”,例如:
struct usb_hcd *hcd;
hcd = usb_create_hcd(&musb_hc_driver, dev, dev_name(dev));
if (!hcd)
return NULL;
这里的“musb_hc_driver”就是一个“struct hc_driver”类型结构体,创建“struct usb_hcd”的时候,会将这个“musb_hc_driver”结构体变量的指针,给到“struct usb_hcd”的成员“driver”。
这个“musb_hc_driver”定义如下,这里都是硬件平台和厂商对应的东西了:
const struct hc_driver musb_hc_driver = {
.description = "musb-hcd",
.product_desc = "MUSB HDRC host driver",
.hcd_priv_size = sizeof(struct musb),
.flags = HCD_USB2 | HCD_MEMORY,
/* not using irq handler or reset hooks from usbcore, since
* those must be shared with peripheral code for OTG configs
*/
.start = musb_h_start,
.stop = musb_h_stop,
.get_frame_number = musb_h_get_frame_number,
.urb_enqueue = musb_urb_enqueue,
.urb_dequeue = musb_urb_dequeue,
.endpoint_disable = musb_h_disable,
.hub_status_data = musb_hub_status_data,
.hub_control = musb_hub_control,
.bus_suspend = musb_bus_suspend,
.bus_resume = musb_bus_resume,
/* .start_port_reset = NULL, */
/* .hub_irq_enable = NULL, */
};
可以看到这个结构体的成员“urb_enqueue”,就是函数“musb_urb_enqueue”的指针。
所以,最终的调用,就是函数“musb_urb_enqueue”,这里面会去操作主机控制器硬件,在USB总线上收发数据,来和device设备通讯,然后将收到的数据,通过urb里面的buffer,再返回给设备驱动或应用!
最终,我一开始的问题,追踪之后,发现“musb_urb_enqueue”返回的时候,urb的buffer是空的,推测是厂商控制硬件获取数据可能出错了,导致没有拿到设备返回的数据!