本文簡要介紹一下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的執行結果,並調用回調函數)。