(接上一篇) 4. 處理USB請求(URB) 在進(jìn)入下一個(gè)主題之前我總結幾個(gè)事實(shí)讓大家注意: 1. 對于每個(gè)usb總線(xiàn)上的設備,usbport在usbhub的幫助下為其創(chuàng )建一個(gè)device handle,并把這些device handle鏈接到一起,并為endpoint 0 創(chuàng )建一個(gè)pipe handle。 2. 在進(jìn)行select configuration的時(shí)候,usbport創(chuàng )建一個(gè)config handle結構,保存其指針到對應的device handle,然后創(chuàng )建若干個(gè)interface handle,全部鏈接到config handle上去。對于每個(gè)interface里面的全部endpoint,為其創(chuàng )建一個(gè)pipe handle,全部連接到對應的device handle上面去。對于每個(gè)pipe handle,為其創(chuàng )建一個(gè)endpoint結構,保存其指針到pipe handle,并把所有創(chuàng )建的endpoint全部鏈接到一起。 3. 客戶(hù)驅動(dòng)必須要保存由select configuration所返回的pipe handle,并在隨后的urb里面恰當的填寫(xiě)這個(gè)值。 前面的結構中的若干個(gè)LIST_ENTRY就是usbport維護管理這些結構的關(guān)鍵。維護管理用的基礎結構了解好了,接下來(lái)就是urb的處理問(wèn)題了。 說(shuō)到urb的處理,就不能不提一些internal io control的問(wèn)題。都知道urb是通過(guò)一個(gè)internal io control提交的,除了這個(gè)以外還有若干個(gè)其他的internal io control,比如用于獲取port狀態(tài)的、比如用于reset port的、等等等等。這些處理這里先不提,因為大部分的處理都是由usbhub完成的,usbport單獨完成的功能很少,還有部分是兩個(gè)配合完成的 -- 這個(gè)留到usbhub的地方再回頭來(lái)說(shuō)。 首先明白一個(gè)事實(shí),客戶(hù)驅動(dòng)把urb提交給的是自己的pdo,這個(gè)pdo作一些過(guò)濾動(dòng)作以后,直接提交給了root hub的pdo。至于是不是這樣的一個(gè)情況,等到說(shuō)起usbhub的時(shí)候就明白了,現在先假定如此。 usbport在一份數據結構的幫助下對每個(gè)urb作一些檢查,然后轉到特定的處理函數,先看這個(gè)數據結構的定義: struct URB_DISPATCH_TABLE // sizeof=0X14 { PVOID DispatchRoutine; // process routine USHORT TransferLen; // UCHAR reserved[2]; UCHAR bmRequestDirection; // setup packet UCHAR bmRequestType; UCHAR Recipient; UCHAR bRequest; UCHAR Flags; UCHAR data[3]; ULONG FunctionCode; }; 很簡(jiǎn)單的一個(gè)結構。TransferLen用于固定大小的傳輸,用來(lái)檢查說(shuō)提交的buffer大小是否正確,如果是0,則不檢查大小信息。接下來(lái)的幾個(gè)成員用于control transfer用來(lái)填充setup packet。許多的urb都不需要你完全的填充所有的setup packet成員,usbport在這里會(huì )為你填充這些已知的固定的成員。flags則是控制usbport的操作方式的,下面會(huì )詳細的解釋。 function code則是指明這份數據是對應哪個(gè)function的。理所當然的,usbport為每個(gè)function code準備一個(gè)這樣的結構構成一個(gè)數組。 下面是處理URB的代碼: processurb(pUrb) { 檢查FunctionCode 檢查pUrb->DeviceHandle if(UrbDispatchTable[FunCode].Flags & 4) { // force usbport to use default pipe get pipe handle from device handle save it to pUrb } if(UrbDispatchTable[FunCode].Flags & 1) { // actual transfer needed if(UrbDispatchTable[FunCode].Flags & 8) { // no transfer buffer needed pUrb->TransferBuffer = 0 pUrb->TransferBufferLength = 0 pUrb->TransferBufferMdl = 0 } validate pipe handle if(pUrb->TransferBufferLength !=0 && pUrb->TransferBufferMdl == 0) { IoAllocateMdl(); MmBuildMdlForNonPagedPool(); set a flag in UrbHeader,indicates that when we complete this urb,we must free the mdl } allocate a transfer struct } check transfer length status = call UrbDispatchTable[FunCode]->DispatchRoutine(...); if(status != pending) complete the urb return status; } proccess urb是一個(gè)公共的處理函數,他根據dispatch table的flags成員作一些有限度的處理,然后交給真正的dispatch routine處理。 這個(gè)dispatch routine就各式各樣了,大致分成4類(lèi): 1類(lèi)就是不需要transfer結構了,參考上flags & 1非0的分支。這類(lèi)urb大多直接完成了,比如select configuration,比如get frame length。 2類(lèi)屬于control 傳輸,這類(lèi)就根據dispatch table里面的那些setup packet成員填充自己的setup pack結構,然后將transfer排隊。 3類(lèi)屬于interrupt or bulk 傳輸,直接排隊transfer。 4類(lèi)屬于iso transfer,最主要的是要檢查各個(gè)Packet,而且要設置start frame number,最后還是要排隊transfer。 不需要排隊的urb大多是一些沒(méi)有實(shí)現的urb,比如set frame length,或者是一些要特別處理的,比如select configuration。其他的最終都是要排隊transfer的。特別注意參加排隊的是transfer結構,而不是irp或者其他。對照 endpoint結構的那些成員PendingTransferList等等就能明白,排隊的對象并不是irp。 transfer 也是一個(gè)不小的結構: struct TRANSFER // sizeof=0XC0 { ULONG Direction; ULONG TimeoutInterval; LARGE_INTEGER SubmitTime; ULONG TransferedLen; ULONG Status; ULONG Irp; PKEVENT pEvent; PURB UrbPointer; LIST_ENTRY TransferListEntry; // link entry ULONG MappedRegisterBase; // for dma ULONG NumberOfMappedRegister; // ULONG TransferDirection; ULONG TransferBufferLen; URB_SETUP_PACKET SetupPacket; PMDL TransferMdl; LIST_ENTRY AdapterDBList; // for double buffer PVOID ParentPointer; // split PVOID EndpointPointer; PVOID ClientTransferPointer; // passed to miniport LIST_ENTRY ChildTransferListHead; LIST_ENTRY SplitTransferListEntry; PVOID IsoTransferInfo; // iso SG_LIST sgList; // scatter-gather list }; 留下了一些分析相關(guān)的成員:其中如果這個(gè)transfer對應有irp則會(huì )設置Irp成員,這個(gè)是可以選的。如果沒(méi)有對應irp也是可以的,那么怎么知道這個(gè)transfer完成了呢?用irp的話(huà)還可以使用complete routine,要是沒(méi)有irp呢?這就是下面的那個(gè)pEvent成員的作用了,他指向一個(gè)event,完成的時(shí)候會(huì )設置這個(gè)event。 還有幾個(gè)list entry,存在的主要原因就是允許傳輸大于MaxTransferLength的數據,那么就必須把原來(lái)的buffer切割成小的buffer,為每個(gè)buffer創(chuàng )建一個(gè)child transfer,這些list entry就是用來(lái)管理這個(gè)的。 至于A(yíng)dapterDBList,則是上面說(shuō)的某個(gè)buffer跨越了非連續的兩個(gè)物理頁(yè)的情況下,用于miniport通知的。 miniport必須要使用額外的緩沖而不是transfer所提供的緩沖,所以usbport必須要提供空間來(lái)保存這些額外的緩沖信息。 最后的是sgList,usbport把要傳輸的buffer映射成一個(gè)一個(gè)的物理頁(yè),用sgList這個(gè)結構來(lái)描述,這個(gè)結構會(huì )傳遞給miniport driver使用。 好了,來(lái)看真實(shí)的排隊情況:usbport通過(guò)調用_USBPORT_QueueTransferUrb函數來(lái)排隊某個(gè)urb,這個(gè)函數很簡(jiǎn)單。 _USBPORT_QueueTransferUrb(pUrb,pEndpoint) { do some check update some fields in transfer struct if(transfer associates with an irp) call _USBPORT_QueuePendingTransferIrp else call _USBPORT_QueuePendingUrbToEndpoint call _USBPORT_FlushPendingList } 根據transfer是否關(guān)聯(lián)有irp調用不同的函數,在有irp的情況下首先是要設置irp的cancel routine,然后再調用_USBPORT_QueuePendingUrbToEndpoint函數。也其實(shí)就是多一個(gè)設置cancel routine的步驟,至于cancel部分后面會(huì )有專(zhuān)門(mén)的講解,先放一放。主要來(lái)看后面這個(gè)函數,更是非常簡(jiǎn)單: _USBPORT_QueuePendingUrbToEndpoint(pTransfer,pEndpoint) { link transfer->TransferListEntry to Endpoint->PendingTransfer } 接下來(lái)當然是_USBPORT_FlushPendingList函數了,看他的名字都知道是在干什么。這個(gè)函數顯得很復雜,因為是幾個(gè)很關(guān)鍵的函數之一。 _USBPORT_FlushPendingList(pEndpoint) { bContinue = TRUE do { if(Endpoint is not root hub\'s endpoint) { check Endpoint->ActiveTransfer list if(is not empty) { get a transfer from active list bContinue = FALSE call _USBPORT_CoreEndpointWorker if return != 0 call _USBPORT_InvalidateEndpoint } } if(bContinue == FALSE) break; check Endpoint->PendingTransfer if(is not empty) { reset canel routine if(irp has not been canceled) { bContinue = FALSE call _USBPORT_QueueActiveUrbToEndpoint if( return != 0 ) call _USBPORT_FlushMapTransferList else { call _USBPORT_CoreEndpointWorker if return != 0 call _USBPORT_InvalidateEndpoint } } } while( bContinue ); } 或者看了會(huì )很奇怪,先不管,我把全部代碼流程都列出來(lái),然后再總體討論。 _USBPORT_QueueActiveUrbToEndpoint(pTransfer,pEndpoint) { bNeedMap = FALSE if(Endpoint is stopped || Transfer is aborted) link transfer to pEndpoint->CancelTransfer else { if(Transfer\'s length != 0) { link transfer to fdo\'s MapTransferList bNeedMap = TRUE; } else { link transfer to pEndpoint->ActiveTransfer } } return bNeedMap } 這個(gè)函數比較簡(jiǎn)單,作作判斷決定transfer該進(jìn)入什么樣子的list,然后返回一個(gè)標記表明是否需要進(jìn)行map,只有在transfer的 transfer length也就是pUrb->TransferBufferLength非0的時(shí)候,才需要進(jìn)行Map。 _USBPORT_FlushMapTransferList { while(!pFdoExt->DoMapping) { if(pFdoExt->MapTransferList is not empty) { pFdoExt->DoMapping = TRUE; get a transfer from the list AllocateAdapterChannel(_USBPORT_MapTransfer); } else break; } } 也是一個(gè)不算復雜的函數:首先檢查當前是否在map,如果為false,然后檢查map transfer list是否是空,不空則取一個(gè)處理調用AllocateAdapterChannel,傳遞的參數是_USBPORT_MapTransfer,在這個(gè)函數里面會(huì )重新設置pFdo->DoMapping = FALSE。 |
聯(lián)系客服