Windows XP下usbport
+ -

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

2024-09-27 0 0

4. 处理USB请求(URB)

在进入下一个主题之前我总结几个事实让大家注意:

  1. 对于每个usb总线上的设备,usbport在usbhub的帮助下为其创建一个device handle,并把这些device handle链接到一起,并为endpoint 0 创建一个pipe handle。

  2. 在进行select configuration的时候,usbport创建一个config handle结构,保存其指针到对应的device handle,然后创建若干个interface handle,全部链接到config handle上去。对于每个interface里面的全部endpoint,为其创建一个pipe handle,全部连接到对应的device handle上面去。对于每个pipe handle,为其创建一个endpoint结构,保存其指针到pipe handle,并把所有创建的endpoint全部链接到一起。

  3. 客户驱动必须要保存由select configuration所返回的pipe handle,并在随后的urb里面恰当的填写这个值。

前面的结构中的若干个LIST_ENTRY就是usbport维护管理这些结构的关键。维护管理用的基础结构了解好了,接下来就是urb的处理问题了。

说到urb的处理,就不能不提一些internal io control的问题。都知道urb是通过一个internal io control提交的,除了这个以外还有若干个其他的internal io control,比如用于获取port状态的、比如用于reset port的、等等等等。这些处理这里先不提,因为大部分的处理都是由usbhub完成的,usbport单独完成的功能很少,还有部分是两个配合完成的 — 这个留到usbhub的地方再回头来说。

首先明白一个事实,客户驱动把urb提交给的是自己的pdo,这个pdo作一些过滤动作以后,直接提交给了root hub的pdo。至于是不是这样的一个情况,等到说起usbhub的时候就明白了,现在先假定如此。

usbport在一份数据结构的帮助下对每个urb作一些检查,然后转到特定的处理函数,先看这个数据结构的定义:

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;
};

很简单的一个结构。TransferLen用于固定大小的传输,用来检查说提交的buffer大小是否正确,如果是0,则不检查大小信息。接下来的几个成员用于control transfer用来填充setup packet。许多的urb都不需要你完全的填充所有的setup packet成员,usbport在这里会为你填充这些已知的固定的成员。flags则是控制usbport的操作方式的,下面会详细的解释。 function code则是指明这份数据是对应哪个function的。理所当然的,usbport为每个function code准备一个这样的结构构成一个数组。

下面是处理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是一个公共的处理函数,他根据dispatch table的flags成员作一些有限度的处理,然后交给真正的dispatch routine处理。

这个dispatch routine就各式各样了,大致分成4类:

  • 1类就是不需要transfer结构了,参考上flags & 1非0的分支。这类urb大多直接完成了,比如select configuration,比如get frame length。
  • 2类属于control 传输,这类就根据dispatch table里面的那些setup packet成员填充自己的setup pack结构,然后将transfer排队。
  • 3类属于interrupt or bulk 传输,直接排队transfer。
  • 4类属于iso transfer,最主要的是要检查各个Packet,而且要设置start frame number,最后还是要排队transfer。

不需要排队的urb大多是一些没有实现的urb,比如set frame length,或者是一些要特别处理的,比如select configuration。其他的最终都是要排队transfer的。特别注意参加排队的是transfer结构,而不是irp或者其他。对照 endpoint结构的那些成员PendingTransferList等等就能明白,排队的对象并不是irp。

transfer 也是一个不小的结构:

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
};

留下了一些分析相关的成员:其中如果这个transfer对应有irp则会设置Irp成员,这个是可以选的。如果没有对应irp也是可以的,那么怎么知道这个transfer完成了呢?用irp的话还可以使用complete routine,要是没有irp呢?这就是下面的那个pEvent成员的作用了,他指向一个event,完成的时候会设置这个event。

还有几个list entry,存在的主要原因就是允许传输大于MaxTransferLength的数据,那么就必须把原来的buffer切割成小的buffer,为每个buffer创建一个child transfer,这些list entry就是用来管理这个的。

至于AdapterDBList,则是上面说的某个buffer跨越了非连续的两个物理页的情况下,用于miniport通知的。 miniport必须要使用额外的缓冲而不是transfer所提供的缓冲,所以usbport必须要提供空间来保存这些额外的缓冲信息。

最后的是sgList,usbport把要传输的buffer映射成一个一个的物理页,用sgList这个结构来描述,这个结构会传递给miniport driver使用。

好了,来看真实的排队情况:usbport通过调用_USBPORT_QueueTransferUrb函数来排队某个urb,这个函数很简单。

_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是否关联有irp调用不同的函数,在有irp的情况下首先是要设置irp的cancel routine,然后再调用USBPORT_QueuePendingUrbToEndpoint函数。也其实就是多一个设置cancel routine的步骤,至于cancel部分后面会有专门的讲解,先放一放。主要来看后面这个函数,更是非常简单:

_USBPORT_QueuePendingUrbToEndpoint(pTransfer,pEndpoint)
{
    link transfer->TransferListEntry to Endpoint->PendingTransfer
}

接下来当然是USBPORT_FlushPendingList函数了,看他的名字都知道是在干什么。这个函数显得很复杂,因为是几个很关键的函数之一。

_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 );
}

或者看了会很奇怪,先不管,我把全部代码流程都列出来,然后再总体讨论。

_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
}

这个函数比较简单,作作判断决定transfer该进入什么样子的list,然后返回一个标记表明是否需要进行map,只有在transfer的 transfer length也就是pUrb->TransferBufferLength非0的时候,才需要进行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;
    }
}

也是一个不算复杂的函数:首先检查当前是否在map,如果为false,然后检查map transfer list是否是空,不空则取一个处理调用AllocateAdapterChannel,传递的参数是_USBPORT_MapTransfer,在这个函数里面会重新设置pFdo->DoMapping = FALSE。

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
  • 音视频博客
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

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

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