本文简要介绍一下UEFI中EHCI驱动的代码实现框架:
下图是HCDI:
上图是Host驱动程序向上层驱动提供的接口图:
1.大部分接口的最后动作都是去操作主控制器寄存器,ECHI的spec:《ehci-specification-for-usb.pdf》;
2.寄存器PORTSC用来获取/设置端口的状态(这里表示root hub的端口),root hub有几个port,这里就会有几个对应的寄存器(根据硬件厂商自己的实现);
3.图中出现的2个链表是由硬件自己维护的,链表位于内存的地址由相关寄存器指定,驱动程序把要发送的数据写到这里,硬件会自动执行发送;
4.Asynchronous Schedule list用来发送controller和bulk(发完就删除,比如获取一下设备状态);
5.Periodic schedule frame list用来发送异步中断(周期发送,比如定期查询hub,kb的状态)。
下图是EHCI驱动初始化的流程:
初始化流程图:
1.定义了一个定时器事件,这样在驱动程序初始化完成后,会有一个函数EhcMonitorAsyncRequests()周期执行,用来查询异步中断传输队列:&Ehc->AsyncIntTransfers;
(参考UEFI spec中的这句话:An Asynchronous Interrupt Transfer is typically used to query a device’s status at a fixed rate. For example, keyboard,mouse, and hub devices use this type of transfer to query their interrupt endpoints at a fixed rate.)后面我们会发现,在初始化HUB、KB这些device的时候会调用到AsyncInterruptTransfer()来把数据添加到periodic schedule frame list。
下面从数据结构的角度看一下EHCI的驱动结构:
下图是EHCI驱动中涉及的主要数据结构的关系图:
1.Struct USB2_HC_DEV是Host controller的核心数据结构,在初始化过程中创建;QTD、QH的数据结构的定义位于 EHCI spec 3.5/3.6;
2.管理controller和bulk传输:插入Asynchronous Schedule list
//把组装好的Qh插入EHCI主控制器的Asynchronous Schedule List,以便硬件执行传输命令
EhcLinkQhToAsync (Ehc, Urb->Qh);
//阻塞式的执行此次controller传输
Status = EhcExecTransfer (Ehc, Urb, TimeOut);
//从Asynchronous Schedule List中将其移除
EhcUnlinkQhFromAsync (Ehc, Urb->Qh);
3.管理isochronous和interrupt传输:插入Periodic schedule frame list
//把组装好的Qh插入EHCI主控制器的Periodic schedule frame list,以便硬件执行传输命令
EhcLinkQhToPeriod (Ehc, Urb->Qh);
//并把URB插入异步中断传输链表 &Ehc->AsyncIntTransfers
InsertHeadList (&Ehc->AsyncIntTransfers, &Urb->UrbList);
4.插入硬件链表的URB,硬件会自动执行发送;
5.链表&Ehc->AsyncIntTransfers是由驱动程序创建并管理的,由EhcMonitorAsyncRequests()管理;
(1)他会循环&Ehc->AsyncIntTransfers上的每个urb;
(2)通过判断QTD.status来判断执行结果(一个urb中包含一个QH和一串QTD);
(3)更新QH,为下一轮异步传输准备;
(4)如果有回调函数,执行回调函数。
6.总结:EHCI驱动初始化完成后,硬件负责维护两个链表(自动发送上面的数据),软件负责维护一个链表(用来周期查询Periodic list中的URB的执行结果,并调用回调函数)。