Windows XP下usbport
+ -

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

2024-09-27 本文链接为:http://www.usbzh.com/article/detail-1392.html ,欢迎转载,转载请附上本文链接。

讲了USB驱动栈整体结构,说明了usbport.sys的重要作用,现在就具体分析usbport.sys的内部实现细节。

首先再重复一下:

  • usbd 是USB1.1的类驱动程序,支持的是USB1.1
  • usbport 是一个USB主机控制器的port driver,支持的USB2.0
  • usbuhci是uhci类型的USB主机控制器的miniport driver
  • usbehci则是ehci类型的USB主机控制器的miniport driver

usbport负责创建管理USB主机控制器的 FDO ( Function Device Object )。而 FDO 对应的 PDO (Physical Device Object) 是由谁创建的呢?当然是这个主机控制器所在总线的总线驱动创建管理的了 — 一般就是PCI总线驱动。

usbport主要完成的功能如下:

  1. 抽象层次上管理USB主机控制器,实际的操作是由各个miniport driver 完成的。
  2. 创建并管理集成在USB主机控制器里面的根总线的PDO,这个PDO上会附加由usbhub.sys创建并管理的FDO 对象。
  3. 管理维护所有挂在当前这个主机控制器上的最多127个usb 设备。
  4. 处理大多数针对当前这个主机控制器管理下的总线上的所有usb设备所发出的USB请求(URB)。

基本功能如上所述,这里就这几方面的功能一一描述。

1. 抽象层次上管理USB主机控制器

USBPort是USB主机控制器的类驱动程序,针对不同的EHCI硬件厂商,提供统一的抽象回调函数实现EHCI主机控制器的全部功能和流程。

USBechic是USBPort不对硬件函数功能的具体硬件。其通过USBPort导出的函数USBPORT_RegisterUSBPortDriver向USBPort驱动注册一个大在的结构体USBPORT_REGISTRATION_PACKET。USBPORT_REGISTRATION_PACKET中包含了USBPort需要的各种针对硬件的相关接口实现。

USBPORT_REGISTRATION_PACKET在XP中分为2个版本:

#define USB_MINIPORT_HCI_VERSION_1            100
#define USB_MINIPORT_HCI_VERSION_2            200

typedef struct _USBPORT_REGISTRATION_PACKET_V1 {
...
} USBPORT_REGISTRATION_PACKET_V1, *PUSBPORT_REGISTRATION_PACKET_V1;

/*
    Miniport version 2 (current) api packet
*/
typedef struct _USBPORT_REGISTRATION_PACKET {
...
} USBPORT_REGISTRATION_PACKET, *PUSBPORT_REGISTRATION_PACKET;

根据版本不同,处理不同。

NTSTATUS
USBPORT_RegisterUSBPortDriver(
    PDRIVER_OBJECT DriverObject,
    ULONG MiniportHciVersion, //100 or 200
    PUSBPORT_REGISTRATION_PACKET RegistrationPacket
    )

在USBPORT_RegisterUSBPortDriver中不仅会重新备份ECHIC传过来的函数集,也会设置各个IRP的回调函数和AddDevice回调如USBPORT_PnPAddDevice。这样当一个USB主机控制器被系统识别后,会分别调用其对应的AddDevice创建对应的FDO,并附加到PCI创建的PDO上。
AddDevice中创建的设备名格式为\\Device\\USBFDO-x
在IRP_MJ_PNP的StartDevice的USBPORT_CreateLegacyFdoSymbolicLink中创建其链接名格式为\\DosDevices\\HCDx

2. 创建并管理集成在USB主机控制器里面的根总线的PDO

在IRP_MN_QUERY_DEVICE_RELATIONS请求中,当请求关系类型为BusRelations时,用于获取子设备。USB主机控制器通过其RootHubPdo指针指向根设备的PDO。

 if (devExt->Fdo.RootHubPdo == NULL) {
     // create a new root hub
     ntStatus =  USBPORT_CreateRootHubPdo(FdoDeviceObject,&devExt->Fdo.RootHubPdo);   
 }

USBPORT_CreateRootHubPdo创建的子设备的名称格式为\\Device\\USBPDO-X
USB主机控制器对于IRP_MJ_INTERNAL_DEVICE_CONTROL请求没有支持。
USB主机控制器对于IRP_MJ_DEVICE_CONTROL请求支持以下IOCTL

USBDIAG获取信息:

IOCTL 功能 实现机制
IOCTL_USB_DIAGNOSTIC_MODE_ON 启用诊断模式 设置设备扩展标志 USBPORT_FDOFLAG_DIAG_MODE
IOCTL_USB_DIAGNOSTIC_MODE_OFF 禁用诊断模式 清除设备扩展标志 USBPORT_FDOFLAG_DIAG_MODE
IOCTL_GET_HCD_DRIVERKEY_NAME 获取 HCD 驱动注册表键名 调用 USBPORT_LegacyGetUnicodeName,内部调用 USBPORT_UserGetControllerKey
IOCTL_USB_GET_ROOT_HUB_NAME 获取根集线器符号链接名 调用 USBPORT_LegacyGetUnicodeName,内部调用 USBPORT_UserGetRootHubName

Roothub Symbolic Link : USB#ROOT_HUB30#4&2dd4af6a&2&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}

3.跟4.就是今天的重点部分了,这两个部分也是 usbport.sys里面着墨最多的地方。我讲的顺序也是先说第三部分,再说第四部分。但是这两个部分并不是完全分开的,比如设备管理的时候可能涉及到要向某个设备发送一些urb。最典型的就是要set address,这里当然就必须由第四个部分来完成了,而且urb的处理也极大的依赖管理设备的时候所建立和维护的诸多数据结构。但是为了讲解方便,在说到第三个部分的时候总是假设usbport有某种方法能把某个urb发送出去并且完成,暂时不管他具体是怎么完成的。

3. 管理维护所有挂在当前这个主机控制器上的最多127个usb 设备

在前面说到usbport实现了两个interface以提供usbhub.sys调用,并在usbhub的帮助下来收集所有连接到总线上的设备信息。这里我们跳过这两个interface的具体说明以及调用方式调用时间等等信息,等到讲到usbhub的时候再回头来看这个过程,那个时候再来说究竟在什么时候在什么样子的情况下usbhub会调用哪个函数。如果没有调用上下文要去分析一个函数的功能是比较盲目的,这里大家了解这样一个规则:usbhub用某种方式得知了总线上有个设备连接进来了(比如你插入了一个u盘)?usbhub是通过调用USBPORTBUSIF_CreateUsbDevice 通知usbport这个事件发生了(函数参数信息可以查阅ddk)。下面得分析也就是从这个地方开始的。

在经过必要的参数检查测试以后,usbport为这个新报告的设备分配一个结构,用来保存必要的信息。下面是结构体的成员:

struct DEVICE_HANDLE // (sizeof=0X70)
{
    ULONG dwSignature; // signature = \'HveD\'
    USHORT DeviceAddress; // usb device addr (1-127)
    USHORT PortNumber; // port number
    ULONG RefCount; // reference count
    ULONG Tt; // for usb2.0
    PVOID ParentHandle; // parent hub device handle
    PCONFIG_HANDLE ConfigHandle; //
    PIPE_HANDLE PipeHandle; // for default pipe
    ULONG DeviceSpeed; // see ddk
    USB_DEVICE_DESCRIPTOR DeviceDesc; //
    UCHAR reserved; // padding,alignment
    ULONG DeviceFlags; //
    LIST_ENTRY DeviceHandleListEntry; // link all device handle together
    LIST_ENTRY PipeHandleListHead; // pipe handle list header
    ULONG TtCount; // for usb2.0
    LIST_ENTRY TtListHead; // for usb2.0
};

usbport 设置好某些域以后,开始open the default pipe。完成以后,发送一个get device descriptor的urb到这个新出现的usb设备用来填写DeviceDesc成员。成功以后把这个新创建的device handle添加到由USB主机控制器所维护的device handle list上去。具体流程参看下面的伪代码:

createdevice(parenthandle,portnumber,portstatus)
{
    检查参数是否合法

    为DEVICE_HANDLE分配内存handle

    handle->refcount = 0
    handle->confighandle = 0
    handle->parenthandle = parenthandle
    handle->portnumber = portnumber
    handle->deviceaddress = 0 // default usb address

    根据portstatus,设置handle->devicespeed
    根据devicespeed,设置默认 pipe 的maxpacketsize
    初始化默认 pipe 的 pipe_handle 的其它成员变量
    打开默认 pipe 的端点
    得到设备的描述信息 device descriptor
    将这个handle保存到 device handle list 链表中
}

在看open default pipe之前得先看看pipe handle这个结构:

struct PIPE_HANDLE // sizeof=0X20
{
    ULONG dwSignature; // signature = \'HpiP\'
    USB_ENDPOINT_DESCRIPTOR EndPointDesc;
    UCHAR reserved; // padding,alignment
    ULONG Flags // flags
    ULONG unknown;
    ENDPOINT* EndpointPointer;
    LIST_ENTRY ListEntry;
};

很简单的一个结构。

现在来看open endpoint这个函数:1. 先照样作一些检查,然后为endpoint结构分配内存。因为这个endpoint结构不仅仅是usbport使用,miniport也会使用。所有这个结构的大小是不固定的,miniport在注册自己的时候会指定自己所需要的结构大小,usbport在这个大小的基础上加上自己所管理的大小来决定要分配多大的内存。2. 然后usbport作很多的初始化工作,设置大部分endpoint结构的成员,必要的时候还需要miniport的帮助。3. 完成以后把这个pipe handle连接到device的pipe handle list header上面,同时把新创建的endpoint也连接到由fdo维护的全局endpoint list header上面去。

endpoint是一个很大的结构,0x160个字节(checked xp版本),篇幅原因不一一列举了,看几个与我们分析相关的成员:

struct ENDPOINT // sizeof=0X160
{
    ULONG dwSignature; // \'PEch\'
    PDEVICE_OBJECT FunctionDevice; // host controller fdo
    PDEVICE_HANDLE DeviceHandle; // device handle
    ULONG CurrentState; // endpoint state
    ULONG NextState; // hose two field will be protected by StateLock(see below)
    ULONG FrameNumber32Bit; // for iso,current frame number
    PWORK_ROUTINE WorkerRoutine; // core work routine
    LIST_ENTRY ActiveTransfer; // activer transfer list header
    LIST_ENTRY PendingTransfer; // pending transfer list header
    LIST_ENTRY CancelTransferList; // cancel transfer list header
    LIST_ENTRY AbortIrpList; // abort irp list header
    LIST_ENTRY EndpointListEntry; // link all endpoints together to fdo\'s list header
    LIST_ENTRY AttendLink; // see below
    LIST_ENTRY StateChangeListEntry; // endpoint state change list entry linked to fdo\'s list header
    LIST_ENTRY ClosedLink; // for lazy close,linked to fdo\'s list header
    LIST_ENTRY FlushList; // flush list entry,linked to fdo
    USB_LOCK ResourceLock; // spin lock for this struct
    USB_LOCK StateLock; // spin lock for endpoint\'s state
    KIRQL SavedResIrql; // save irpl
    KIRQL SavedStateIrql;
    COMMON_BUFFER* CommonBuffer; // for miniport driver COMMON_BUFFER*
    USHORT DeviceAddress; // device addr,copied from device handle
    USHORT EndPointAddr; // endpoint number
    ULONG MaxPacketSize; // max packet size
    UCHAR Period; // period
    ULONG DeviceSpeed;
    ULONG BandWidth;
    ULONG Offset; // schedule offset
    ULONG TransferType; //
    ULONG Direction; // in or out
    ULONG CommonBufferVir; // for common buffer
    ULONG CommonBufferPhy; // for common buffer
    ULONG CommonBufferLen; // for common buffer
    ULONG MaxTransferLen;
    USHORT PortNumber;
    PVOID ClientContextPointer; // point to miniport endpoint context
};

许多成员的作用就像他的名字所描述的一样,不过有几个特别提出要特别注意的地方。

首先是state,有两个成员表征了endpoint的state信息。state是用来描述当前endpoint的状态的,在不同的状态下面对于发送到endpoint的urb的处理方式是不一样,这个会在后面看到。

然后是诸多的list entry,他们大多数是用来作urb的排队的,后面也会一一看到用法。

workroutine 是一个函数指针,他的存在是因为发送到root hub的urb必须特殊处理,而不用转换成usb信号出现在usb总线上面。所有root hub对应的两个endpoint的workroutine是特别设计的,其他的endpoint对应的workroutine是一个通用的函数,这个 workroutine要作的工作就是处理urb。

至于那个common buffer,多数情况下是不是用来作传输用的。因为usbport所使用的dma是一个master而且还能scatter-gather的,所以大部分的urb都直接使用他自己的buffer本身占用的物理页,而不需要一个额外的copy操作,只有在某个buffer很不幸的跨越了两个不连续的物理页的时候才需要使用到这个common buffer作额外的copy操作。特别注意这个common buffer是提供给miniport driver使用的,而不是由usbport来使用的,miniport driver还会从这个common buffer里面提交一些内存来作额外的维护信息。

create device就到这里,基本操作就是分配device handle结构,分配endpoint zero 的结构,链接他们到各自对应的list header上面去。

device创建好了以后,usbhub再发出另外一个initialize device的请求。对于usbport来说这个请求唯一的处理就是为其分配一个地址,并且发出set address的urb。

完成这两步,这个device就能交付使用了。

使用的时候第一个步骤就是select configuration,device的驱动程序读取device的configuration descriptor,然后选择适当的interface构造一个select configuration的urb传递下来。中间的处理由usbhub完成,暂时忽略。假定这个urb到达了root hub的pdo,经过一系列的检查(这个部分会在urb的处理的时候再次提到),最终来到处理函数 USBPORT_SelectConfiguration。

首先为其分配一个config handle的结构:

struct CONFIG_HANDLE //sizeof=0X14
{
    ULONG dwSignature; // \'HgfC\'
    PVOID ConfigDescPointer; // ptr config desc buffer
    LIST_ENTRY InterfaceListHead; // link interfaces together
    USB_CONFIGURATION_DESCRIPTOR ConfigDesc;
};

很简单的一个变长结构(因为configurtation descriptor是变长的),实际上的configuration descriptor就跟在这个结构的末尾,从0ffset 0x10开始。当然,device handle结构的config handle成员指向了这个新分配的内存。

接下来发送一个set configuration的urb到设备。然后一一open 这个select configuration里面所描述的interface,并且填写将来要返回的USBD_INTERFACE_INFORMATION结构。填写 information结构是一个很简单的操作,所有必须的信息都在前面的步骤里面完成了,我们只是看看open interface完成的操作:

首先还是要分配结构:

struct INTERFACE_HANDLE
{
    ULONG dwSignature; // \'HxfI\'
    LIST_ENTRY ListEntry; // linked to config handle\'s interface list header
    ULONG HasAltSetting;
    USB_INTERFACE_DESCRIPTOR InterfaceDesc;
    UCHAR reserved[3]; // Padding db 3 dup(?)
    PIPE_HANDLE PipeHandle;
};

这个结构是变长的,结尾由若干个pile handle结构组成。个数当然由interface desc里面的endpointnumber指出。

结构分配完了,照例对各个成员进行初始化,然后调用open endpoint函数(如上所述)。最后再把这些个interface handle连接到config handle上面,工作就完成了…

客户驱动程序在这个select configuration urb返回的时候,就能查看USBD_INTERFACE_INFORMATION结构,一一了解各个自己感兴趣的东西。当然其中最最主要的就是所返回的 pipe handle,正如你所想象的那样,他就是一个指向PIPE_HANDLE结构的指针。客户驱动保存这个指针,因为随后的很多urb都需要你去填写这个 pipe handle成员,usbport也需要使用这个指针去寻找对应的endpoint结构。

select configuration完成以后,就能开始传输数据了,这也就是下来的内容了—- urb的处理。

本文链接为:http://www.usbzh.com/article/detail-1392.html ,欢迎转载,转载请附上本文链接。

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

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的内部实现细节。首先再重复一下:usbd 是USB1.1的类驱动程序,支持的是USB1.1usbport 是一个USB主机控制器的port driver,支持的USB2.0usbuhci是uh......
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 = ......
USBPort驱动概述
usbport.sys 将USB主机控制器和根集线器硬件抽象出来,提供统一的软件接口,是类驱动.usbehci.sys 是USB主机控制器的硬件部分,是mini小端口驱动usbechci.sys和usbehci.sys组成完整的usbechi 功能FDO驱动usbport.sys 是USB根集线器的......
关注公众号
  • HID人机交互
  • Linux&USB
  • UAC音频
  • CDC
  • TYPE-C
  • USB规范
  • USB大容量存储
  • USB百科
  • USB周边
  • UVC摄像头
  • USB资源
  • Windows系统USB
  • 音视频博客
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

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

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