Windows XP下usbhub.sys驱动内部实现解析

Windows XP USB启动安装全攻略:驱动注入原理与实战 操作系统安装的核心在于引导程序对硬件驱动的识别与加载。Windows XP等早期系统在安装阶段,其文本模式安装环境默认不包含USB大容量存储驱动,导致无法直接从USB设备启动安装。通过修改安装源文件中的关键配置文件,如TXTSETUP.SIF和DOSNET.INF,可以将USB存储驱动(如usbstor.sys)的加载时机提前至系统引导的初始阶段,这一过程被称为“驱动注入”。这项技术的价值在于突破了原生安装程序的限制,使得为老旧工业设备、嵌入式系统或特定兼容性测试创建便携式、隔离的Windows XP工作环 阅读详情

http://hi.baidu.com/jlgwxq/blog/item/6ac02b417362ad1873f05de9.html

Windows XP下usbhub.sys驱动内部实现解析
2010-01-25 13:12
说过了usbport.sys驱动,现在来说说usbhub.sys驱动。

usbhub是一个usb hub device的驱动程序。当kernel枚举到某个usb host controller的时候,usb host controller就会创建一个pdo,这个pdo代表了跟host controller关联到一起的root hub,windows为这个pdo加载的驱动程序就是usbhub.sys。

usbhub致力于usb设备的枚举、pnp管理、电源管理这些事务,而实际的usb的数据信息传输则是由usbport来完成的,所以在usbhub这个驱动程序里面并没有多少usb 2.0跟usb 1.1的差别。

usbhub主要完成的功能,也就成了今天这个文章里面最主要的内容。列一个表,讲解的顺序也就如下:

1.DriverEntry
2.AddDevice
3.pnp for hub fdo
4.pnp for pdo
5.internal io control dispatch
6.power dispatch

上面的顺序也几乎是一个sys驱动里面的routine所被调用的顺序,重点放在pnp跟power上面。而pnp的部分又被分成了两个一个是for fdo的,一个是for pdo的,重点中的重点是fdo的query bus relationship的处理,而其他的比如start,stop,remove这些只是稍微提一下,而for pdo的部分,query id、query text、query caps这些算是比较重要的处理,但是这些处理的过程都是很显而易见的。至于power部分,遵循windows的通用power处理模型。internal io control则是对外的接口,大部分的usb device的请求都是用过它来进行了。这里简单的列一个表,说明大部分的io control的处理语意、简单流程、完成的功能,这里还要涉及到部分usbport的功能。

1. DriverEntry

这个简单,设置了大多数的dispatch routine、driver unload、add device,然后query了部分reg config,然后为usb的serial number generation作了些准备就return。至于都设置了哪些dispatch routine,如下所列:

create 简单完成
close 简单完成
device control 面向user mode的那些诸如get node info请求
internal device control 面向kernel mode的请求,下面详细介绍
pnp 下面详细介绍
power 下面详细介绍
system control 交给wmilib完成

至于什么是serial number generation,以及主要致力于在这个generation过程中所分配的资源的释放问题的driver unload routine,会在说到这个serial number的时候交代。

2. AddDevice

当系统枚举到一个usb hub的pdo的时候,加载usbhub.sys,然后在适当的情况下调用这个函数。这个函数作的工作很简单,无外乎就是创建一个fdo,然后初始化他的device extension的诸多成员,usbhub同时在这里初始化了wmilib的调用信息,为自己的fdo的wmi注册。

3. pnp for fdo

完成了adddevice调用以后,kernel就会开始发送pnp的irp到usbhub,这些irp都是为这个新创建的fdo准备的。

前导的query caps这个很简单,设置一个完成routine,然后传递下去。在complete routine里面设置了Surprise Remove OK,这个设置会阻止safe removal hardware对话框以及任务栏图标的出现,特别注意一点:它是为hub本身这个设备而设置的,对于连接到hub上的设备来说,设置还是不设置这个标记的决定权在于那个设备所对应的fdo的驱动程序(当然还有filter do)。

接着是start,在这个irp里面,首先把这个irp传递下去,等待lower driver 完成这个irp,然后hub再完成自己的初始化,诸如获取root hub的pdo、获取device descriptor、获取hub descriptor、获取configuration descriptor、计算自己用于remote wakeup的power state、获取由usbport提供的USBHUB跟USBD两个interface以备将来使用。接着hub 发出一个select configuration的urb,等待完成,保证必要的结果。接着分配一个重复使用的irp(通过在complete routine里面返回more process required),并利用自己的device extension的空间构造一个用于interrupt transfer的urb,同时为这个urb分配必要的输入输出缓冲区。接着hub初始化他的每个port,然后发出这个interrupt urb。

题外话,这个urb是用来干什么的?按照usb的规范,hub除了默认的用于control transfer的endpoint0以外,还有一个用于interrupt transfer的endpoint,这个endpoint用来通知host有一个变化发生到了hub或者hub的某个port或者某些ports上面。它的输出情况是一个bitmap,这个bitmap的每一个位代表了发生变化的port编号,而第0位代表了hub本身。比如,如果interrupt transfer返回的数据缓冲里面的2进制值是00101就表示hub本身和端口2(从1开始计算)发生了变化,当host解析这个数据以后就应该对发生变化的hub或者端口发出get status请求来得到具体是发生了什么样子的变化,从而决定应该作什么样的操作。一般的情况下,一个比较智能的hub会用nak回答这个interrupt,直到实际上检测到了变化为止,而不是返回一个全0的bitmap,对于nak的transfer,usb host controller的miniport driver会自动的重试。这个urb的处理是整个usb fdo处理的核心部分,hub枚举连接到其上的设备也是依靠它来完成的,这个先放一放,下面会有专门的部分讲解。

接下来的irp就不一定有顺序了,我们一个一个的来看吧:

query remove
cancel remove
query stop
cancel stop
这些都是为pnp状态变化作准备的,都设置了Irp->Status = success,然后传递给下层驱动了。

stop
首先检查连接到hub上的device有没有被stop(windows在stop hub的时候会首先发送stop到这些device去),如果没有,就表示这个device的driver没有正确的处理,stop这个请求,hub主动的stop掉这些device对应的pdo,接着hub作一系列的cleanup工作。诸如:cancel掉自己的wait wake power irp,cancel掉自己的idle request irp,用delete pending 完成连接到自己上面的device所发出的wait wake irp以及idle notification irp,abort掉自己在start里面发出的urb,free为这个urb所分配的irp,以及输入输出缓冲区,然后通知usbport 那些为连接到这个hub上的device所创建的device handle应该要被删除了(通过在start的时候所获取的interface完成)。disable 掉自己所有的port,释放为自己的device descriptor、hub descriptor、configuration descriptor所分配的空间,然后发出一个设置了null configuration descriptor的select configuration urb来unconfig 这个hub。最后,当然这个irp传递给了下层驱动。

surprise removal
为什么这个放到这么前面来说?因为大多数情况下在remove之前都会遇到它。当某个设备在某次query bus relation的时候没有被返回,kernel就会在恰当的时候发送它到那个消失了的device去,而当这个device open handle变成0(不是pointer count)的时候,kernel再发送remove到这个device。。。这个部分可以参考ddk的描述。fdo的处理很简单,设置status = success,然后传递下去。

remove
跟stop差不多,首先remove掉那些连接到自己上的device并且没有处理remove操作的pdo(所有连接到自己上面的pdo都被放入了一个数组里面,如果处理了remove irp,pdo会把自己从这个数组里面删除,这样fdo只用遍历这个数组就知道哪些pdo还没有处理remove操作了。另外,在创建pdo的时候,也就是在query bus relation的时候,pdo被加入到了这个数组里面)。然后hub作如同stop一样的clean操作,因为按照规范,stop并不会作为remove的前导irp发送,所以完全可能一个处于start状态的设备在没有收到stop的情况下收到remove请求。当然hub保留了一个标志在其device extension里面,以避免对一个已经完成了clean操作的设备再作第二次clean操作。clean完了以后,hub取消fdo的wmi的注册,然后传递remove下去,然后deattch自己,然后delete自己。

关于hub fdo的start、stop、remove就如上所述。hub fdo除了处理这些状态变迁的pnp irp以外(当然还有query caps和query relation),还处理了一个pnp irp,就是query device state。hub fdo根据自己保存的当前状态(hub的状态变化会通过上面那个interrupt urb返回)来回应query device state。

下面就是重头戏了,query device relation的处理。正如大多数的bus 的fdo一样,只是处理bus relation。(注:大多数bus的fdo处理bus relation,而bus枚举出来的pdo处理target relation。usbhub也是这样,而其他的relation,据我跟踪到的情况,pci会为有着multi function的device处理remove relation。)

先说这个irp会在什么时候被发送。kernel会在start以后发送这个irp,kernel还会在某些特别的情况下发送这个irp,比如当query device state返回了那些错误的值的时候。但是大多数情况是因为某个地方用busRelation作为参数调用了IoInvaliateDeviceRelations函数而引起kernel发送这个irp。usbhub也是一样,这里先回到上面这个那个urb的处理上来。

当hub硬件检测到某个port发生了状态变化的时候,这个urb被完成,usbhub设置的complete routine被调用,usbhub读取发生变化的port的状态,发现了是属于一个连接状态变化(connection change),也就是说或者是一个设备被连接了进来、或者是一个设备被拔了下来,usbhub就会调用 IoInvalidateDeviceRelations函数。(实际上的处理并不只是port的connection变化处理,还有hub状态变化、以及port的其他状态变化,比如reset完成、resume信号被检测到等等处理,而且按照规范,还会通过control pipe发送clear feature的request来应答硬件所报告的这个变化情况)。

hub的fdo收到了这个irp以后,遍历自己的每个port,获取其状态,根据其状态作进一步处理。代码:

queryBusRel
{
for(i = 0;i < NumberOfPorts; i ++)
{
get port i + 1 status // base is port 1
if(status & connected)
{
if(fdoExt->portData[ i ]->pdo)
{
ref the pdo
}
else
{
reset port i + 1
create pdo,do some initialization (*)
ref the new created pdo
fdoExt->portData[ i ]->pdo = new created pdo
}
}
else
{
if(fdoExt->portData[ i ]->pdo)
{
pdoExt->ReportMissing = TRUE
free pdo\'s serial number string
tell usbport to remove pdo\'s device handle
disable port i + 1
}
fdoExt->portData[ i ]->pdo = 0
fdoExt->portData[ i ]->connect = notConnected
}
}
pBusRel = ExAllocatePool()
pBusRel->Count = 0
for(i = 0; i < NumberOfPorts; i ++)
{
if(fdoExt->portData[ i ]->pdo)
pBusRel->DeviceObject[pBusRel->Count ++] = fdoExt->portData[ i ]->pdo
}

pIrp->IoStatus.Information = pBusRel
}

大致的处理就如上,逻辑上是非常简单的。下面来看上面标记有*的地方的处理,这个是hub为连接到其上的device作的创建初始化工作。

createPortDevice
{
generate a name(such as usbpdo-2)
IoCreateDevice
Device->StackSize = roothubPdo->StackSize
use port number to build instance string
tell usbport to create new device handle
save the device handle to the pdo\'s extension
reset port,e.g. reset the device
tell usbport to init the device(allocate a usb addr)
get the device,configuration desc for the device

if the device reports a serial number string
verify the reported serial number string
if verify failed or the device didn\'t report
if serial number string generation isn\'t disabled
generate a serial number string for the device

special check the new device is a hub or not
if the device is a hub
save the only one interface descriptor to pdoExt
}

大部分的工作都是由usbport来完成的,主要就是用于usbport维护device handle的数据结构的创建,分配usb address。然后按照需要为这个usb device生成一个serial number string,这个serial number string被用在了两个地方:一个是用来回应Query instance id请求,一个是用来设置caps的UniqueId成员。它是使用一个逐次增加的数字来生成这个string。usbhub为每个设备记录一份数据,里面放置有vendor id跟product id还有device object:

struct SERIAL_NUMBER_TABLE_ENTRY // sizeof=0XC
{
ULONG idVendor;
ULONG idProduct;
ULONG PhysicalDevice;
}

然后把这些entry组成一个数组,每次生成string的时候都遍历这个数组,查找具有相同的vendor id和product id并且PhysicalDevice非空的元素并计数,直到找到一个PhysicalDevice为空(以前有,然后被删除了)或者整个数组都遍历完了为止。然后用计算出来的数字生成一个Inst %d的string作为他的serial number string,上面这个数组是动态分配增长的,所以在driverunload里面进行释放工作 。

最后注意一点,新设备的stacksize是跟roothub的pdo的stack size相同的,这是因为发送到pdo的irp要么被完成了(比如create、close、power、wmi、pnp)要么被传递给了root hub pdo(也就是internal io control)。那些被传递给root hub pdo的internal io control不会再往下传递了,所以这里并没有使用rootHubPdo->StackSize + 1这个数字,因为+1是不需要的。

注意这个事实:internal io control是直接传递给root hub pdo的,而不是传个fdo,然后再pdo再fdo再pdo这样的方式。如下图:

root hub pdo
|
+---root hub fdo
|
+----hub A pdo
|
+---hub A fdo
|
+---device pdo
|
+--device fdo

一般的讲,urb在device fdo的地方构造,然后用internal io control传递给device pdo,它是由 hub A创建的,device pdo直接把这个irp传给了root hub的pdo,而不是交给hub A fdo,然后再交给hub A pdo.....这个在说到internal io control的处理的时候就会知道原因。接下来要说的就是为这个新创建出来的pdo所发送的pnp irp。

4.pnp for port device pdo

query caps
rawDevice = 0, removable = 1
如果serial number generation is enabled,uniqueId = 1
如果wakeup is supported,DeviceD1 = DeviceD2 = WakeFromD0 = WakeFromD1 = WakeFromD2 = 1
然后根据fdo的caps在start的时候所计算的device power state map设置pdo的caps里面的device power state map
最后完成这个irp

start port pdo
创建device interface,如果是hub则是_GUID_DEVINTERFACE_USB_HUB,否则则是 _GUID_DEVINTERFACE_USB_DEVICE。然后按照需求进行restore(多发生在stop了以后再start的情况,会重新通知 usbport创建device handle(stop会remove掉这个device handle),还会进行reset),然后注册pdo的wmi,最后完成这个irp。

query remove
cancel remove
query stop
cancel stop
直接用success完成irp。

stop
complete wait wake irp,complete idle notification request irp,告诉usbport remove 这个pdo的device hanlde(start会重新创建这个device handle)。然后disable掉pdo连接的port,然后删除start的时候注册的device interface,最后完成这个irp。

surprise removal
删除device interface然后完成这个irp

remove
complete wait wake irp,complete idle notification irp,删除device interface,disable port,remove device handle,清除掉fdoExt->portData数组里对应的pdo指针和connect成员。释放掉serial number的内存,取消pdo wmi的注册,最后delete device,完成irp。

query device relation
target relation,简单到不能再简单

query interface
因为usbport对于_USB_BUS_INTERFACE_USBDI_GUID这个interface要求必须设置interface结构的InterfaceSpecificData为device handle,所以usbhub设置了这个成员然后传递给root hub pdo。这里是第一次看到usbhub为某个request设置device handle,后面还会看到internal io control的request也会设置device handle成员。正是因为usbhub无条件的设置这个device handle,所以irp直接传递给root hub的pdo的,一来能节省不必要的传递过程,二来因为usbhub这种强制的设置功能。如果传递给fdo再pdo的话,device handle总是被usbhub用pdo的device handle修改,这样device handle成员就发生了变化,这是不允许的。

query bus information
返回 GUID_BUS_TYPE_USB

query resource requirement
这个irp的处理有点奇怪,都知道usb设备是不需要硬件资源的,usbhub的处理方式只是设置了一个标记。因为kernel在为pdo创建device key的时候会发送一系列的irp到pdo,只有在这些irp都成功了以后,kernel才会创建或者更新device key,这个irp就是其中一个。从源代码上来看,它并不算是最后一个,但是确是这一系列的irp中只发送一次的一个。只有在这个irp发送完成了以后,诸如IoOpenDeviceRegistryKey这些函数获取到的信息才真实的反应了当前这个设备的信息。正如你想的那样,有地方要依靠它们的存在。

query device state
如果fdo的处理一样,根据pdo的状态回应这个query irp

query device text
用irp里面的LCID作为language id,然后用device descriptor里面的iProduct作为string index发送get string descriptor request到usb设备。如果成功了则返回,否则使用使用English (United States) = 409h作为language id再次get string descriptor,如果还是失败,则返回generic usb string,它是在driverEntry里面从注册表里面读出来的。

query device id
这个处理过程就没有任何好多说的了,也就是用那些idVendor、idProduct生成各种id,利用device tree可以查看到usbhub为pdo生成的各种id string。

pnp的处理就到这里,接下来的是internal device control的处理,这个也是usb设备的接口。

5. internal device control

再次提到device handle这个东西,传递给usbport的urb必须要设置URB_HEADER->UsbdDeviceHandle成员。很明显这个是由 usbhub创建的pdo来完成的,正如上面看到的那样,pdo在通知usbport创建device handle的时候保存这个device handle到了device extension里面,所以pdo会从自己的device extension里面取得这个device handle并用它来设置URB_HEADER里面得成员。这也是为什么urb只是经过一个pdo以后就交给了root hub pdo,因为如果交给了另外一个hub创建得pdo的话,新的pdo会覆盖原来或许本来已经设置正确了的device handle,这当然不行了。也正是这个原因,internal device control最先被处理的应该是hub创建的pdo,而不应该传递给hub的fdo,所以下面的都是pdo的处理。这里列个表,来描述各个internal device control的处理情况。

5.1 submit urb 220003h

usbhub首先根据当前pdo的状态(是否已经start、是否是位于d0状态等等)适当的failed掉这些状态的urb。如果当前的pdo状态是能够接受urb的状态,则继续。

然后检查URB_HEADER->Function对几个特别的function作额外的处理,这些要特别处理的Function是:
select configuration
select interface
control transfer
interrupt transfer
iso transfer
get ms feature descriptor
中间的3个transfer会检查ReportMissing状态(查看上面QueryBusRelation的处理),适当的fail这个urb。最后一个ms feature descriptor则完全是有usbhub来完成的,详细的信息可以看这个网页http://www.sourcequest.com/modsContent.htm。对于select configuration的情况,如果是config device,则要对configuration descriptor作一次validate,具体也就是检查他的bLength、bDescriptorType、wTotalLength 作一次检查,然后检查在configuration descriptor设置的MaxPower是否大于hub能提供的PerPortPower,如果也通过了,则设置一个complete routine,然后传递给root hub pdo。

对于select interface也设置了相同的complete routine。在这个complete routine里面主要是处理带宽不够的情况,设置一个timer,用来记录这个wmi事件。使用这个timer是基于这样的事实:客户端的驱动在遇到select configuration和select interface返回带宽不够的时候重试urb,因为如果在重试成功了以后就会cancel这个timer。最终这个urb被传递给了root hub pdo,交由usbport进行排队、传输、完成。

5.2 reset port 220007h

这个的语意是reset device所连接到hub对应的port,也就是说是用来reset device的upstream port,实际上就是发送reset信号到device去。

resetUpStreamPort
{
tell usbport to mark device handle busy
tell usbport to remove device handle but keep the struct data
send reset request to hub hardware
tell usbport to create new device handle
send reset request for set usb address
tell usbport to allocate a new address for the device
tell usbport to use the new created device handle to reinitialize old device handle
tell usbport to remove the new created device handle
}

5.3 get root hub pdo 22000fh

直接交给root hub pdo,root hub pdo会返回两个指针,虽然msdn说一个是root hub pdo,另外一个是host controller fdo,但是实际上两个都是指向了root hub pdo。不过这个地方很奇怪,hub已经保存有这两个指针,为什么不用自己保存的指针填充这个结果而要交给root hub处理呢?

5.4 get port status 220013h

usbhub处理,发送control urb到hub硬件获得这个port status,然后返回。

5.5 enable port 220017h

usbhub处理,同上。

5.6 get hub count 22001bh

usbhub、root hub都处理,usbhub 先把count += 1然后另外发送一个get hub count到自己的下层驱动,把返回的结果加在count上,返回。为什么要新创建一个urb下传呢?因为原先的irp的stack size的数目不够。

5.7 get hub name 220020h

usbhub处理,返回hub的symlink name。

5.8 get bus info 220420h

通过interface的方式交由usbport处理完成。

5.9 get controll name 220424h

同上

5.10 get parent hub info 22042ch

usbhub pdo返回自己的parent fdo对应的pdo,以及自己的port number。

5.11 get device handle 220423h

usbhub pdo返回保存在自己device extension里面的device handle。

5.12 cycle port 22001Fh

模拟一次unplug and plug event,设置fdoExt->portData[portNumber]->pdo,然后IoInvalidateDeviceRelations。对比上面的代码就知道会新创建一个pdo,而kernel会发生surprise removal以及remove pnp irp到原先的那个pdo完成删除工作。

5.13 notification 220027h

这个稍微有些复杂,因为他是和power management关联在一起的,说完power management再回头来看它的处理过程。

 

6. power dispatch

这个部分处理很有模版化的趋势,先看fdo的处理:

query power

FdoQueryPower
{
if(bQuerySystemPower)
{
newDeviceState = map system state to device state
}
if(newDeviceState == D3)
{
if(fdo had submitted wait wake irp)
cancel it // because we are going to d3
}
PoStartNextPowerIrp
PoCallDriver
}

wait wake

FdoWaitWake
{
set complete routine = FdoWaitWakeComplete
PoStartNextPowerIrp
PoCallDriver
}

wait wake complete

FdoWaitWakeComplete
{
if(irp->IoStatus->Status == STATUS_SUCCESS)
{
request power irp to set device state to D0
// in the complete routine,complete children port pdo\'s
// wait wake irp with STATUS_SUCCESS
}
else
{
complete all children port pdo\'s wait wake irp with error
}
}

set fdo system power state

FdoSetSystemPowerState
{
if(newSystemState == S0)
{
if(currentDeviceState != D0)
{
save this irp
Request set device power d0 Irp
return pending

// in the complete routine,call down the saved set system power irp
}
start next
call down
}
else
{
if(some one need wait wake)
// hub or device that connected to the hub
{
newDeviceState =map system state to device state
}
else newDeviceState = D3

if(prevDeviceState == newDeviceState)
// didn\'t need to change device state
{
start next power irp
call down
}
else
{
save the set system power irp
request set device power state irp
return pending

// in the complete routine,call down the
// saved set system power irp
}
}

fdo set device power irp

FdoSetDevicePowerIrp
{
if(newState == prevState)
{
start next call down return
}

// is not the same
if(newState != D0)
{
cancel interrupt transfer
}
set complete routine = FdoSetDeviceStateComplete
call down
}

set fdo device state complete

FdoSetDeviceStateComplete
{
if(irp->IoStatus.Status != STATUS_SUCCESS)
{
start next
return success
}

if(newState == D0)
{
if(prevState == D3)
{
query work item = FdoSetPowerD0Worker
save this irp
return more_process_required
}
submit interrupt transfer
start next
}
return success
}

fdo set power d0 worker

FdoSetPowerD0Worker
{
power on all ports
flush changes when the hub was powered off
invalidate bus relation
submit interrupt transfer
start next
complete this power irp
}

pdo的power处理相对简单很多

pdo power dispatch

PdoPowerDispatch
{
if(minor == WAIT_WAKE)
{
return PdoWaitWake
}

if(pdoExt->ParentFdoExt->CurrentDevicePowerState != d0)
{
if(minor == set || minor == query)
{
request set parent hub d0

// complete = PdoSetHubD0ForPowerProcess
return pending
}
}

switch(minor)
{
case query
return PdoQueryPower
case set
return PdoSetPower
default
status = pIrp->IoStatus.Status
start next
complete
break
}
return status
}

PdoSetHubD0ForPowerProcess
{
switch(minor)
{
case query
PdoQueryPower
case set
PdoSetPower
default
start next
complete
break
}
return success
}

pdo wait wake

PdoWaitWake
{
set cancel routine
save this irp

if(!pdoExt->ParentFdo->WaitWakeIrpSubmitted)
{
call PoRequestPowerIrp to request a wait wake power irp to the ParentFdo stack
start next
return pending
}
}

pdo query power

PdoQueryPower
{
if(bQuery == query device state)
{
start next
return SUCCESS
}

newDeviceState = map system state to device state

if(newDeviceState != D3)
{
start next
return success
}

// here new device state = D3
if(!pdoExt->WaitWakeIrp)
{
start next
return success
}

// here new state == D3,and has a wait wake irp pending
if(newSystemState >= PowerSystemHibernate)
{
start next
return success
}

// device new state is d3,this state can\'t wake system
// and the system go to a wakable state
// so fail it
return STATUS_UNSUCCESSFUL
}

pdo set power state

PdoSetPowerState
{
if(bSetSystemPowerState)
{
start next
complete success
return success
}

if(newState == prevState)
{
start next
complete success
return success
}

switch(newState)
{
case D0:
if(prevState == D3)
{
if(pdo is suspended)
resume it
else if(pdo is power down)
power it

check port status
if(status & connected)
{
tell usbport to reinit device handle
}
}
else
{
send clear suspend request
complete idle request
clear remote wakeup feature
}
break
case D1
case D2
if(prevState == D1 || prevState == D2)
{
start next
complete success
return success
}
if(pdoExt->WaitWakeIrp)
{
// set hardware to support remote wake
enable port
set remote wakeup feature
}

tell hardware to suspend port
break
case D3
complete idle notification with status = POWER_STATE_INVALID
complete wait wake with status = POWER_STATE_INVALID

if no one request wait wake
cancel parent hub\'s wait wake irp
close port
break
}

start next
complete success
return success
}

处理的方式都是显而易见的,遵循ms的power处理的规范,没有什么特别注意的地方,查看msdn就能找到这个处理流程的描述。唯一一个让我觉得奇怪的是它并没有按照msdn所描述的那样调用PoSetPowerState函数,而且根据我跟踪分析的大多数驱动比如pci.sys、usbstor.sys、hidclass.sys、usbhub.sys这些都没有调用PoSetPowerState函数...令我觉得很是奇怪。

再回头来看idle notification这个request,至于这个idle request是用来干什么的,改设置什么参数,参考msdn。usbhub对这个的处理方式很直接,所以发送到pdo的idle notification request都设置一个cancel routine,然后保存起来。在某些地方检查整个hub是否进入了一个idle状态,if and only if所有的port上的设备都发送了idle notification的irp的时候,hub进入了idle状态,于是hub 回调每个port所提供的回调函数,然后往自己的parent hub发送一个idle notification request。而这个idle notification irp的完成,当然是在hub自己的idle request完成的时候,一一完成自己的每个port上的idle request。

usbhub的就到这里... (结束)

Windows 2000 USB驱动完整安装包:INF配置与微端口驱动深度解析 USB驱动是操作系统与物理外设通信的基础技术,其核心原理在于硬件抽象、协议栈匹配与内核服务注册。在Windows 2000这类早期NT内核系统中,USB支持高度依赖INF配置文件的精确语法和微端口驱动(如usbport.sys、usbd.sys)的版本一致性,任何偏差都将导致Error Code 10、28等典型故障。该技术具有显著的工业现场价值——支撑老旧工控设备、医疗仪器及嵌入式教学平台的持续运行,尤其适用于无法升级却必须接入现代USB传感器、串口模块或HID设备的严苛场景。本文聚焦Windows 20 阅读详情

相关推荐

Windows XP平台USB驱动开发完整实战项目

简介:USB驱动程序是Windows XP系统中实现USB设备通信的关键组件。本文档详细介绍了在Windows XP环境下开发USB驱动的技术流程,涵盖USB协议基础、驱动模型选择(WinUSB与WDM)、开发环境搭建、驱动代码编写、INF注册、测试调试及数字签名等核心环节。通过本项目实践,开发者可掌握USB主机控制器、功能驱动与设备驱动的协同机制,深入理解PDO、FDO、IRP等WDM核心概念,并具备开发稳定、兼容性强的USB驱动的能力,适用于工业控制、嵌入式设备等场景。

weixin_35696112的博客 969

USB驱动笔记

总线,USB,驱动

abc353940924的博客 1397

华为C8650台式机驱动安装与刷机实战指南

华为C8650或许已经退出历史舞台,但它所承载的技术范式——复合设备、动态枚举、厂商私有协议——依然是今天智能设备通信的基础。掌握它的驱动机制,不仅是为了解决一次连接问题,更是为了理解软硬件协同的本质。下次当你面对一台“无法识别”的设备时,不妨想想:- 它上报了哪些VID/PID?- INF文件是否匹配?- 系统是否阻止了未签名驱动?- 是否有旧驱动残留干扰?有了这套思维框架,别说C8650,就算是二十年后的复古设备,你也照样能搞定 😉。

weixin_32306683的博客 1010

USB 通用父驱动程序 (Usbccgp.sys)

USB 通用父驱动程序 (Usbccgp.sys) 本节介绍 Microsoft 为复合设备提供的 Usbccgp.sys 驱动程序。 许多 USB 设备提供了多个 USB 接口。在 USB 术语中,此类设备被称为复合设备。Microsoft Windows 2000 和 Windows 98 操作系统的 USB 总线驱动程序 (Usbhub.sys) 中包含有一个通用父设备,它将复合设备

M_O_Bz的专栏 5043

Win2000 USB驱动安装详解:usb.inf解析与兼容性迁移

USB驱动安装是设备与系统通信的基础,其核心在于INF文件对硬件ID的匹配与驱动服务的注册。在Windows 2000的WDM模型下,usb.inf通过[Version]、[Manufacturer]和[Services]等段落定义设备识别与加载逻辑,而硬件ID的精确匹配和数字签名处理直接影响安装成败。掌握这些原理,不仅能解决老设备在legacy系统中的驱动问题,还能应对从Win2000到Win7/Win10迁移时的兼容性挑战,例如平台扩展名适配和签名强制。本文深入拆解usb.inf结构,结合手动安装、签名

weixin_33146151的博客 151

USBDeview:全面的USB和COM端口管理工具

USBDeview是一款小巧实用的工具,专为Windows操作系统设计,用以列出系统中连接的所有USB设备的详细信息,并提供了一些管理USB设备的实用功能。这款工具可以帮助IT专业人员和普通用户更加直观地查看和管理USB设备,包括设备的状态、安装日期、硬件ID等重要信息。

weixin_31688273的博客 1944

windows USB 设备驱动开发-Host端和Device端

windows Host端设备驱动和Device设备驱动

王马的博客 1606

Ubuntu20.04/Debian10安装向日葵远程控制踩坑实录(附依赖问题解决方案)

本文详细介绍了在Ubuntu 20.04/Debian 10系统上安装向日葵远程控制软件的全过程,重点解决了常见的依赖问题,特别是libwebkitgtk相关错误。通过自动修复、手动修改.deb包依赖和使用兼容层三种解决方案,帮助用户顺利完成安装并优化远程控制体验。

silver的专栏 749

USB学习之二:USB总线驱动程序

USB总线驱动程序的作用 1. 识别USB设备 1.1 分配地址 1.2 并告诉USB设备(set address) 1.3 发出命令获取描述符 描述符的信息可以在include\linux\usb\Ch9.h看到 2. 查找并安装对应的设备驱动程序 3. 提供USB读写函数 将一个USB设备接到开发板上,看输出信息: usb 1-1: new full speed USB device ...

u012830148的博客 2565

Word毕业论文排版:图片和表格题注的终极指南(含自动编号技巧)

本文是Word毕业论文排版中图片和表格题注的终极指南,详细讲解了如何利用题注功能实现自动编号,告别繁琐易错的手动操作。核心内容包括创建定制标签、配置带章节的编号格式、插入交叉引用以及一键更新全文编号的技巧,旨在帮助学生构建自动化文档体系,提升排版效率和论文严谨性。

weixin_29211309的博客 1094

39文章解读与程序——EI\CSCD\北大核心《基于两阶段鲁棒优化算法的微网多电源容量配置》已提供下载资源

电网论文源程序擅长文章解读,论文与完整源程序,等方面的知识,电网论文源程序关注python,机器学习,计算机视觉,深度学习,神经网络,数据挖掘领域.电网论文源程序擅长文章解读,论文与完整源程序,等方面的知识,电网论文源程序关注python,机器学习,计算机视觉,深度学习,神经网络,数据挖掘领域.《《《《《《《《更多资源还请持续关注本专栏》》》》》》》《《《《《《《《更多资源还请持续关注本专栏》》》》》》》论文与完整源程序_电网论文源程序的博客-优快云博客。电网论文源程序-优快云博客。

LIANG674027206的博客 1208

第十一篇:万丈高楼平地起-驱动编程基础知识点(三)

1. //     // Initialize DevicePowerState to PowerDeviceD3. PowerDeviceD3 is the lowest     // power state for hardware. ToasterAddDevice initializes DevicePowerState to     // the lowest power sta

1630

保姆级教程:在RK3588开发板上编译带MPP硬件加速的FFmpeg(含完整依赖库配置)

本文提供在RK3588开发板上编译支持MPP硬件加速的FFmpeg的详细教程,涵盖交叉编译环境搭建、MPP框架及依赖库配置,最终实现4K视频硬件编解码性能优化。通过实战步骤和性能对比,展示如何充分发挥RK3588的硬件加速能力。

weixin_30839881的博客 418

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

http://hi.baidu.com/jlgwxq/blog/item/e54543100b5cb7c5c2fd7860.html Windows XP下usbport.sys驱动内部实现解析(四) 2010-01-22 18:16 (接上一篇) 然后来看看实际上的work routine,先看通用的这个。root hub的是一个特殊的函数。 USBPORT_DmaEndpoi

killcpp的专栏 629

【win11】主板更新之后pin不可用,恢复方法

台式机的主板更新了BIOS,再开机竟然登录不进去系统了。 登录界面上显示:【由于此设备上的安全设置已更改,你的pin码不再可用。单击以重新设置pin】 点击【单击以重新设置pin】没有任何反应。 也没有密码登录选项。 在网上找到解决方法,不用PE,只需要按步骤操作即可。通过实际验证可行,现记录如下。

1万+

初学树莓派——(九)DHT11数据传入ONENET

目录 前言 DHT11实现本地温湿度监测 DHT11.py 接线图 效果图 温湿度数据上传至ONENET 新建产品 新建设备 ONENET.py 测试 Shell脚本配置 效果图 前言 本文分部分实现温湿度上传至OneNet云平台,本地数据监测,数据上传 新建一个文件夹,文件夹内包含DHT11.py、ONENET.py、tmp_data.txt、hum_data.txt DHT11实现本地温湿度监测 DHT11.py import RPi.GPIO as GPIO

会点灯的大力水手的博客 2611

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

http://hi.baidu.com/jlgwxq/blog/item/e0f223312c0aa548ac4b5f67.html  Windows XP下usbport.sys驱动内部实现解析(二) 2010-01-22 18:15 (接上一篇) 4. 处理USB请求(URB) 在进入下一个主题之前我总结几个事实让大家注意: 1. 对于每个usb总线上的设备,usbpo

killcpp的专栏 716

携程机票数据爬取实战:使用 Python 获取携程网机票信息

通过本篇博客,我们详细介绍了如何使用 Python 爬取携程网的机票信息,涉及了静态网页爬取(使用 Requests 和 BeautifulSoup)和动态网页爬取(使用 Selenium)两种方法。通过获取的数据,我们不仅能够进行票价分析,还可以构建更为复杂的预测模型或趋势分析工具。爬虫技术不仅能够帮助我们收集有价值的数据,还能够为商业决策提供数据支持。在实际应用中,我们可以结合更多的分析方法,如自然语言处理、图像处理等,进一步提升数据的价值。

2201_76125261的博客 1657

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

http://hi.baidu.com/jlgwxq/blog/item/a3bcc30356a3cc057aec2c64.html Windows XP下usbport.sys驱动内部实现解析(一) 2010-01-22 18:10 上一篇讲了USB驱动栈整体结构,说明了usbport.sys的重要作用,现在就具体分析usbport.sys的内部实现细节。 首先再重复一下:

killcpp的专栏 1294

gem5-gpu教程01 部署(实测可跑通)

这将GPGPU-Sim更新为与gem5-gpu兼容的最新版本。此版本是已知可用于gem5 gpu的最新版本。

qq_20388971的博客 1404

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

USB 驱动程序堆栈体系结构

USB 驱动程序堆栈体系结构 本主题提供了通用串行总线 (USB) 驱动程序堆栈体系结构的概述。 下图显示了 Windows 8 USB 驱动程序堆栈体系结构框图。该图分别显示了 USB 2.0 和 USB 3.0 的 USB 驱动程序堆栈。当设备附加到 xHCI 控制器时,Windows 会加载 USB 3.0 驱动程序堆栈。USB 3.0 堆栈是 Windows 8 中的新功能。 当

M_O_Bz的专栏 2437

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

Windows XP下USB整体结构

http://hi.baidu.com/jlgwxq/blog/item/6aa939cc053d8f0893457e40.html Windows XP下USB整体结构 2010-01-22 13:57 这里主要讲的是Windows XP下USB驱动协议栈的整体结构,以及内部的一些重要数据结构。在写这些东西时,参考了大牛Tiamo的相关论坛贴子 ,内容很多雷同,说是转贴也不为过。

killcpp的专栏 779

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

从USB 2.0到USB 3.2:IAD描述符的演进与在Windows/Linux下的兼容性实战

本文深入探讨了USB接口技术从USB 2.0到USB 3.2的演进过程中,IAD描述符的关键作用及其在Windows和Linux系统下的兼容性实战。通过分析IAD的技术演进、跨平台兼容性解决方案及现代复合设备设计最佳实践,帮助开发者解决多接口设备的逻辑聚合问题,确保设备在不同操作系统上的无缝兼容。

weixin_30550081的博客 461

USB设备偶发断连根因与实战加固指南

USB设备连接稳定性是嵌入式系统开发中的核心基础问题,其本质涉及协议时序、电源完整性、主机驱动兼容性及硬件鲁棒性等多层协同。从USB枚举超时、端点使能延迟、挂起/唤醒握手失败,到字符串描述符编码错误,每一个微小偏差都可能引发‘识别成功但通信失效’的偶发断连现象。结合ft231x usb uart驱动适配、stm32 如何做usb设备等高频实践场景,本文聚焦CDC类USB Device在Windows/Linux多平台下的真实落地挑战,提供基于USB抓包、逻辑分析仪和主机日志的分层诊断路径,并输出可工程复用的

weixin_33937913的博客 404

INF中服务安装段落无效

你用的简化版XP,少了两个文件。 解决方法: 将mdmcpq.inf拷到 windows\inf usbser.sys 到 \windows\system32\drivers

shan 1527

WDF开发USB设备驱动教程(5)

PDF全文下载:http://bbs.driverdevelop.com/read.php?tid-120461.html 五.设备控制上一章结束后,我们已经完成了USB设备的配置,完成了配置工作的USB设备,就已经具备了正常工作的能力。这一章中,我们讲解一些USB设备控制的内容。包括:Pipe的重置;设备的重置;Pipe操作

张佩的技术库 9595
上一篇: Windows XP下usbport.sys驱动内部实现解析(四)
下一篇: Deferred Procedure Call Details(延迟过程调用详解)
killcpp
博客等级 码龄20年 13粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值