libusb阻塞

摘要:
收到响应后,bulk_transfer_cb回调被设置为完成,这样联系人被阻止,函数返回。bulktransferlibusb_submit_的分段处理。传输最终转移到submit_bulk_传输在handle_bulk_完成时,如果url_如果idx是最后一个url,则表示所有urb都已收集。timeout processing if libusb_bulk_如果传输传入的超时为0,则没有超时,libusb将始终等待数据。问题的根本原因是超时。在do_sync_bulk_In传输时,由于所有urb都未完成,因此bulk_transfer_Cb不会被调用,将被阻塞。libusb_handle_Events将继续查询fd,超时2秒,但由于url已被取消,设备将返回-2。
这两天发现手头一个usb指纹头出现了一点状况,若libusb以同步方式发送bulk transfer出现阻塞。经过测试发现跟timeout有些关系:若timeout为0(无timeout),不会阻塞;若timeout为1000或者2000,则会。另外,若采用异步方式传送bulk transfer,则不会阻塞。
同步方式
libusb_bulk_transfer(devh, ep_bulk, buf, CAM_BUF_SZ, &len, timeout);
进入libusb研究,发现libusb是采用异步方式来实现的。在do_sync_bulk_transfer中:
staticintdo_sync_bulk_transfer(structlibusb_device_handle *dev_handle,
unsignedcharendpoint,unsignedchar*buffer,intlength,
int*transferred,unsignedinttimeout,unsignedchartype)
{
libusb_fill_bulk_transfer(transfer,dev_handle,endpoint,buffer,length,
bulk_transfer_cb,&completed,timeout);
transfer->type =type;
r =libusb_submit_transfer(transfer);
if(r <0){
libusb_free_transfer(transfer);
returnr;
}
while(!completed){
r =libusb_handle_events(HANDLE_CTX(dev_handle));
}
}
这里libusb_fill_bulk_transfer来填充bulk transfer,然后libusb_submit_transfer提交bulk transfer,最后用libusb_handle_events来等待完成。当收到回应后,bulk_transfer_cb回调设置completed,从而阻塞被接触,函数返回。
分段处理bulk transfer
libusb_submit_transfer最终调到了submit_bulk_transfer。该函数会检查buffer大小,如果大于MAX_BULK_BUFFER_LENGTH(16K),则分成若干16K大小的urb,每个urb指向用户buffer的适当位置,然后用ioctl(IOCTL_USBFS_SUBMITURB)提交每个urb。
收到数据时会判断是否收齐所有urb。在handle_bulk_completion中,若urb_idx为最后一个urb,则认为收齐了所有的urb。最终会调用usbi_handle_transfer_completion来调用urb callback。
timeout处理
若libusb_bulk_transfer传入的timeout为0,则没有timeout,libusb会一直等待数据。在libusb_handle_events中设置了一个2s的poll timeout,libusb会在while中一直poll,每次poll的timeout为2s。
若设置了timeout,libusb_submit_transfer会按照timeout升序将transfer插入到libusb_context的flying_transfers列表中,然后提交transfer(当然会分段了,如上所述)
libusb_handle_events会根据自己设置的2s timeout和flying_transfers中的timeout得出实际timeout,然后用poll查询usb fd。
问题根源
libusb阻塞的原因就是超时。有时usb指纹头返回数据较慢,在指定的timeout时间内没有完成所有urb请求,进入超时处理。handle_timeout()会cancel掉为完成的urb(IOCTL_USBFS_DISCARDURB)。在do_sync_bulk_transfer中,由于未完成所有urb,bulk_transfer_cb没有被调用,从而会阻塞。libusb_handle_events会继续以2s超时来查询fd,但由于urb已经取消,设备会返回-2(ENOENT)。
按道理讲,即使超时,libusb也应该在超时后返回。libusb为什么没有返回呢?handle_bulk_completion函数中,若读到ENOENT,awaiting_discard会递减至0。与awaiting_discard一起的还有awaiting_reap,若二者均为0,也会调用bulk_transfer_cb通知用户。
awaiting_discard在超时时cancel urb时被设置。若ioctl(IOCTL_USBFS_DISCARDURB)返回EINVAL,则awaiting_reap会递增。
经过打印验证,发现在cancel urb时,对于已经完成的urb,会返回EINVAL。而未完成的urb,则返回0。我想这大概是一个bug,正确做法应该是不要取消已经完成的urb。
附:
阻塞时libusb打印

found usbfs at /dev/bus/usb
found usb devices insysfs
add fd 9events 1
created defaultcontext
scan usb1
sysfs descriptors available
bus=1dev=1
busnum 1devaddr 1session_id 257
allocating newdevice for1/1(session 257)
scan 1-1
bus=1dev=2
busnum 1devaddr 2session_id 258
allocating newdevice for1/2(session 258)
scan 1-1.1
bus=1dev=3
busnum 1devaddr 3session_id 259
allocating newdevice for1/3(session 259)
open 1.3
add fd 11events 4
destroy device 1.1
destroy device 1.2
configuration 1
interface0
nexttimeout in2.499878s
poll()2fds withtimeout in2000ms
poll()returned 1
urb type=2status=0transferred=0
handling completion status 0
actual_length=0control transfer的回调)
nexttimeout in2.499909s
poll()2fds withtimeout in2000ms
poll()returned 1
urb type=2status=0transferred=0
handling completion status 0
actual_length=0(另一个control transfer的回调)
need 10urbs fornewtransfer withlength 153600
nexttimeout in0.997836s(从这里开始查询bulk
poll()2fds withtimeout in998ms
poll()returned 1
urb type=3status=0transferred=16384
handling completion status 0of bulk urb 1/10
nexttimeout in0.379086s
poll()2fds withtimeout in380ms
poll()returned 1
urb type=3status=0transferred=16384
handling completion status 0of bulk urb 2/10
nexttimeout in0.253080s
poll()2fds withtimeout in254ms
poll()returned 0
all URBshave already been processed fortimeouts (超时)
poll()2fds withtimeout in2000ms
poll()returned 1
urb type=3status=-2transferred=9728
handling completion status -2of bulk urb 3/10
CANCEL:detected a cancelled URB
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
poll()returned 1
urb type=3status=-2transferred=0
handling completion status -2of bulk urb 4/10
CANCEL:detected a cancelled URB
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
poll()returned 1
urb type=3status=-2transferred=0
handling completion status -2of bulk urb 5/10
CANCEL:detected a cancelled URB
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
poll()returned 1
urb type=3status=-2transferred=0
handling completion status -2of bulk urb 6/10
CANCEL:detected a cancelled URB
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
poll()returned 1
urb type=3status=-2transferred=0
handling completion status -2of bulk urb 7/10
CANCEL:detected a cancelled URB
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
poll()returned 1
urb type=3status=-2transferred=0
handling completion status -2of bulk urb 8/10
CANCEL:detected a cancelled URB
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
poll()returned 1
urb type=3status=-2transferred=0
handling completion status -2of bulk urb 9/10
CANCEL:detected a cancelled URB
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
poll()returned 1
urb type=3status=-2transferred=0
handling completion status -2of bulk urb 10/10
CANCEL:detected a cancelled URB
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
poll()returned 0
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
poll()returned 0
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
poll()returned 0
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
poll()returned 0
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
poll()returned 0
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
poll()returned 0
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
poll()returned 0
all URBshave already been processed fortimeouts
poll()2fds withtimeout in2000ms
^C

kernel usbmon的打印:

e8c3a240 1032172219S Co:1:003:0s 00090001000000000
e8c3a240 1032173347C Co:1:003:000
e8c3a240 1032174262S Co:1:003:0s 40e0 0000000000000
e8c3a240 1032174597C Co:1:003:000
e8c3a240 1032175054S Co:1:003:0s 02010000008100000
e8c3a240 1032249749C Co:1:003:000
e8c3a240 1032250298S Co:1:003:0s 40e5 0000000000000
e8c3a240 1032250634C Co:1:003:000
e8c3a240 1032251121S Bi:1:003:1-11516384<
e8c3a440 1032251365S Bi:1:003:1-11516384<
e8c3a1c0 1032252890S Bi:1:003:1-11516384<
e8c3a2c0 1032254018S Bi:1:003:1-11516384<
e8c3a740 1032254475S Bi:1:003:1-11516384<
e8c3a7c0 1032254871S Bi:1:003:1-11516384<
e8c3a5c0 1032255268S Bi:1:003:1-11516384<
e8c3a0c0 1032255695S Bi:1:003:1-11516384<
e8c3a640 1032256091S Bi:1:003:1-11516384<
e849ce20 1032256518S Bi:1:003:1-1156144<
e8c3a240 1032561640C Bi:1:003:1016384=40e5000000000000a59fa89e b3a3a09c a899989b 8b8a9ba09f9392a0929f8498
e8c3a440 1033162310C Bi:1:003:1016384=909e8a928d99898d908f94909ba197a18ba5a69d9396959d948d9e9e968c9584
e8c3a1c0 1033653347C Bi:1:003:1016384=d1d9d0d8 d3cdd2d7 d1d8c5d6 c5cfd1d3 d0ccd0d4 d0cbd1d1 bed2d2d0 d9dad4d9
e8c3a2c0 1034152250C Bi:1:003:1016384=a79fa0aa ada2bab2 b6b3a5ad aeae9bb0 b2aa9eaa 9396a8a6afa5af95 9daaa5ba
e8c3a740 1034753624C Bi:1:003:1-216320=d6d0d5d2 ccd4c7cc cdd6cfc9 d3d2d2ce cfcfd5c8 d0c4cdc7 cdc9c6ca d4d0cec2
e8c3a2c0 1034753776S Co:1:002:0s 23089031000100000
e8c3a2c0 1034754111C Co:1:002:000
e8c3a7c0 1034755026C Bi:1:003:1-264=a3a9bda3 a5a4a9a5 93a69e98949d9294aa9d8d9d 9b91a6a4a793a097 9b8f8f96
e8c3a2c0 1034755117S Co:1:002:0s 23089031000100000
e8c3a2c0 1034755483C Co:1:002:000
e8c3a5c0 1034756215C Bi:1:003:1-264=d1c7d2d5 dad6d6db ded1d0da d8d2d4da d3d8d8d6 dbd4dedd d2d5ded5 dbd6d7d8
e8c3a2c0 1034756459S Co:1:002:0s 23089031000100000
e8c3a2c0 1034756855C Co:1:002:000
e8c3a0c0 1034757587C Bi:1:003:1-264=b04a5e59 637c82707f8483787f7e7881838f8497988a8389938c8f928b8ca28c
e8c3a2c0 1034757648S Co:1:002:0s 23089031000100000
e8c3a2c0 1034757983C Co:1:002:000
e8c3a640 1034758715C Bi:1:003:1-20
e8c3a2c0 1034758776S Co:1:002:0s 23089031000100000
e8c3a2c0 1034759111C Co:1:002:000
e849ce20 1034759569C Bi:1:003:1-20
e8c3a2c0 1034759630S Co:1:002:0s 23089031000100000
e8c3a2c0 1034759995C Co:1:002:000

免责声明:文章转载自《libusb阻塞》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇postgresql 导入 导出(一张表)使用golang理解mysql的两阶段提交下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

深入理解linux网络技术内幕读书笔记(十)--帧的接收

Table of Contents 1 概述1.1 帧接收的中断处理 2 设备的开启与关闭 3 队列 4 通知内核帧已接收:NAPI和netif_rx 4.1 NAPI简介4.1.1 NAPI优点 4.2 NAPI所用之net_device字段 4.3 net_rx_action软中断处理函数和NAPI 4.4 新旧驱动程序接口 概...

源码分析Kafka 消息拉取流程

本节重点讨论 Kafka 的消息拉起流程。 @ 目录 1、KafkaConsumer poll 详解 1.1 KafkaConsumer updateAssignmentMetadataIfNeeded 详解 1.1.1 ConsumerCoordinator#poll 1.1.2 updateFetchPositions 详解 1.2 消息拉...

WebSocket原理及与http1.0/1.1 long poll跟 ajax轮询的区别【转自知乎】

今天学习了几个以前没有见过的东东,作者的文章写的还是很通熟易懂的!! 起码我基本都看懂了(2333)————正文 今天要学习的是WebSocket原理与http1.0/1.1 long poll 和 ajax轮询的区别 WebSocket是HTML5出的东西,也就是说HTTP谢意没有变化,或者说没有关系。首先HTTP有1.1和1.0直说,也就是所谓的kee...

how to learn device driver

making a linux usb driver http://www.kroah.com/linux/ http://matthias.vallentin.net/blog/2007/04/writing-a-linux-kernel-driver-for-an-unknown-usb-device/ http://www.linuxjournal.c...

TCP建立连接时socket的epoll态及一个可能的状态不一致问题

零、原因 其实本来是在看TCP三次握手时客户端和服务器端socket对于epoll状态何时返回何种状态,不过后来引出了一个另有意思的问题:就是客户端和服务器双方对于三次握手的状态出现了不一致。我们知道,在三次握手中,客户端在发送最后一个ack之后进入ESTABLISHED状态,并没有要求服务器对于这个ACK再次ACK(当然也没有办法要求ACK,否则这样就是...

关于Web服务器的认识

       马上就要毕业了,也要开始找工作了,大学写了这么多代码了,却没有好好总结一下常用的概念很是遗憾额,就通过这篇博客记录一下我最常用的一些知识好了。        说到Web服务器,有很多文章都介绍的很好,之前看到一篇非常不错的,对我帮助很大,可惜现在找不到原文了,看到博客园有人转载,我就在这里也记一下好了,在此非常感谢作者的分析,受益匪浅。   ...