WDK 7600.16385.1中关于USB ISO传输的驱动例子, 是针对于USB2.0与USB1.1的, 即HS与FS.

这个例子没有非常特别之处, 但对于USB2.0的ISO传输的数据分割算法, 还是挺有意思的.

根据MSDN参考文章:

http://msdn.microsoft.com/en-us/library/windows/hardware/hh406225.aspx

<<How to transfer data to USB isochronous endpoints>>

USB2.0 ISO传输需要根据iso ep的interval值的不同而变化,
第一, 如下表
Interval Polling period (2Interval-1)
1 1; Data is transferred every bus interval.
2 2; Data is transferred every second bus interval.
3 4; Data is transferred every fourth bus interval.
4 8; Data is transferred every eighth bus interval.

对于FULL SPEED, polling period一直为一个FRAME一次.


第二, 如下表, 对于不同的polling period, 传输包的个数也有限制:
Polling period Number of Packets for high speed/SuperSpeed
1 Multiple of 8
2 Multiple of 4
3 Multiple of 2
4 Any

第三, 对于一个URB, iso packet的限制

full speed: 255
high/super speed: 1024
于是, 就是催生的WDK 7600.16385.1中USB2.0 ISO传输的一个算法:
不过在看这个算法前, 提及几个概念:
1. bus interval
 Full speed: 1ms, frame, High/Super speed: 125us, microframe
2. Windows中USB packet的概念
packet表示在一个bus interval中传输数据的量.
它的决定因素, 是usb设备端的ep描述符决定的.
对于full speed, 每个bus interval(frame)传输一个packet, 该packet对应于一个iso transaction
high speed, 每个bus interval(micro frame)最多可以传输 3*1024 的packet
super speed, 这个能力的表达由ep描述符与ep companion描述符来描述: 最多可以达到3*16*1024/microframe
如何得到某个ep packet size:
MaximumPacketSize 成员存在于两个数据结构中:USBD_PIPE_INFORMATION与WDF_USB_PIPE_INFORMATION.


MaximumPacketSize的来源:
对于FULL SPEEC EP来讲, MaximumPacketSize来自于EP描述符的wMaxPacketSize低11位
HIGH SPEED: EP描述符的wMaxPacketSize的第11,12位表示,"附加"的transaction的个数, 可以为0, 1, 2
SUPER SPEED: USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR中成员起了作用:
Mult: 表示附加的busrt的个数, 可以为0,1,2
bMaxBurst : burst的数量, 这是针对于USB3.0的新概念, 可以为0-15 (表示1-16个burst)
wBytesPerInterval : 必须小于或等于(bMaxBurst+1) * (Mult+1) *wMaxPacketSize
  • wBytesPerInterval indicates the total number of bytes that the host can send or receive in a bus interval. Even though the maximum number of bytes per bus interval can be calculated as (bMaxBurst+1) * (Mult+1) *wMaxPacketSize, the USB 3.0 specification recommends using the wBytesPerInterval value instead.
  • ThewBytesPerInterval value must be less than or equal to that calculated value.
事实上, SUPER SPEED的MaximumPacketSize就是wBytesPerInterval.
由于WINDOWS USB STACK已经帮上层USB客户驱动设置好了MaximumPacketSize, 所以, 它会提出:

Important

For a client driver the values described in the preceding is for information only. 
The driver must always use the MaximumPacketSize value of the endpoint descriptor to determine the layout of the transfer buffer.
此处所谓的layout of the transfer buffer, 我的理解, 就是如何将buffer拆分成一个一个的iso packet.
文章中出现: 请注意: iso packet是可以小于MaximumPacketSize的, 但事实上, 如果iso packet小于MaximumPacketSize, 就会出现后面列出的问题.
The MaximumPacketSize value indicates the maximum permitted size of the isochronous packet. The client driver can set the size of each isochronous packet to any value less than the MaximumPacketSize value.
现在来看看这个算法:
首先, Windows Driver Kit (WDK) 8.1 Samples 已经将USBSamp例子中的这个算法给去掉了, 新的例子代码, 只要发现不符合Number of Packets for high speed/SuperSpeed的要求, 就退出了.

所以, 从某种意义上来讲, 微软最新的代码, 并非比早期版本的代码更"好".
1.  得到包的大小
packetSize = pipeInfo.MaximumPacketSize;

2. 未圆整前包的个数
    numberOfPackets = (TotalLength + packetSize - 1) / packetSize;

3. 将包的个数, 向上圆整到符合Number of Packets for high speed/SuperSpeed的要求, 这里以interval 1, 8 packets/bus interval为例


    if(0 == (numberOfPackets % 8)) {

actualPackets = numberOfPackets;
    }
    else {

//
        // we need multiple of 8 packets only.
        //
        actualPackets = numberOfPackets +
                        (8 - (numberOfPackets % 8));
    }

4. 得到实际平均每个包中的数据量
    minDataInEachPacket = TotalLength / actualPackets;

5. 如果实际平均每个包的数据量 == packetSize, 说明实际包的个数, 并没有因为第3步, 而有所增加

所以, numberOfPacketsFilledToBrim变量(表示包中数据量等于packetsize的包的个数), 就等于实际包的数量, 即所有包都是满包.
dataLeftToBeDistributed数据也为0, 因为正好所有的包都是满包.

    if(minDataInEachPacket == packetSize) {

numberOfPacketsFilledToBrim = actualPackets;
        dataLeftToBeDistributed     = 0;


    }
    else {

6. 由于第三步, 实际包的个数比第二步算出来的个数, 有所增加, 所以, minDataInEachPacket小于packetSize.

numberOfPacketsFilledToBrim还是满包的个数
dataLeftToBeDistributed表示, 不能达到满包的那个包, 比minDataInEachPacket多出的数据量

dataLeftToBeDistributed = TotalLength -
                              (minDataInEachPacket * actualPackets);

numberOfPacketsFilledToBrim = dataLeftToBeDistributed /
                                  (packetSize - minDataInEachPacket);

dataLeftToBeDistributed -= (numberOfPacketsFilledToBrim *
                                (packetSize - minDataInEachPacket));

}

之后, 在计算每一个stage中的数据量的时候: (每一个irp/urb pair 小于1024个iso packet):
if(nPackets > numberOfPacketsFilledToBrim) {

1. 包的个数nPackets中, 满包的有numberOfPacketsFilledToBrim个, 而且, 有一个包, 是minDataInEachPacket+dataLeftToBeDistributed, 剩余包的数据量为minDataInEachPacket


            stageSize =  packetSize * numberOfPacketsFilledToBrim;
            stageSize += (minDataInEachPacket *
                          (nPackets - numberOfPacketsFilledToBrim));
            stageSize += dataLeftToBeDistributed;
        }
        else {

2. 所有包均为满包:
            stageSize = packetSize * nPackets;
        }


实际的例子:
Algorithm at play:

TotalLength  = 8193
    packetSize   = 8
    Step 1

Step 2
    numberOfPackets = (8193 + 8 - 1) / 8 = 1025

Step 3
    actualPackets = 1025 + 7 = 1032

Step 4
    minDataInEachPacket = 8193 / 1032 = 7 bytes

Step 5
    dataLeftToBeDistributed = 8193 - (7 * 1032) = 969.

Step 6
    numberOfPacketsFilledToBrim = 969 / (8 - 7) = 969.

Step 7
    dataLeftToBeDistributed = 969 - (969 * 1) = 0.

最后, 说一下这个算法的问题:

实际运行过程中, 会导致 babble error, 所以, 最后, 笔者将USBSamp的算法改成另外一种.

比如, 如果一个ISO IN EP的MAX PACKET SIZE = 768, 且其extra transaction 是2, 刚其MaximumPacketSize为768*3 = 2304.

为了符合interval =1, 包的个数为8的倍数的要求, 刚以18432为读写请求:

结果如下:

1. request 18432, actual read 18432

8 packets, each 2304 bytes, 每个ISO PAKCET的OFFSET也均为2304的整数倍

2. request 18431, actual read 17664

最后一个transaction的767 bytes没有收到, 最后一个PACKET收到1536 bytes

7 packets 2304, one packet 2303,每个ISO PAKCET的OFFSET也均为2304的整数倍

最后一个iso packet USBD_STATUS = USBD_STATUS_BABBLE_DETECTED 0xC0000012

这里的BABBLE ERROR, 应该就是buffer只有767 bytes, 但DEVICE回了768 bytes所导致的.


IsoPacket[0].offset = 0 IsoPacket[0].Length = 2304 IsoPacket[0].Status = 0
UsbSamp: 
IsoPacket[1].offset = 2304 IsoPacket[1].Length = 2304 IsoPacket[1].Status = 0
UsbSamp: 
IsoPacket[2].offset = 4608 IsoPacket[2].Length = 2304 IsoPacket[2].Status = 0
UsbSamp: 
IsoPacket[3].offset = 6912 IsoPacket[3].Length = 2304 IsoPacket[3].Status = 0
UsbSamp: 
IsoPacket[4].offset = 9216 IsoPacket[4].Length = 2304 IsoPacket[4].Status = 0
UsbSamp: 
IsoPacket[5].offset = 11520 IsoPacket[5].Length = 2304 IsoPacket[5].Status = 0
UsbSamp: 
IsoPacket[6].offset = 13824 IsoPacket[6].Length = 2304 IsoPacket[6].Status = 0
UsbSamp: 
IsoPacket[7].offset = 16128 IsoPacket[7].Length = 1536 IsoPacket[7].Status = c0000012

3. request 18433, actual read 0

16个PACKETS

一个1153 BYTES, 15个1152 BYTES

OFFSET为:

urb header status C0000B00
UsbSamp: 
subReqContext
UsbSamp: 
IsoPacket[0].offset = 0 IsoPacket[0].Length = 768 IsoPacket[0].Status = c0000011
UsbSamp: 
IsoPacket[1].offset = 1153 IsoPacket[1].Length = 768 IsoPacket[1].Status = c0000011
UsbSamp: 
IsoPacket[2].offset = 2305 IsoPacket[2].Length = 768 IsoPacket[2].Status = c0000011
UsbSamp: 
IsoPacket[3].offset = 3457 IsoPacket[3].Length = 768 IsoPacket[3].Status = c0000011
UsbSamp: 
IsoPacket[4].offset = 4609 IsoPacket[4].Length = 768 IsoPacket[4].Status = c0000011
UsbSamp: 
IsoPacket[5].offset = 5761 IsoPacket[5].Length = 768 IsoPacket[5].Status = c0000011
UsbSamp: 
IsoPacket[6].offset = 6913 IsoPacket[6].Length = 768 IsoPacket[6].Status = c0000011
UsbSamp: 
IsoPacket[7].offset = 8065 IsoPacket[7].Length = 768 IsoPacket[7].Status = c0000011
UsbSamp: 
IsoPacket[8].offset = 9217 IsoPacket[8].Length = 768 IsoPacket[8].Status = c0000011
UsbSamp: 
IsoPacket[9].offset = 10369 IsoPacket[9].Length = 768 IsoPacket[9].Status = c0000011
UsbSamp: 
IsoPacket[10].offset = 11521 IsoPacket[10].Length = 768 IsoPacket[10].Status = c0000011
UsbSamp: 
IsoPacket[11].offset = 12673 IsoPacket[11].Length = 768 IsoPacket[11].Status = c0000011
UsbSamp: 
IsoPacket[12].offset = 13825 IsoPacket[12].Length = 768 IsoPacket[12].Status = c0000011
UsbSamp: 
IsoPacket[13].offset = 14977 IsoPacket[13].Length = 768 IsoPacket[13].Status = c0000011
UsbSamp: 
IsoPacket[14].offset = 16129 IsoPacket[14].Length = 768 IsoPacket[14].Status = c0000011
UsbSamp: 
IsoPacket[15].offset = 17281 IsoPacket[15].Length = 768 IsoPacket[15].Status = c0000011

其中c0000011为USBD_STATUS_XACT_ERRO, C0000B00为USBD_STATUS_ISOCH_REQUEST_FAILED

如果, 再改变一下APP的请求长度, 比如7*2304 = 16128

结果如下:

1. request 16128, read 0 bytes

8 packets, each 2016 bytes

urb header status C0000B00

IsoPacket[0].offset = 0 IsoPacket[0].Length = 1536 IsoPacket[0].Status = c0000012
UsbSamp: 
IsoPacket[1].offset = 2016 IsoPacket[1].Length = 1536 IsoPacket[1].Status = c0000012
UsbSamp: 
IsoPacket[2].offset = 4032 IsoPacket[2].Length = 1536 IsoPacket[2].Status = c0000012
UsbSamp: 
IsoPacket[3].offset = 6048 IsoPacket[3].Length = 1536 IsoPacket[3].Status = c0000012
UsbSamp: 
IsoPacket[4].offset = 8064 IsoPacket[4].Length = 1536 IsoPacket[4].Status = c0000012
UsbSamp: 
IsoPacket[5].offset = 10080 IsoPacket[5].Length = 1536 IsoPacket[5].Status = c0000012
UsbSamp: 
IsoPacket[6].offset = 12096 IsoPacket[6].Length = 1536 IsoPacket[6].Status = c0000012
UsbSamp: 
IsoPacket[7].offset = 14112 IsoPacket[7].Length = 1536 IsoPacket[7].Status = c0000012


2. request 16129, read 0 bytes

1 packet 2017, 7 packet 2016

urb header status C0000B00
UsbSamp: 
subReqContext
UsbSamp: 
IsoPacket[0].offset = 0 IsoPacket[0].Length = 1536 IsoPacket[0].Status = c0000012
UsbSamp: 
IsoPacket[1].offset = 2017 IsoPacket[1].Length = 1536 IsoPacket[1].Status = c0000012
UsbSamp: 
IsoPacket[2].offset = 4033 IsoPacket[2].Length = 1536 IsoPacket[2].Status = c0000012
UsbSamp: 
IsoPacket[3].offset = 6049 IsoPacket[3].Length = 1536 IsoPacket[3].Status = c0000012
UsbSamp: 
IsoPacket[4].offset = 8065 IsoPacket[4].Length = 1536 IsoPacket[4].Status = c0000012
UsbSamp: 
IsoPacket[5].offset = 10081 IsoPacket[5].Length = 1536 IsoPacket[5].Status = c0000012
UsbSamp: 
IsoPacket[6].offset = 12097 IsoPacket[6].Length = 1536 IsoPacket[6].Status = c0000012
UsbSamp: 
IsoPacket[7].offset = 14113 IsoPacket[7].Length = 1536 IsoPacket[7].Status = c0000012


3. 

request 16127, read 0 bytes

1 packet 2022, 7 packet 2015

urb header status C0000B00
UsbSamp: 
subReqContext
UsbSamp: 
IsoPacket[0].offset = 0 IsoPacket[0].Length = 1536 IsoPacket[0].Status = c0000012
UsbSamp: 
IsoPacket[1].offset = 2022 IsoPacket[1].Length = 1536 IsoPacket[1].Status = c0000012
UsbSamp: 
IsoPacket[2].offset = 4037 IsoPacket[2].Length = 1536 IsoPacket[2].Status = c0000012
UsbSamp: 
IsoPacket[3].offset = 6052 IsoPacket[3].Length = 1536 IsoPacket[3].Status = c0000012
UsbSamp: 
IsoPacket[4].offset = 8067 IsoPacket[4].Length = 1536 IsoPacket[4].Status = c0000012
UsbSamp: 
IsoPacket[5].offset = 10082 IsoPacket[5].Length = 1536 IsoPacket[5].Status = c0000012
UsbSamp: 
IsoPacket[6].offset = 12097 IsoPacket[6].Length = 1536 IsoPacket[6].Status = c0000012
UsbSamp: 
IsoPacket[7].offset = 14112 IsoPacket[7].Length = 1536 IsoPacket[7].Status = c0000012


综上实验所示, 只要一个iso packet的buffer size小于ep 的MaximumPacketSize, 则ISO就会产生BABBLE ERROR.

但该结论只适用于ISO IN, 对于ISO OUT, 该规则不适用.

到目前为止, 或许只需要将新的ISO IN OFFSET的LAYOUT办法给出来, 就可以解决该问题.
但在此之前, 我们可以利用USB ANALYZER( Lecory 的 Advisor T3), 将问题看得更加深入一点, 真正地了解, USBD_STATUS_BABBLE_DETECTED与USBD_STATUS_XACT_ERRO分别针对于哪一种现象:
从USB TRACE来看, 所有的USBD_STATUS_BABBLE_DETECTED, 都是ISO DATA2 768, DATA1 768, DATA0 768, 3 TRANSACTIONS的模式, 也非常容易理解这个BABBLE的情况. 如下图:
而USBD_STATUS_XACT_ERRO则不同: 它是规则地以 DATA2 768, DATA 1 768, 与DATA 2 768, DATA 1 0, 2 TRANSACTION进行交替的.

似乎, 从3 TRANSACTIONS的EP来讲, 如果数据达到了2个 TRANSACTIONS, 则为BABBLE, 如果只达到了一个,甚至更少, 则为XACT. 当然, 这只是笔者根据这几个情况得出的结论, 正确性有待考证.

最后, 笔者将该算法去除, 在符合polling period = 1, 2, 4, 8对packets(1, 2, 4, 8)的要求的情况下, 能够接受任意长度的ISO IN传输.

具体实现为:

所有的OFFSET设置为MaximumPacketSize的整数倍, 在SYS空间中申请一块NON PAGED POOL, 这块BUFFER的长度, 向上圆整到MaximumPacketSize的整数倍(如8*n, 4*n, 2*n, 1*n倍), ISO IN 的数据先存放在SYS空间的这块BUFFER中, 安排一个WORK ITEM, 在WORK ITEM中, 最后将数据COPY到用户空间的BUFFER中, COPY长度为用户请求的长度, 这样解决了BABBLE ERROR的问题(事实上, 也同时解决了XACT ERROR的问题).


第二十四篇:SuperSpeed/HighSpeed USB的ISO传输相关推荐

  1. 第二十四篇:可靠信号机制

    前言 曾经的 UNIX 系统中,信号的不可靠的.什么是不可靠?就是信号丢失呗.那什么是信号丢失?就是当系统正在处理某个事务的时候,如果收到了某个信号,但它不能及时处理这个信号,那么只能忽略掉此信号. ...

  2. Python之路【第二十四篇】Python算法排序一

    什么是算法 1.什么是算法 算法(algorithm):就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为输出.简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果. ...

  3. SpringBoot第二十四篇: springboot整合docker

    这篇文篇介绍,怎么为 springboot程序构建一个docker镜像.docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源.Docker 可以让开发者打包他们的 ...

  4. setitimer 创建两个定时器_JavaScript第二十四篇 高级定时器(下)

    数组分块 所谓数组分块,就是当你发现某个循环占用了大量时间,同时对于上述两个问题,你的回答都是"否",那么你就可以使用定时器分割这个循环. 思路是结合定时器进行递归调用定时器 基本 ...

  5. JavaScript第二十四篇 高级定时器(下)

    数组分块 所谓数组分块,就是当你发现某个循环占用了大量时间,同时对于上述两个问题,你的回答都是"否",那么你就可以使用定时器分割这个循环. 思路是结合定时器进行递归调用定时器 基本 ...

  6. flask第二十四篇——模板【6】自定义过滤器

    请关注孟船长的公众号:自动化测试实战 大家想了解其他过滤器可以参考这里: http://jinja.pocoo.org/docs/dev/templates/#builtin-filters ---- ...

  7. “约见”面试官系列之常见面试题第二十四篇之vue-router使用(建议收藏)

    开发的时候有时候会遇到一种情况,比如 :点击这个链接跳转到其他组件的情况,通常会跳转到新的页面,蛋是,我们不想跳转到新页面,只在当前页面切换着显示,那么就要涉及到路由的嵌套了,也可以说是子路由的使用. ...

  8. 【Java成王之路】EE初阶第二十四篇: Servlet

    Sevlet Servlet 是什么 Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一 个 web app. Tomcat 是一个 ...

  9. 第二十四篇 -- 学习第二十九天打卡20190720

    学习书籍<剑桥商务英语中级词汇精选>乱序版 Day05 Day25spectrum ['spektrəm] n.范围:领域:光谱:声谱例:This large group of resea ...

  10. 第二十六篇:USB3.0高带宽ISO(48KBytes/125us)实战

    USB3.1技术已经推出, 10Gbps的速率足以满足数据, HD视频传输的要求. 要步入USB3.1的研发, 还得将USB3.0的基础打扎实. 微软提供的SUPER MUTT只包含一个接口0, 其下 ...

最新文章

  1. Linux的企业-Mfs高可用corosync+pacemaker+fence+iscci
  2. tomcat 相关以及安装时遇到的一些问题整理
  3. linux免密登录_Linux SSH免密钥登录总结
  4. 添加到界面前获取尺寸
  5. 走进javascript——DOM事件
  6. 计算机系统结构试卷及答案
  7. android 开源 视频播放器,安卓视频播放器——ijkPlayer(Bilibili开源)-Go语言中文社区...
  8. 使用百度的地图生成器部署到https域名
  9. linux addr2line使用手册,addr2line 命令使用方法
  10. openStack开源云repo db local or on-line 实战部署之Ruiy王者归来
  11. 快门光圈感光度口诀_摄影:一张图让你明白什么叫光圈、快门、感光度、景深、ISO。...
  12. golang--channal与select
  13. 大三下,我们该做什么?一篇被转万次的日志,你值得一看
  14. 计算机基础2,计算机基础总结2
  15. 微服务--应对每秒上万并发下的参数优化实战(实战经验)
  16. HCIE课程笔记18-局域网二层技术
  17. 垃圾收集 (Garbage Collection,GC)
  18. npm介绍与cnpm介绍
  19. AOP技术介绍--(.Net中关于AOP的实现)
  20. buuctf MISC菜刀666

热门文章

  1. MySQL InnoDB 存储引擎写入磁盘(落盘)的原理\MySQL怎么保证持久性、原子性?(MySQL中是如何实现事务提交和回滚的)\隔离性
  2. C#自学29—简体字繁体字转换
  3. 租用GPU服务器跑深度学习模型心得
  4. Proxy的常见使用——正向代理的使用及配置总结
  5. 基于GNN网络的session推荐模型(知识图谱技术在推荐场景的应用)
  6. 卧槽!被蜜雪冰城洗脑了!
  7. 三、青龙面板 添加企业微信应用推送消息
  8. 今天是没有python的一天(大物实验‘利用牛顿环测量曲率半径’报告和大物复习)
  9. 单片机中code、data、idata、xdata等关键字意思
  10. SimpleMind Pro 1.29.1 小巧的思维导图工具