关于EHCI的一些东西

intel的本意是让EHCI只支持高速设备,需要companion controller UHCI来支持全速或低速设备

if the attacheddevice is not a high-speed device, the eHC driver releasesownership of the port
(and thus controlof the device) to a companion host controller. For that port,enumeration starts over from
the initial attachdetect point and the device is enumerated under the cHC. Otherwise,the eHC retains
ownership of theport and the device completes enumeration under theeHC.

The EHCI providessupport for two categories of transfer types: asynchronous andperiodic. Periodic
transfer typesinclude both isochronous and interrupt. Asynchronous transfer typesinclude control and bulk.

The periodicschedule is based on a time-oriented frame list that represents asliding window oftime of host
controller workitems. All isochronous and interrupt transfers are serviced via theperiodic schedule. The
asynchronousschedule is a simple circular list of schedule work items thatprovides a round-robin service
opportunity for allasynchronous transfers.

关于异步调度
很久之前做过一个简单的echihost驱动程序,并且实现了EHCI驱动读取U盘文件系统的功能,当时看了EHCI的spec,再模仿模仿现有的一点代码,稀里糊涂的做出来了,但是对EHCI的一些细节还没有考究过。特别是bulktransfer的Async调度,当时只是简单的组好qh、qTD数据结构,然后做好队列,再enableAsync调度,所以不需要复杂的动态移除qh之类的操作,对 Interrupton Async Advance Doorbell能功能都半知半解 。今天重读了一下,终于理解了这个功能的意义。


前提:
Software must notremove an active queue head from the schedule.Software
should firstdeactivate all active qTDs, wait for the queue head to go inactive,then remove the queue head fromthe asynchronous list.

USBCMD寄存器的第6bit是 Interrupton Async AdvanceDoorbell位,从字面意思可以看出来,这是一个门铃,什么门铃呢?异步调度从前走的时候产生中断的门铃。异步调度队列是一个环形队列,队列中的每个元素可以认为是一个调度单元,异步调度一个一个顺序往下调度执行事务。
当host驱动程序把这位置1的时候,表示让controller在要调度队列中的下一个元素的时候产生一个中断,这个中断就叫做AsyncAdcance中断,门铃的意思就是按下这个门铃,先给controller提个醒,让其到时候产生中断。
controller产生中断之前还会把USBSTS寄存器的Interrupton Async Advance   status位置1。(中断程序需要为这个状态位写1来清0)
另外,要产生中断,还需要USBINTR的Interrupton Async Advance Enable使能位置1。

那么这个门铃和中断有什么用呢?
是为了确定性( deterministic )的移除异步调度队列的队列头用的。
因为host驱动程序移除并释放队列里的某些队列头的时候,必须保证controller的cache里面已经没有这些队列头的信息,也就是说它已经用不到队列头了,否则有可能造成队列数据不一致;换句话说必须保证controller已经确认了队列的完整性
整个握手的过程是这样的,host驱动软件移除了某些个队列头(我的理解是移走的时候还不能把这个被移走的队列头的next指针破坏掉,因为next指针还需要被controller用来找到后面的头呢,也就是advance的过程,可以看linux内核代码start_unlink_async() ),它把 Interrupton Async AdvanceDoorbell置位,通知controller有东西从队列里被移走了。controller知道了之后就赶紧扫一遍整个环形队列,发现没有问题之后,在调度下一个队列头的时候产生中断,告诉驱动程序"我往下走没问题了,你把那些个移除的队列头内存都释放了吧~~",然后驱动程序就可以做下面的队列头还有buffer清理工作了。

下图就比较清楚的描述了这样的场景:
左上图一开始,驱动正在移除B,C的时候,controller正在处理A,这时候controllercache里都是A的信息,右上图移除了B,C之后,设置了doorbell位,让controller做advance工作,controller根据链表(确切的说是A和B的next指针)的信息,advance到D,然后就产生了中断通知驱动(见中下图)。
 

关于EHCI的一些东西(1)




USB 的DataToggle

"USB在通信过程中,有DataToggle这么一个概念。

例如,在一次通信中,主机如果需要接收一个数据包,那么,主机会先发送一个IN的令牌包,然后从机发送数据包,然后主机再发送ACK握手包进行确认,这就完成了一次数据的接收。

假如出现通信错误,掉包的情况,那么又如何处理呢?

第一步,假如是令牌包IN发生了通信错误,那么从机则不会发送数据包。主机可以再次发送令牌包IN来让从机发送数据包。

第二步,假如是数据包发生了错误,那么主机收不到数据,则不会发出ACK信号,而再次发送IN;而从机由于没有收到ACK,则得知数据包出错,可以再次发送该数据包。

现在问题来了,假如是ACK信号出错,主机已经成功接收到数据,认为通信完成。由于从机并没有接收到ACK信号,还认为数据出错,继续准备上一包数据,此时岂不是要不同步了?

这个时候,DataToggle就派上用场了。DataToggle要求数据包前加DATA0和DATA1标识,并且要求每成功完成一次通信后,对DATA标识进行切换,这样,主机在下一次的IN包中,就可能通过DATA的标识来判断从机是否成功地完成上一次数据通信了。

DataToggle在USBReset阶段是要清为0的,而今天就发现了一个错误,不幸在GetMaxLun和ClassReset这两条命令中对DataToggle进行了清零操作,造成了USB的通信过程中,发送这两条命令则有可能通信失败。"

上面是某个blog上对data toggle的描述,觉得挺有用的,不过找不到链接了。对于data toggle的描述USB2.0的规范在8.6章有详细描述:

关于EHCI的一些东西(2)

8.6 Data Toggle Synchronization andRetry

Receiver sequence bitstoggle only when the receiver is able to accept dataand
receives an error-free datapacket with the correct data PID. Transmitter sequence bits toggleonly when the
data transmitter receives avalid ACK handshake. The data transmitter and receiver must havetheir
sequence bits synchronizedat the start of a transaction. The synchronization mechanism usedvaries with
the transaction type. Datatoggle synchronization is not supported for isochronoustransfers.

Instead of explicitlyidentifying DATA0 and DATA1, it uses a value “DATAx”to
represent either/bothDATA0/DATA1 PIDs.

再看ehci控制器如何支持这个data toggle,先来看qh里面的transferoverlay,为什么叫overlay呢?因为下一个qTD被调度的时候,这个区域就完全被qTD的头覆盖率了。


qTD的Token部分最高(31)位是data toggle位,使用方法依赖于qh里面的Data ToggleControl位。
再看qh里面的Data Toggle Control(DTC)位,这位决定了当overlaytransition发生的时候,qh里面的DT位如何取值,如果为0,则qh的DT位保留原来值,而忽略qTD里面的DT;如果为1,则qh的DT位被qTD的DT位取代。

具体就根据事务类型看qTD里面的DT该怎么设置了,还得好好研究USB协议。




1. 从软件层面看,PCIe总线与PCI总线基本兼容
2.PCI总线空间与处理器空间隔离
3.每一个PCI设备都有独立的配置空间,在配置空间中含有该设备在PCI总线中使用的基地址,系统软件可以动态配置这个基地址,从而保证每个PCI设备使用的物理地址都不相同。PCI桥的配置空间中含有其下PCI子树所能使用的地址范围。PCI设备只有在系统软件初始化配置空间之后,才能够被其他主设备访问。当PCI配置空间被初始化之后,该设备在当前的PCI总线上将拥有一个独立的PCI总线地址空间,即BAR(BaseAddress Register)寄存器所描述的空间。
4.PCI设备使用共享总线方式进行数据传递,在同一条总线上,所有PCI设备共享同一带宽,这极大地影响PCI总线的利用率。这种机制显然不如PCIe总线的交换结构。
5.处理器与设备间的数据交换主要由“处理器访问PCI设备的地址空间”和“PCI设备使用DMA机制访问主存储器”这两部分组成。
6.在BAR寄存器中存放的是PCI设备使用的“PCI总线域”的物理地址,而不是“存储器域”的物理地址。
7.在x86体系结构中,存储器域的I/O地址与PCI总线域的I/O地址相同。在PowerPC处理器中,需要将这两个空间相互映射(通过Inbound和Outbound寄存器)。
8.系统软件开发者需要事先了解PCI设备的INTx信号与中断控制器的连接关系。此外外部设备与中断控制器的连接关系由硬件设计人员指定。
9.在一个处理器系统中,多数PCI设备仅使用INTA#信号,很少使用INTB#和INTC#信号,而INTD#更是极少使用。PCI设备配置空间的InterruptPin寄存器记录该设备究竟是用哪条INTx信号。
10.BIOS初始化代码根据中断路由表的信息,可以将PCI设备使用的中断向量号写入到该PCI设备配置空间的Interrupt Lineregister寄存器中。
11.在一棵PCI总线树上,有几个PCI桥(包括host主桥)就有几条PCI总线。总线号由系统软件决定,通常与host主桥直接相连的总线编号为0。系统软件使用深度优先算法扫描PCI总线树上的所有PCI总线,并以此进行编号。
12.PCI总线三要素,总线号、设备号、功能号。一条PCI总线上的设备号由PCI设备的IDSEL信号(相当于片选)与PCI总线地址线的连接关系确定,AD[31:11]将与PCI设备的IDSEL信号对应相连,这个片选决定了该设备在这条总线的设备号。而功能号与PCI设备号的具体设计相关。
13.PCI桥的配置空间在系统软件遍历PCI总线树的时候进行配置,系统不需要专门的驱动程序设置PCI桥的使用方法,这也是PCI桥被称为透明桥的原因。
14.PCI总线的初始化对于设备驱动来说可以说是透明的,因为系统起来对所有设备进行枚举和地址空间初始化后,设备驱动关心的就是自己的这些个空间和中断等信息,以及自己的具体功能,从这个角度来说,PCI的设计真是很科学。


  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值