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的就到这里...
(结束)