Windows XP下usbport
+ -

Windows XP下usbport.sys驱动内部实现解析(三)

2024-09-27 10 0

_USBPORT_MapTransfer是个比较复杂的函数了,他涉及到transfer的切割、sgList结构的填写,少安毋躁。。。哈哈

  1. struct SG_LIST
  2. {
  3. ULONG Flags;
  4. PVOID CurrentVa;
  5. PVOID MappedSysAddress;
  6. ULONG EntriesCount;
  7. SG_ENTRY Entries[1]; // var size
  8. };
  9. struct SG_ENTRY
  10. {
  11. ULARGE_INTEGER PhysicalAddr;
  12. PVOID VirtualAddr;
  13. ULONG Length;
  14. ULONG Offset;
  15. ULONG field_14; // unknown
  16. };
  17. _USBPORT_MapTransfer(pFdo,pIrp,pMapRegisterBase,pTransfer)
  18. {
  19. pFdoExt->DoMapping = FALSE
  20. // 1. build sgList
  21. ULONG ulMappedLength = pUrb->TransferBufferLength
  22. do
  23. {
  24. IoMapTransfer(...&ulMappedLength..);
  25. add physical:virtual to sgList
  26. ulMappedLength = pUrb->TransferBufferLength - ulMappedLength
  27. } while(ulMappedLength != 0);
  28. // 2. split transfer
  29. if this is an iso transfer
  30. init some field for iso transfer only
  31. LIST_ENTRY listHeader;
  32. call _USBPORT_SplitTransfer(..pTransfer,&listHeader);
  33. while(listHeader is not empty)
  34. {
  35. get transfer from list header
  36. insert to endpoint\'s ActiveTransfer
  37. }
  38. // 3. endpoint worker
  39. call _USBPORT_CoreEndpointWorker
  40. if return != 0
  41. call _USBPORT_InvalidateEndpoint
  42. }
  43. 看到这里我想你也能大致CoreEndpointWorkerInvalidateEndpoint的大致作用,很明显是用来处理active transfer list的。先不管他,来看split
  44. USBPORT_SplitTransfer(pFdoExt,pEndpoint,pTransfer,pListHead)
  45. {
  46. init pTransfer->ChildTransferListHead
  47. if(pTransfer->TransferBufferLength <= pEndpoint->MaxTransferLength)
  48. insert pTransfer to pListHead
  49. else
  50. {
  51. ASSERT(pTransfer->Type == interrupt || pTransfer->Type == bulk)
  52. call _USBPORT_SplitBulkInterruptTransfer
  53. }
  54. }

很简单的逻辑:只有在transfer length 大于endpoint的最大传输大小,并且是interrupt或者bulk的时候,才split。最多传输长度并不是MaxPacketSize,这个长度要大得多。

  1. _USBPORT_SplitBulkInterruptTransfer(pFdo,pEndpoint,pTransfer,pListHead)
  2. {
  3. ULONG ulChildrenCount = pEndpoint->MaxTransferLen / pTransfer->TransferBufferLength
  4. ExAllocatePool(sizeof(TRANSFER) * ulChildrenCount)
  5. copy from pTransfer to all the new allocated structs
  6. reinit all the children transfer\'s list entry
  7. ULONG ulLeftTransferLen = pTransfer->TransferBufferLength
  8. pChildTransfer = FirstChildTransfer
  9. pOrgSgList = &pTransfer->sgList
  10. curSgEntryIndex = 0
  11. curOffsetInEntry = 0
  12. OffsetInTotalBuffer = 0
  13. while(leftTransferLen != 0)
  14. {
  15. call _USBPORT_MakeSplitTransfer( pFdo, pOrgSgList, pChildTransfer, MaxTransferLength, MaxPacketLength, &curSgEntryIndex, &curOffsetInEntry, leftTransferLength, OffsetInTotalBuffer )
  16. leftTransferLen = return value
  17. link pChildTransfer to pListHead
  18. OffsetInTotalBuffer += pChildTransfer->TransferBufferLength
  19. pChildTransfer = NextChildTransfer
  20. }
  21. }

这个过程有些复杂。简单的讲,这个函数首先计算需要多少个children transfer,然后分配这么多个结构,然后把原来结构的内容copy到新的每个结构里面,但是那些list必须要重新初始化成正确的值,不能指向原来的transfer。然后调用一个辅助函数切割原来transfer的sgList,每个小的transfer的大小最多是 pEndpoint->MaxTransfer 这么大。

sgList是由一个一个的sgEntry组成的,这些sgEntry的长度却未必就是pEndpoint->MaxTransfer 这么大,所以必须由两变量来表示当前处理的是哪个entry,以及当前处理到了这个entry的哪个offset。也就是上面 curSgEntryIndex 和 curOffsetInEntry 的作用,USBPORT_MakeSplitTransfer辅助函数会修改这两个值。

最后注意,原来那个被切割的transfer并没有进入pListHeader,当然也就没有进入Endpoint的ActiveTransfer。

  1. USBPORT_MakeSplitTransfer( pFdo, pOrgSgList, pChildTransfer, MaxTransferLen, MaxPacketLen, pCurEntryIndex, pCurOffsetInEntry, LeftTransferLen, OffsetInTotalBuffer)
  2. {
  3. ASSERT(MaxTransferLen % MaxPacketLen == 0)
  4. length = MaxTransferLen
  5. left = pOrgSgList->sgEntry[*pCurEntryIndex].Length - *pCurOffsetInEntry
  6. pChildTransfer->sgList.Count = 0
  7. i = 0
  8. while(length > left)
  9. {
  10. pChildTransfer->sgList.Count ++
  11. pChildTransfer->sgList.sgEntry[ i ]. Length = left
  12. pChildTransfer->sgList.sgEntry[ i ].VirtualAddr = pOrgSgList->sgEntry[*pCurEntryIndex].VirtualAddr + *pCurOffsetInEntry
  13. pChildTransfer->sgList.sgEntry[ i ].PhysicalAddr = pOrgSgList->sgEntry[*pCurEntryIndex].PhysicalAddr + *pCurOffsetInEntry
  14. i ++
  15. *pCurEntryIndex ++
  16. *pCurOffsetInEntry = 0
  17. LeftTransferLen -= left
  18. length -= left
  19. left = pOrgSgList->sgEntry[*pCurEntryIndex].Length
  20. }
  21. if(length)
  22. {
  23. pChildTransfer->sgList.Count ++
  24. pChildTransfer->sgList.sgEntry[ i ].Length = length
  25. pChildTransfer->sgList.sgEntry[ i ].VirtualAddr = pOrgSgList->sgEntry[*pCurEntryIndex].VirtualAddr + *pCurOffsetInEntry;
  26. pChildTransfer->sgList.sgEntry[ i ].PhysicalAddr = pOrgSgList->sgEntry[*pCurEntryIndex].PhysicalAddr + *pCurOffsetInEntry;
  27. *pCurOffsetInEntry += length
  28. if(*pCurOffsetInEntry == pOrgSgList->sgEntry[*pCurEntryIndex].Length)
  29. {
  30. *pCurOffsetInEntry = 0
  31. *pCurEntryIndex ++
  32. }
  33. }
  34. return LeftTransferLen - length
  35. }

逻辑很简单,按照MaxTransferLen划分切割就行了。

接下来就是要真正处理active transfer的部分了:

  1. _USBPORT_CoreEndpointWorker(pEndpoint,bRecursion)
  2. {
  3. if(!bRecursion)
  4. {
  5. if(pEndpoint->Busy)
  6. return 1;
  7. else
  8. pEndpoint->Busy = 1
  9. }
  10. call _USBPORT_GetEndpointState(pEndpoint)
  11. state = return value
  12. if(state == closed)
  13. {
  14. pEndpoint->Busy = 0
  15. return 0;
  16. }
  17. call _USBPORT_PollEndpoint(pEndpoint)
  18. call _USBPORT_GetEndpointState(pEndpoint)
  19. state = return value
  20. if(state == removed)
  21. {
  22. pEndpoint->CurrentState = close
  23. pEndpoint->NextState = close
  24. insert the endpoint to fdo\'s ClosedEndpointListHead
  25. pEndpoint->Busy = 0
  26. return 0
  27. }
  28. if(pEndpoint->ActiveTransfer is empty &&
  29. pEndpoint->PendingTransfer is empty &&
  30. pEndpoint->CancelTransfer is empty)
  31. {
  32. pEndpoint->Busy = 0
  33. call _USBPORT_FlushAbortList(pEndpoint)
  34. return 0
  35. }
  36. call _USBPORT_GetEndpointState(pEndpoint)
  37. state = return value
  38. if(state != pEndpoint->NextState)
  39. {
  40. pEndpoint->Busy = 0
  41. return 1
  42. }
  43. call pEndpoint->WorkRoutine(pEndpoint)
  44. call _USBPORT_FlushAbortList(pEndpoint)
  45. pEndpoint->Busy = 0
  46. return 0;
  47. }

眼见函数就要分析完了,这个家伙马上又引出来若干个函数。。哈哈

其中USBPORT_FlushAbortList函数在讲到cancel的时候再说,他其实是一个无关紧要的函数。

而_USBPORT_PollEndpoint函数则是调用miniport对应的Poll函数,在这个函数里面,miniport driver总是检查自己的transfer有没有完成了的,如果有就告诉usbport自己完成了某个transfer。

至于USBPORT_GetEndpointState更是简单,如果pEndpoint->CurrentState == pEndpoint->NextState,则返回CurrentState,否则返回一个特殊的值 ENDPOINT_STATE_TRANSITION,这个值从来不会被设置到CurrentState 跟 NextState成员上面。

至于这个state的用法这里先大致的说说,state表征了endpoint的当前状态,能够设置的值有active, pause, remove, close,后两种是在特别的情况下才设置的。当你关闭一个endpoint的时候 (select configuration却填一个0的config desc),endpoint进入remove状态,上面已经看到他马上就转换成了close状态而进入了fdo的closed enpoint list,作lazy close。而active状态就是正常状态,在这个状态下,endpoint把自己的active list里面的transfer一一提交给miniport driver。而pause的状态是为了处理cancel跟abort的情况的。如果active list里面的transfer有某个被标记成了canceled或者aborted.endpoint就从active状态进入pause状态。进入 pause状态以后一一完成那些标记成canceled和aborted的transfer,直到全部都处理完了,就转入active状态。

那上面这些操作都是由谁来完成的呢?当然不用说也是pEndpoint->WorkRoutine要作的事情了。

当然,状态的转换并不是这么简单的事情。一个最主要的原因是要同步endpoint的FrameNumber32Bit成员到当前usb host controller的Frame Number,这个主要是用于Iso传输的,所以状态转换的过程是个迂回的。

endpoint 首先进入fdo的state change list,然后fdo让miniport driver在下一个start of frame的时候引发一个中断,在isr的dpc里面同步endpoint的Frame Number,然后把endpoint从state change list里面取出来,这样endpoint才算是进入了新的状态,也就是让endpoint的CurrentState跟NextState相同。

然后要注意的是,这个函数如果返回值是1的话,紧接着调用的就是_USBPORT_InvalidateEndpoint函数。这个函数告诉一个辅助线程 (通过设置一个Event),调用一次_USBPORT_CoreEndpointWorker。miniport driver也会在比如某个传输完成的时候调用这个函数来排队CoreEndpointWorker的调用,使得usbport能继续向其提交新的传输,而不是停下来。

上面这段话是什么意思呢?看这个流程:进入pending -> flush pending -> 进入map ->flush map->MapTransfer进入 active->CoreWorker 提交一些但不是全部的transfer给硬件。

这里就层层返回了..并没有连续的提交。那要提交下一个怎么办呢?必须要再次进入CoreWorker才可以,这就是通过InvalidateEndpoint函数来完成的。

Invalidate 这个函数把你要Invalidate的Endpoint收集到一个list里面链接到fdo的AttendListHead上,然后唤醒work thread。work thread从fdo的AttendListHead上一一取出每个endpoint,然后调用CoreWorker,如果返回还是1,则保存到另外一个 list上面。当第一遍循环完了,再把那些还是返回1的endpoint重新链接到fdo的AttendListHead上面,再次排队下一个 CoreWorker调用,也就是再次设置Event,而不是直接循环再调用CoreWorker。

希望能明白这种排队poll调用的机制。记住一点:transfer的处理是要通过调用CoreWorker才会传递给miniport然后传递给硬件的,InvalidateEndpoint正是引发一次CoreWorker调用的关键。

原文转自:https://blog.csdn.net/killcpp/article/details/7287110

0 篇笔记 写笔记

Windows复位USB集线器HUB端口设备RestartUsbPort
本人描述了如何在 Windows 下使用IOCTL_USB_HUB_CYCLE_PORT 重新启动USB端口。具体过程为:通过给定的设备实例ID在 Windows设备管理中查找USB 备、确定使用的 USB 端口号、获取其父设备(其 USB 集线器)、打开集线器并执行 IOCTL_USB_HUB_C......
Windows XP下usbport.sys驱动内部实现解析(一)
讲了USB驱动栈整体结构,说明了usbport.sys的重要作用,现在就具体分析usbport.sys的内部实现细节。首先再重复一下:usbport 是一个USB主机控制器的port driverusbuhci是uhci类型的USB主机控制器的miniport driverusbehci则是e......
Windows XP下usbport.sys驱动内部实现解析(二)
4. 处理USB请求(URB)在进入下一个主题之前我总结几个事实让大家注意:对于每个usb总线上的设备,usbport在usbhub的帮助下为其创建一个device handle,并把这些device handle链接到一起,并为endpoint 0 创建一个pipe handle。在进行se......
Windows XP下usbport.sys驱动内部实现解析(三)
_USBPORT_MapTransfer是个比较复杂的函数了,他涉及到transfer的切割、sgList结构的填写,少安毋躁。。。哈哈struct SG_LIST{ ULONG Flags; PVOID CurrentVa; PVOID MappedSysAddress......
Windows XP下usbport.sys驱动内部实现解析(四)
然后来看看实际上的work routine,先看通用的这个。root hub的是一个特殊的函数。USBPORT_DmaEndpointWorker(pEndpoint){ call _USBPORT_GetEndpointState(pEndpoint) curState = ......
关注公众号
  • HID人机交互
  • Linux&USB
  • UAC音频
  • TYPE-C
  • USB规范
  • USB大容量存储
  • USB百科
  • USB周边
  • UVC摄像头
  • Windows系统USB
  • 音视频博客
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

    打开支付宝扫一扫,即可进行扫码打赏哦

    您的支持,是我们前进的动力!