summaryrefslogtreecommitdiff
path: root/pofx/WDF/Driver/MultiComp
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /pofx/WDF/Driver/MultiComp
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'pofx/WDF/Driver/MultiComp')
-rw-r--r--pofx/WDF/Driver/MultiComp/WdfMultiComp.mht2807
-rw-r--r--pofx/WDF/Driver/MultiComp/driver/HwSim.c251
-rw-r--r--pofx/WDF/Driver/MultiComp/driver/HwSim.h83
-rw-r--r--pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.c1036
-rw-r--r--pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.h114
-rw-r--r--pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.inx79
-rw-r--r--pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj188
-rw-r--r--pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj.Filters37
-rw-r--r--pofx/WDF/Driver/MultiComp/inc/WdfPoFx.h731
-rw-r--r--pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj183
-rw-r--r--pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters35
-rw-r--r--pofx/WDF/Driver/MultiComp/lib/WdfPoFxPriv.h382
-rw-r--r--pofx/WDF/Driver/MultiComp/lib/component.c562
-rw-r--r--pofx/WDF/Driver/MultiComp/lib/device.c868
-rw-r--r--pofx/WDF/Driver/MultiComp/lib/init.c1336
-rw-r--r--pofx/WDF/Driver/MultiComp/lib/s0idle.c471
-rw-r--r--pofx/WDF/Driver/MultiComp/lib/s0idle.h113
17 files changed, 9276 insertions, 0 deletions
diff --git a/pofx/WDF/Driver/MultiComp/WdfMultiComp.mht b/pofx/WDF/Driver/MultiComp/WdfMultiComp.mht
new file mode 100644
index 00000000..f6dc1775
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/WdfMultiComp.mht
@@ -0,0 +1,2807 @@
+MIME-Version: 1.0
+Content-Type: multipart/related; boundary="----=_NextPart_01CC4DF5.1FA44F90"
+
+This document is a Single File Web Page, also known as a Web Archive file. If you are seeing this message, your browser or editor doesn't support Web Archive files. Please download a browser that supports Web Archive, such as Windows� Internet Explorer�.
+
+------=_NextPart_01CC4DF5.1FA44F90
+Content-Location: file:///C:/EEB34A10/WdfMultiComp.htm
+Content-Transfer-Encoding: quoted-printable
+Content-Type: text/html; charset="us-ascii"
+
+<html xmlns:v=3D"urn:schemas-microsoft-com:vml"
+xmlns:o=3D"urn:schemas-microsoft-com:office:office"
+xmlns:w=3D"urn:schemas-microsoft-com:office:word"
+xmlns:m=3D"http://schemas.microsoft.com/office/2004/12/omml"
+xmlns=3D"http://www.w3.org/TR/REC-html40">
+
+<head>
+<meta http-equiv=3DContent-Type content=3D"text/html; charset=3Dus-ascii">
+<meta name=3DProgId content=3DWord.Document>
+<meta name=3DGenerator content=3D"Microsoft Word 14">
+<meta name=3DOriginator content=3D"Microsoft Word 14">
+<link rel=3DFile-List href=3D"WdfMultiComp_files/filelist.xml">
+<link rel=3DEdit-Time-Data href=3D"WdfMultiComp_files/editdata.mso">
+<link rel=3DOLE-Object-Data href=3D"WdfMultiComp_files/oledata.mso">
+<!--[if !mso]>
+<style>
+v\:* {behavior:url(#default#VML);}
+o\:* {behavior:url(#default#VML);}
+w\:* {behavior:url(#default#VML);}
+.shape {behavior:url(#default#VML);}
+</style>
+<![endif]--><!--[if gte mso 9]><xml>
+ <o:DocumentProperties>
+ <o:Author>Abhishek Ram</o:Author>
+ <o:Template>Normal</o:Template>
+ <o:LastAuthor>Abhishek Ram</o:LastAuthor>
+ <o:Revision>11</o:Revision>
+ <o:TotalTime>589</o:TotalTime>
+ <o:Created>2011-06-25T00:43:00Z</o:Created>
+ <o:LastSaved>2011-07-29T20:40:00Z</o:LastSaved>
+ <o:Pages>1</o:Pages>
+ <o:Words>922</o:Words>
+ <o:Characters>5261</o:Characters>
+ <o:Company>Microsoft Corporation</o:Company>
+ <o:Lines>43</o:Lines>
+ <o:Paragraphs>12</o:Paragraphs>
+ <o:CharactersWithSpaces>6171</o:CharactersWithSpaces>
+ <o:Version>14.00</o:Version>
+ </o:DocumentProperties>
+</xml><![endif]-->
+<link rel=3DthemeData href=3D"WdfMultiComp_files/themedata.thmx">
+<link rel=3DcolorSchemeMapping href=3D"WdfMultiComp_files/colorschememappin=
+g.xml">
+<!--[if gte mso 9]><xml>
+ <w:WordDocument>
+ <w:Zoom>125</w:Zoom>
+ <w:SpellingState>Clean</w:SpellingState>
+ <w:GrammarState>Clean</w:GrammarState>
+ <w:TrackMoves>false</w:TrackMoves>
+ <w:TrackFormatting/>
+ <w:PunctuationKerning/>
+ <w:ValidateAgainstSchemas/>
+ <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid>
+ <w:IgnoreMixedContent>false</w:IgnoreMixedContent>
+ <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText>
+ <w:DoNotPromoteQF/>
+ <w:LidThemeOther>EN-US</w:LidThemeOther>
+ <w:LidThemeAsian>X-NONE</w:LidThemeAsian>
+ <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript>
+ <w:Compatibility>
+ <w:BreakWrappedTables/>
+ <w:SnapToGridInCell/>
+ <w:WrapTextWithPunct/>
+ <w:UseAsianBreakRules/>
+ <w:DontGrowAutofit/>
+ <w:SplitPgBreakAndParaMark/>
+ <w:EnableOpenTypeKerning/>
+ <w:DontFlipMirrorIndents/>
+ <w:OverrideTableStyleHps/>
+ </w:Compatibility>
+ <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel>
+ <m:mathPr>
+ <m:mathFont m:val=3D"Cambria Math"/>
+ <m:brkBin m:val=3D"before"/>
+ <m:brkBinSub m:val=3D"&#45;-"/>
+ <m:smallFrac m:val=3D"off"/>
+ <m:dispDef/>
+ <m:lMargin m:val=3D"0"/>
+ <m:rMargin m:val=3D"0"/>
+ <m:defJc m:val=3D"centerGroup"/>
+ <m:wrapIndent m:val=3D"1440"/>
+ <m:intLim m:val=3D"subSup"/>
+ <m:naryLim m:val=3D"undOvr"/>
+ </m:mathPr></w:WordDocument>
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <w:LatentStyles DefLockedState=3D"false" DefUnhideWhenUsed=3D"true"
+ DefSemiHidden=3D"true" DefQFormat=3D"false" DefPriority=3D"99"
+ LatentStyleCount=3D"267">
+ <w:LsdException Locked=3D"false" Priority=3D"0" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Normal"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"heading 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"=
+heading 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"=
+heading 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"=
+heading 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"=
+heading 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"=
+heading 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"=
+heading 7"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"=
+heading 8"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"=
+heading 9"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 7"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 8"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 9"/>
+ <w:LsdException Locked=3D"false" Priority=3D"35" QFormat=3D"true" Name=3D=
+"caption"/>
+ <w:LsdException Locked=3D"false" Priority=3D"10" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Title"/>
+ <w:LsdException Locked=3D"false" Priority=3D"1" Name=3D"Default Paragraph=
+ Font"/>
+ <w:LsdException Locked=3D"false" Priority=3D"11" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Subtitle"/>
+ <w:LsdException Locked=3D"false" Priority=3D"22" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Strong"/>
+ <w:LsdException Locked=3D"false" Priority=3D"20" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Emphasis"/>
+ <w:LsdException Locked=3D"false" Priority=3D"59" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Table Grid"/>
+ <w:LsdException Locked=3D"false" UnhideWhenUsed=3D"false" Name=3D"Placeho=
+lder Text"/>
+ <w:LsdException Locked=3D"false" Priority=3D"1" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"No Spacing"/>
+ <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light Shading"/>
+ <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light List"/>
+ <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light Grid"/>
+ <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Dark List"/>
+ <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Shading"/>
+ <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful List"/>
+ <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Grid"/>
+ <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light List Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 1"/>
+ <w:LsdException Locked=3D"false" UnhideWhenUsed=3D"false" Name=3D"Revisio=
+n"/>
+ <w:LsdException Locked=3D"false" Priority=3D"34" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"List Paragraph"/>
+ <w:LsdException Locked=3D"false" Priority=3D"29" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Quote"/>
+ <w:LsdException Locked=3D"false" Priority=3D"30" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Intense Quote"/>
+ <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light List Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light List Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light List Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light List Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light List Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"19" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Subtle Emphasis"/>
+ <w:LsdException Locked=3D"false" Priority=3D"21" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Intense Emphasis"/>
+ <w:LsdException Locked=3D"false" Priority=3D"31" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Subtle Reference"/>
+ <w:LsdException Locked=3D"false" Priority=3D"32" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Intense Reference"/>
+ <w:LsdException Locked=3D"false" Priority=3D"33" SemiHidden=3D"false"
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Book Title"/>
+ <w:LsdException Locked=3D"false" Priority=3D"37" Name=3D"Bibliography"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" QFormat=3D"true" Name=3D=
+"TOC Heading"/>
+ </w:LatentStyles>
+</xml><![endif]-->
+<style>
+<!--
+ /* Font Definitions */
+ @font-face
+ {font-family:Wingdings;
+ panose-1:5 0 0 0 0 0 0 0 0 0;
+ mso-font-charset:2;
+ mso-generic-font-family:auto;
+ mso-font-pitch:variable;
+ mso-font-signature:0 268435456 0 0 -2147483648 0;}
+@font-face
+ {font-family:"Cambria Math";
+ panose-1:2 4 5 3 5 4 6 3 2 4;
+ mso-font-charset:1;
+ mso-generic-font-family:roman;
+ mso-font-format:other;
+ mso-font-pitch:variable;
+ mso-font-signature:0 0 0 0 0 0;}
+@font-face
+ {font-family:Cambria;
+ panose-1:2 4 5 3 5 4 6 3 2 4;
+ mso-font-charset:0;
+ mso-generic-font-family:roman;
+ mso-font-pitch:variable;
+ mso-font-signature:-536870145 1073742943 0 0 415 0;}
+@font-face
+ {font-family:Tahoma;
+ panose-1:2 11 6 4 3 5 4 4 2 4;
+ mso-font-charset:0;
+ mso-generic-font-family:swiss;
+ mso-font-pitch:variable;
+ mso-font-signature:-520081665 -1073717157 41 0 66047 0;}
+@font-face
+ {font-family:Consolas;
+ panose-1:2 11 6 9 2 2 4 3 2 4;
+ mso-font-charset:0;
+ mso-generic-font-family:modern;
+ mso-font-pitch:fixed;
+ mso-font-signature:-520092929 1073806591 9 0 415 0;}
+ /* Style Definitions */
+ p.MsoNormal, li.MsoNormal, div.MsoNormal
+ {mso-style-unhide:no;
+ mso-style-qformat:yes;
+ mso-style-parent:"";
+ margin:0in;
+ margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";
+ mso-fareast-font-family:"Times New Roman";
+ mso-fareast-theme-font:minor-fareast;}
+h1
+ {mso-style-priority:9;
+ mso-style-unhide:no;
+ mso-style-qformat:yes;
+ mso-style-link:"Heading 1 Char";
+ margin-top:24.0pt;
+ margin-right:0in;
+ margin-bottom:0in;
+ margin-left:0in;
+ margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ page-break-after:avoid;
+ mso-outline-level:1;
+ font-size:14.0pt;
+ font-family:"Cambria","serif";
+ mso-fareast-font-family:"Times New Roman";
+ mso-fareast-theme-font:minor-fareast;
+ mso-bidi-font-family:"Times New Roman";
+ color:#365F91;}
+h3
+ {mso-style-noshow:yes;
+ mso-style-priority:9;
+ mso-style-qformat:yes;
+ mso-style-link:"Heading 3 Char";
+ mso-margin-top-alt:auto;
+ margin-right:0in;
+ mso-margin-bottom-alt:auto;
+ margin-left:0in;
+ mso-pagination:widow-orphan;
+ mso-outline-level:3;
+ font-size:13.5pt;
+ font-family:"Times New Roman","serif";
+ mso-fareast-font-family:"Times New Roman";
+ mso-fareast-theme-font:minor-fareast;}
+p.MsoPlainText, li.MsoPlainText, div.MsoPlainText
+ {mso-style-noshow:yes;
+ mso-style-priority:99;
+ mso-style-link:"Plain Text Char";
+ margin:0in;
+ margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ font-size:10.5pt;
+ font-family:Consolas;
+ mso-fareast-font-family:"Times New Roman";
+ mso-fareast-theme-font:minor-fareast;}
+p.MsoAcetate, li.MsoAcetate, div.MsoAcetate
+ {mso-style-noshow:yes;
+ mso-style-priority:99;
+ mso-style-link:"Balloon Text Char";
+ margin:0in;
+ margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ font-size:8.0pt;
+ font-family:"Tahoma","sans-serif";
+ mso-fareast-font-family:"Times New Roman";
+ mso-fareast-theme-font:minor-fareast;}
+p.MsoListParagraph, li.MsoListParagraph, div.MsoListParagraph
+ {mso-style-priority:34;
+ mso-style-unhide:no;
+ mso-style-qformat:yes;
+ margin-top:0in;
+ margin-right:0in;
+ margin-bottom:0in;
+ margin-left:.5in;
+ margin-bottom:.0001pt;
+ mso-add-space:auto;
+ mso-pagination:widow-orphan;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";
+ mso-fareast-font-family:"Times New Roman";
+ mso-fareast-theme-font:minor-fareast;}
+p.MsoListParagraphCxSpFirst, li.MsoListParagraphCxSpFirst, div.MsoListParag=
+raphCxSpFirst
+ {mso-style-priority:34;
+ mso-style-unhide:no;
+ mso-style-qformat:yes;
+ mso-style-type:export-only;
+ margin-top:0in;
+ margin-right:0in;
+ margin-bottom:0in;
+ margin-left:.5in;
+ margin-bottom:.0001pt;
+ mso-add-space:auto;
+ mso-pagination:widow-orphan;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";
+ mso-fareast-font-family:"Times New Roman";
+ mso-fareast-theme-font:minor-fareast;}
+p.MsoListParagraphCxSpMiddle, li.MsoListParagraphCxSpMiddle, div.MsoListPar=
+agraphCxSpMiddle
+ {mso-style-priority:34;
+ mso-style-unhide:no;
+ mso-style-qformat:yes;
+ mso-style-type:export-only;
+ margin-top:0in;
+ margin-right:0in;
+ margin-bottom:0in;
+ margin-left:.5in;
+ margin-bottom:.0001pt;
+ mso-add-space:auto;
+ mso-pagination:widow-orphan;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";
+ mso-fareast-font-family:"Times New Roman";
+ mso-fareast-theme-font:minor-fareast;}
+p.MsoListParagraphCxSpLast, li.MsoListParagraphCxSpLast, div.MsoListParagra=
+phCxSpLast
+ {mso-style-priority:34;
+ mso-style-unhide:no;
+ mso-style-qformat:yes;
+ mso-style-type:export-only;
+ margin-top:0in;
+ margin-right:0in;
+ margin-bottom:0in;
+ margin-left:.5in;
+ margin-bottom:.0001pt;
+ mso-add-space:auto;
+ mso-pagination:widow-orphan;
+ font-size:12.0pt;
+ font-family:"Times New Roman","serif";
+ mso-fareast-font-family:"Times New Roman";
+ mso-fareast-theme-font:minor-fareast;}
+span.Heading1Char
+ {mso-style-name:"Heading 1 Char";
+ mso-style-priority:9;
+ mso-style-unhide:no;
+ mso-style-locked:yes;
+ mso-style-link:"Heading 1";
+ mso-ansi-font-size:14.0pt;
+ mso-bidi-font-size:14.0pt;
+ font-family:"Cambria","serif";
+ mso-ascii-font-family:Cambria;
+ mso-fareast-font-family:"Times New Roman";
+ mso-fareast-theme-font:minor-fareast;
+ mso-hansi-font-family:Cambria;
+ mso-bidi-font-family:"Times New Roman";
+ color:#365F91;
+ mso-font-kerning:18.0pt;
+ font-weight:bold;}
+span.Heading3Char
+ {mso-style-name:"Heading 3 Char";
+ mso-style-noshow:yes;
+ mso-style-priority:9;
+ mso-style-unhide:no;
+ mso-style-locked:yes;
+ mso-style-link:"Heading 3";
+ mso-ansi-font-size:13.5pt;
+ mso-bidi-font-size:13.5pt;
+ font-family:"Times New Roman","serif";
+ mso-ascii-font-family:"Times New Roman";
+ mso-fareast-font-family:"Times New Roman";
+ mso-fareast-theme-font:minor-fareast;
+ mso-hansi-font-family:"Times New Roman";
+ mso-bidi-font-family:"Times New Roman";
+ font-weight:bold;}
+span.PlainTextChar
+ {mso-style-name:"Plain Text Char";
+ mso-style-noshow:yes;
+ mso-style-priority:99;
+ mso-style-unhide:no;
+ mso-style-locked:yes;
+ mso-style-link:"Plain Text";
+ mso-ansi-font-size:10.5pt;
+ mso-bidi-font-size:10.5pt;
+ font-family:Consolas;
+ mso-ascii-font-family:Consolas;
+ mso-hansi-font-family:Consolas;
+ mso-bidi-font-family:Consolas;}
+span.BalloonTextChar
+ {mso-style-name:"Balloon Text Char";
+ mso-style-noshow:yes;
+ mso-style-priority:99;
+ mso-style-unhide:no;
+ mso-style-locked:yes;
+ mso-style-link:"Balloon Text";
+ mso-ansi-font-size:8.0pt;
+ mso-bidi-font-size:8.0pt;
+ font-family:"Tahoma","sans-serif";
+ mso-ascii-font-family:Tahoma;
+ mso-fareast-font-family:"Times New Roman";
+ mso-fareast-theme-font:minor-fareast;
+ mso-hansi-font-family:Tahoma;
+ mso-bidi-font-family:Tahoma;}
+span.SpellE
+ {mso-style-name:"";
+ mso-spl-e:yes;}
+.MsoChpDefault
+ {mso-style-type:export-only;
+ mso-default-props:yes;
+ font-size:10.0pt;
+ mso-ansi-font-size:10.0pt;
+ mso-bidi-font-size:10.0pt;
+ font-family:"Calibri","sans-serif";
+ mso-ascii-font-family:Calibri;
+ mso-ascii-theme-font:minor-latin;
+ mso-fareast-font-family:Calibri;
+ mso-fareast-theme-font:minor-latin;
+ mso-hansi-font-family:Calibri;
+ mso-hansi-theme-font:minor-latin;
+ mso-bidi-font-family:"Times New Roman";
+ mso-bidi-theme-font:minor-bidi;}
+@page WordSection1
+ {size:8.5in 11.0in;
+ margin:1.0in 75.1pt 1.0in 75.05pt;
+ mso-header-margin:.5in;
+ mso-footer-margin:.5in;
+ mso-paper-source:0;}
+div.WordSection1
+ {page:WordSection1;}
+ /* List Definitions */
+ @list l0
+ {mso-list-id:557473843;
+ mso-list-type:hybrid;
+ mso-list-template-ids:-1934189430 -1019988350 67698691 67698693 67698689 6=
+7698691 67698693 67698689 67698691 67698693;}
+@list l0:level1
+ {mso-level-start-at:6;
+ mso-level-number-format:bullet;
+ mso-level-text:-;
+ mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;
+ font-family:"Arial","sans-serif";
+ mso-fareast-font-family:"Times New Roman";}
+@list l0:level2
+ {mso-level-number-format:bullet;
+ mso-level-text:o;
+ mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;
+ font-family:"Courier New";}
+@list l0:level3
+ {mso-level-number-format:bullet;
+ mso-level-text:\F0A7;
+ mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;
+ font-family:Wingdings;}
+@list l0:level4
+ {mso-level-number-format:bullet;
+ mso-level-text:\F0B7;
+ mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;
+ font-family:Symbol;}
+@list l0:level5
+ {mso-level-number-format:bullet;
+ mso-level-text:o;
+ mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;
+ font-family:"Courier New";}
+@list l0:level6
+ {mso-level-number-format:bullet;
+ mso-level-text:\F0A7;
+ mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;
+ font-family:Wingdings;}
+@list l0:level7
+ {mso-level-number-format:bullet;
+ mso-level-text:\F0B7;
+ mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;
+ font-family:Symbol;}
+@list l0:level8
+ {mso-level-number-format:bullet;
+ mso-level-text:o;
+ mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;
+ font-family:"Courier New";}
+@list l0:level9
+ {mso-level-number-format:bullet;
+ mso-level-text:\F0A7;
+ mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;
+ font-family:Wingdings;}
+@list l1
+ {mso-list-id:881289435;
+ mso-list-type:hybrid;
+ mso-list-template-ids:357181992 67698703 67698713 67698715 67698703 676987=
+13 67698715 67698703 67698713 67698715;}
+@list l1:level1
+ {mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;}
+@list l1:level2
+ {mso-level-number-format:alpha-lower;
+ mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;}
+@list l1:level3
+ {mso-level-number-format:roman-lower;
+ mso-level-tab-stop:none;
+ mso-level-number-position:right;
+ text-indent:-9.0pt;}
+@list l1:level4
+ {mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;}
+@list l1:level5
+ {mso-level-number-format:alpha-lower;
+ mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;}
+@list l1:level6
+ {mso-level-number-format:roman-lower;
+ mso-level-tab-stop:none;
+ mso-level-number-position:right;
+ text-indent:-9.0pt;}
+@list l1:level7
+ {mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;}
+@list l1:level8
+ {mso-level-number-format:alpha-lower;
+ mso-level-tab-stop:none;
+ mso-level-number-position:left;
+ text-indent:-.25in;}
+@list l1:level9
+ {mso-level-number-format:roman-lower;
+ mso-level-tab-stop:none;
+ mso-level-number-position:right;
+ text-indent:-9.0pt;}
+ol
+ {margin-bottom:0in;}
+ul
+ {margin-bottom:0in;}
+-->
+</style>
+<!--[if gte mso 10]>
+<style>
+ /* Style Definitions */
+ table.MsoNormalTable
+ {mso-style-name:"Table Normal";
+ mso-tstyle-rowband-size:0;
+ mso-tstyle-colband-size:0;
+ mso-style-noshow:yes;
+ mso-style-priority:99;
+ mso-style-parent:"";
+ mso-padding-alt:0in 5.4pt 0in 5.4pt;
+ mso-para-margin:0in;
+ mso-para-margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ font-size:10.0pt;
+ font-family:"Calibri","sans-serif";
+ mso-ascii-font-family:Calibri;
+ mso-ascii-theme-font:minor-latin;
+ mso-hansi-font-family:Calibri;
+ mso-hansi-theme-font:minor-latin;
+ mso-bidi-font-family:"Times New Roman";
+ mso-bidi-theme-font:minor-bidi;}
+</style>
+<![endif]--><!--[if gte mso 9]><xml>
+ <o:shapedefaults v:ext=3D"edit" spidmax=3D"1026"/>
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <o:shapelayout v:ext=3D"edit">
+ <o:idmap v:ext=3D"edit" data=3D"1"/>
+ </o:shapelayout></xml><![endif]-->
+</head>
+
+<body lang=3DEN-US style=3D'tab-interval:.5in'>
+
+<div class=3DWordSection1>
+
+<h1><span style=3D'mso-fareast-font-family:"Times New Roman"'>F-state-based=
+ power
+management for multi-component device<o:p></o:p></span></h1>
+
+<h3><span style=3D'mso-fareast-font-family:"Times New Roman"'>Summary<o:p><=
+/o:p></span></h3>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif";
+mso-fareast-font-family:"Times New Roman"'>This sample demonstrates how a K=
+MDF
+driver can implement F-state-based power management for a device that has an
+arbitrary number of components that can be individually power-managed.<o:p>=
+</o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif";
+mso-fareast-font-family:"Times New Roman"'><o:p>&nbsp;</o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif";
+mso-fareast-font-family:"Times New Roman"'>The sample driver statically lin=
+ks
+to a helper library (WdfPoFx.lib) that encapsulates all of the generic code=
+ to
+interact with the power framework. The device-specific code is implemented =
+in
+the driver itself, outside of the helper library. The idea behind this
+organizing the code in this manner is make the helper library reusable by o=
+ther
+drivers. The directory structure for the sample is as follows:<o:p></o:p></=
+span></p>
+
+<p class=3DMsoListParagraphCxSpFirst style=3D'text-indent:-.25in;mso-list:l=
+0 level1 lfo2'><![if !supportLists]><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-font=
+-family:
+Arial'><span style=3D'mso-list:Ignore'>-<span style=3D'font:7.0pt "Times Ne=
+w Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span></span></span><![endif]><span style=3D'font-size:10.0pt;font-family:=
+"Arial","sans-serif";
+mso-fareast-font-family:"Times New Roman"'>The helper library is implemente=
+d in
+the &#8216;lib&#8217; subdirectory<o:p></o:p></span></p>
+
+<p class=3DMsoListParagraphCxSpMiddle style=3D'text-indent:-.25in;mso-list:=
+l0 level1 lfo2'><![if !supportLists]><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-font=
+-family:
+Arial'><span style=3D'mso-list:Ignore'>-<span style=3D'font:7.0pt "Times Ne=
+w Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span></span></span><![endif]><span style=3D'font-size:10.0pt;font-family:=
+"Arial","sans-serif";
+mso-fareast-font-family:"Times New Roman"'>The interface between the helper
+library and the rest of the driver code is defined in the &#8216;<span
+class=3DSpellE>inc</span>&#8217; subdirectory<o:p></o:p></span></p>
+
+<p class=3DMsoListParagraphCxSpLast style=3D'text-indent:-.25in;mso-list:l0=
+ level1 lfo2'><![if !supportLists]><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-font=
+-family:
+Arial'><span style=3D'mso-list:Ignore'>-<span style=3D'font:7.0pt "Times Ne=
+w Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span></span></span><![endif]><span style=3D'font-size:10.0pt;font-family:=
+"Arial","sans-serif";
+mso-fareast-font-family:"Times New Roman"'>The driver is implemented in the
+&#8216;driver&#8217; subdirectory<o:p></o:p></span></p>
+
+<h3><span style=3D'mso-fareast-font-family:"Times New Roman"'>Installation<=
+o:p></o:p></span></h3>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'>The
+driver can be installed on a root-enumerated device using the devcon.exe to=
+ol. <o:p></o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'><o:p>&nbsp;</o:p></span></p>
+
+<p class=3DMsoListParagraphCxSpFirst style=3D'text-indent:-.25in;mso-list:l=
+1 level1 lfo4'><![if !supportLists]><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-font=
+-family:
+Arial'><span style=3D'mso-list:Ignore'>1.<span style=3D'font:7.0pt "Times N=
+ew Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;
+</span></span></span><![endif]><span style=3D'font-size:10.0pt;font-family:=
+"Arial","sans-serif"'>Obtain
+the devcon.exe tool from the WDK<o:p></o:p></span></p>
+
+<p class=3DMsoListParagraphCxSpMiddle style=3D'text-indent:-.25in;mso-list:=
+l1 level1 lfo4'><![if !supportLists]><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-font=
+-family:
+Arial'><span style=3D'mso-list:Ignore'>2.<span style=3D'font:7.0pt "Times N=
+ew Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;
+</span></span></span><![endif]><span style=3D'font-size:10.0pt;font-family:=
+"Arial","sans-serif"'>Copy
+the driver binary, INF file and the KMDF <span class=3DSpellE>coinstaller</=
+span>
+to a directory on your test machine.<o:p></o:p></span></p>
+
+<p class=3DMsoListParagraphCxSpLast style=3D'text-indent:-.25in;mso-list:l1=
+ level1 lfo4'><![if !supportLists]><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-font=
+-family:
+Arial'><span style=3D'mso-list:Ignore'>3.<span style=3D'font:7.0pt "Times N=
+ew Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;
+</span></span></span><![endif]><span style=3D'font-size:10.0pt;font-family:=
+"Arial","sans-serif"'>Run
+the command &#8220;devcon.exe install WdfMultiComp.inf WDF\<span class=3DSp=
+ellE>WdfMultiComp</span>&#8221;<o:p></o:p></span></p>
+
+<h3><span style=3D'mso-fareast-font-family:"Times New Roman"'>Testing<o:p><=
+/o:p></span></h3>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'>Use
+the PowerFxApp.exe application to send I/O requests to the driver. Running =
+the
+command &#8220;PowerFxApp.exe /?&#8221; displays detailed usage information=
+.<o:p></o:p></span></p>
+
+<h3><span style=3D'mso-fareast-font-family:"Times New Roman"'>Design overvi=
+ew<o:p></o:p></span></h3>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'>The
+driver controls a device that has more than one component. It needs to acce=
+ss
+one of those components for processing each I/O request that it receives. T=
+he
+specific component that it needs to access depends on the I/O request that =
+it
+receives.<o:p></o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'><o:p>&nbsp;</o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'>In
+order to support this, the driver creates one top-level, power-managed queu=
+e to
+receive all its requests. It also creates one secondary, power-managed queue
+for each of its components. These secondary queues are called component que=
+ues.
+This is shown in the diagram below.<o:p></o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'><!--[if gte vml 1]><v:shapetype
+ id=3D"_x0000_t75" coordsize=3D"21600,21600" o:spt=3D"75" o:preferrelative=
+=3D"t"
+ path=3D"m@4@5l@4@11@9@11@9@5xe" filled=3D"f" stroked=3D"f">
+ <v:stroke joinstyle=3D"miter"/>
+ <v:formulas>
+ <v:f eqn=3D"if lineDrawn pixelLineWidth 0"/>
+ <v:f eqn=3D"sum @0 1 0"/>
+ <v:f eqn=3D"sum 0 0 @1"/>
+ <v:f eqn=3D"prod @2 1 2"/>
+ <v:f eqn=3D"prod @3 21600 pixelWidth"/>
+ <v:f eqn=3D"prod @3 21600 pixelHeight"/>
+ <v:f eqn=3D"sum @0 0 1"/>
+ <v:f eqn=3D"prod @6 1 2"/>
+ <v:f eqn=3D"prod @7 21600 pixelWidth"/>
+ <v:f eqn=3D"sum @8 21600 0"/>
+ <v:f eqn=3D"prod @7 21600 pixelHeight"/>
+ <v:f eqn=3D"sum @10 21600 0"/>
+ </v:formulas>
+ <v:path o:extrusionok=3D"f" gradientshapeok=3D"t" o:connecttype=3D"rect"/>
+ <o:lock v:ext=3D"edit" aspectratio=3D"t"/>
+</v:shapetype><v:shape id=3D"_x0000_i1025" type=3D"#_x0000_t75" style=3D'wi=
+dth:588.75pt;
+ height:246pt' o:ole=3D"">
+ <v:imagedata src=3D"WdfMultiComp_files/image001.emz" o:title=3D""/>
+</v:shape><![endif]--><![if !vml]><img width=3D785 height=3D328
+src=3D"WdfMultiComp_files/image002.gif" v:shapes=3D"_x0000_i1025"><![endif]=
+><!--[if gte mso 9]><xml>
+ <o:OLEObject Type=3D"Embed" ProgID=3D"Visio.Drawing.11" ShapeID=3D"_x0000_=
+i1025"
+ DrawAspect=3D"Content" ObjectID=3D"_1373452002">
+ </o:OLEObject>
+</xml><![endif]--><o:p></o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'><o:p>&nbsp;</o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'>When
+the driver&#8217;s dispatch routine for the top-level queue is invoked, it
+examines the request to determine which component it needs to access in ord=
+er
+to process the request. Then, it forwards the request to the component queue
+for the component that it needs to access for that request. When the
+driver&#8217;s dispatch routine for the component queue is invoked, it acce=
+sses
+the component hardware to process the request.<o:p></o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'><o:p>&nbsp;</o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'>The
+driver&#8217;s top-level queue and component queues are all power-managed so
+KMDF ensures that the device is in D0 while the queues are in a dispatching
+state. The key point to note is that the driver is designed to maintain a c=
+omponent
+queue in a dispatching state only when the component is active. In order to
+achieve this, the driver stops the component queue when the component becom=
+es
+idle and starts the component queue when the component becomes active. (To =
+be
+precise, this mechanism of stopping and starting queues is encapsulated in =
+the
+power framework helper library used by the driver). Thus, the driver is abl=
+e to
+ensure that when the component queue is in a dispatching state, not only is=
+ the
+device in D0 but the component corresponding to that queue is also active. =
+Thus
+it is safe to access the component hardware when the component queue&#8217;s
+dispatch routine is invoked.<o:p></o:p></span></p>
+
+<h3><span style=3D'mso-fareast-font-family:"Times New Roman"'>Implementation
+notes<o:p></o:p></span></h3>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'>The
+driver uses the power framework helper library to manage most of its
+interactions with the power framework. In order to achieve this, during dev=
+ice
+initialization, the driver performs the following tasks in its <span
+class=3DSpellE>EvtDriverDeviceAdd</span> callback:<o:p></o:p></span></p>
+
+<p class=3DMsoListParagraphCxSpFirst style=3D'text-indent:-.25in;mso-list:l=
+0 level1 lfo2'><![if !supportLists]><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-font=
+-family:
+Arial'><span style=3D'mso-list:Ignore'>-<span style=3D'font:7.0pt "Times Ne=
+w Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span></span></span><![endif]><span style=3D'font-size:10.0pt;font-family:=
+"Arial","sans-serif"'>Enables
+the helper library to register its own KMDF callbacks for PNP and
+power-management of the device.<o:p></o:p></span></p>
+
+<p class=3DMsoListParagraphCxSpMiddle style=3D'text-indent:-.25in;mso-list:=
+l0 level1 lfo2'><![if !supportLists]><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-font=
+-family:
+Arial'><span style=3D'mso-list:Ignore'>-<span style=3D'font:7.0pt "Times Ne=
+w Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span></span></span><![endif]><span style=3D'font-size:10.0pt;font-family:=
+"Arial","sans-serif"'>Provides
+the helper library with power-framework-related information about the devic=
+e.<o:p></o:p></span></p>
+
+<p class=3DMsoListParagraphCxSpLast style=3D'text-indent:-.25in;mso-list:l0=
+ level1 lfo2'><![if !supportLists]><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-font=
+-family:
+Arial'><span style=3D'mso-list:Ignore'>-<span style=3D'font:7.0pt "Times Ne=
+w Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span></span></span><![endif]><span style=3D'font-size:10.0pt;font-family:=
+"Arial","sans-serif"'>Provides
+the helper library with information about the component queues.<o:p></o:p><=
+/span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'>During
+I/O request processing, the driver uses routines provided by helper library=
+ to
+forward requests to component queues and also to complete requests. <o:p></=
+o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'><o:p>&nbsp;</o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'>The
+main tasks performed by the power framework helper library on behalf of the
+driver are:<o:p></o:p></span></p>
+
+<p class=3DMsoListParagraphCxSpFirst style=3D'text-indent:-.25in;mso-list:l=
+0 level1 lfo2'><![if !supportLists]><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-font=
+-family:
+Arial'><span style=3D'mso-list:Ignore'>-<span style=3D'font:7.0pt "Times Ne=
+w Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span></span></span><![endif]><span style=3D'font-size:10.0pt;font-family:=
+"Arial","sans-serif"'>Registration
+and <span class=3DSpellE>unregistration</span> with the power framework.<o:=
+p></o:p></span></p>
+
+<p class=3DMsoListParagraphCxSpMiddle style=3D'text-indent:-.25in;mso-list:=
+l0 level1 lfo2'><![if !supportLists]><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-font=
+-family:
+Arial'><span style=3D'mso-list:Ignore'>-<span style=3D'font:7.0pt "Times Ne=
+w Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span></span></span><![endif]><span style=3D'font-size:10.0pt;font-family:=
+"Arial","sans-serif"'>Stopping
+component queues when the corresponding components become idle and starting
+them when the corresponding components become active.<o:p></o:p></span></p>
+
+<p class=3DMsoListParagraphCxSpLast style=3D'text-indent:-.25in;mso-list:l0=
+ level1 lfo2'><![if !supportLists]><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-font=
+-family:
+Arial'><span style=3D'mso-list:Ignore'>-<span style=3D'font:7.0pt "Times Ne=
+w Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span></span></span><![endif]><span style=3D'font-size:10.0pt;font-family:=
+"Arial","sans-serif"'>Notifying
+the power framework when the device returns to its working state (D0) in
+response to the system returning from a low-power state to the working state
+(S0).<o:p></o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'><o:p>&nbsp;</o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'>The
+power framework helper library does not have any hardware-specific informat=
+ion,
+so any tasks that are specific to the device&#8217;s hardware are performed=
+ by
+the driver. In this sample, the device hardware is represented by a very si=
+mple
+simulation. The notable hardware-specific tasks in this sample are:<o:p></o=
+:p></span></p>
+
+<p class=3DMsoListParagraphCxSpFirst style=3D'text-indent:-.25in;mso-list:l=
+0 level1 lfo2'><![if !supportLists]><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-font=
+-family:
+Arial'><span style=3D'mso-list:Ignore'>-<span style=3D'font:7.0pt "Times Ne=
+w Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span></span></span><![endif]><span style=3D'font-size:10.0pt;font-family:=
+"Arial","sans-serif"'>Accessing
+component hardware to process I/O requests.<o:p></o:p></span></p>
+
+<p class=3DMsoListParagraphCxSpLast style=3D'text-indent:-.25in;mso-list:l0=
+ level1 lfo2'><![if !supportLists]><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-font=
+-family:
+Arial'><span style=3D'mso-list:Ignore'>-<span style=3D'font:7.0pt "Times Ne=
+w Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</span></span></span><![endif]><span style=3D'font-size:10.0pt;font-family:=
+"Arial","sans-serif"'>Accessing
+component hardware to change the component&#8217;s F-state.<o:p></o:p></spa=
+n></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'>As
+mentioned earlier, the hardware access shown is this sample is entirely
+simulated in software. This sample does not work with a real device &#8211;=
+ it
+installs on a root-enumerated software device.<o:p></o:p></span></p>
+
+<h3><span style=3D'mso-fareast-font-family:"Times New Roman"'>S0-idle power
+management support<o:p></o:p></span></h3>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'>The
+power framework helper library implements support for S0-idle power managem=
+ent
+for the device. Note that this is different from the component power manage=
+ment
+support that is enabled by the power framework. Component power management
+enables individual components of the device to be power-managed by putting =
+them
+in different F-states while the device is in a working state (D0). S0-idle
+power management for the device enables the device as a whole to be
+power-managed by putting it into different D-states while the system is in a
+working state (S0).<o:p></o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'><o:p>&nbsp;</o:p></span></p>
+
+<p class=3DMsoNormal><span style=3D'font-size:10.0pt;font-family:"Arial","s=
+ans-serif"'>The
+code to implement S0-idle power management support for the device is
+conditionally compiled based on the value of the </span><span style=3D'font=
+-size:
+9.0pt;font-family:"Courier New"'>PFH_S0IDLE_SUPPORTED</span><span
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif"'> compiler switc=
+h. If
+the switch is set to a non-zero value, the code to implement S0-idle power
+management support is included. If set to zero, the code is omitted thereby
+resulting in a smaller binary size. Thus, a driver that requires S0-idle po=
+wer
+management for the device can use the power framework helper library&#8217;s
+support for it but a driver that does not require it can reduce its binary =
+size
+by omitting the code that is specific to S0-idle power management.<o:p></o:=
+p></span></p>
+
+</div>
+
+</body>
+
+</html>
+
+------=_NextPart_01CC4DF5.1FA44F90
+Content-Location: file:///C:/EEB34A10/WdfMultiComp_files/themedata.thmx
+Content-Transfer-Encoding: base64
+Content-Type: application/vnd.ms-officetheme
+
+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
+cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA=
+
+------=_NextPart_01CC4DF5.1FA44F90
+Content-Location: file:///C:/EEB34A10/WdfMultiComp_files/colorschememapping.xml
+Content-Transfer-Encoding: quoted-printable
+Content-Type: text/xml
+
+<?xml version=3D"1.0" encoding=3D"UTF-8" standalone=3D"yes"?>
+<a:clrMap xmlns:a=3D"http://schemas.openxmlformats.org/drawingml/2006/main"=
+ bg1=3D"lt1" tx1=3D"dk1" bg2=3D"lt2" tx2=3D"dk2" accent1=3D"accent1" accent=
+2=3D"accent2" accent3=3D"accent3" accent4=3D"accent4" accent5=3D"accent5" a=
+ccent6=3D"accent6" hlink=3D"hlink" folHlink=3D"folHlink"/>
+------=_NextPart_01CC4DF5.1FA44F90
+Content-Location: file:///C:/EEB34A10/WdfMultiComp_files/image001.emz
+Content-Transfer-Encoding: base64
+Content-Type: image/x-emz
+
+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
+egO5CJD//wENOK1w7N8BAN==
+
+------=_NextPart_01CC4DF5.1FA44F90
+Content-Location: file:///C:/EEB34A10/WdfMultiComp_files/image002.gif
+Content-Transfer-Encoding: base64
+Content-Type: image/gif
+
+R0lGODlhEQNIAXcAMSH+GlNvZnR3YXJlOiBNaWNyb3NvZnQgT2ZmaWNlACH5BAEAAAAALAwAAQAE
+AzcBhwAAAAAAAAICAgUFBQQEBAMDAwYGBh4eHhISEh8fHwEBAREREScnJygoKDw8PCQkJCUlJTk5
+OSkpKSYmJjs7Oz4+Pj09PTo6OjExMSMjIzIyMl5eXkBAQE1NTUlJSUhISFFRUVBQUFJSUkFBQV9f
+X1lZWVpaWl1dXUZGRk9PT1xcXExMTEdHR05OTlVVVVRUVFdXV1NTU0pKSlhYWFZWVkREREtLS0ND
+Q1tbW0JCQm5ubmBgYHFxcWJiYnNzc3d3d2hoaG9vb3BwcGpqan19fWZmZmlpaWxsbHZ2dnp6emRk
+ZHR0dGtra3h4eHV1dXJycmFhYWNjY3x8fH9/f35+fnt7e21tbWVlZXl5eWdnZ56enp+fn4mJiZSU
+lJqampGRkYCAgIKCgpmZmZWVlY2NjYiIiIyMjIODg5KSkpycnI6Ojpubm4aGhpCQkIGBgY+Pj52d
+nYeHh5OTk4WFhYqKioSEhJaWlpiYmJeXl4uLi6CgoKSkpKqqqrCwsKurq7W1tampqaWlpb6+vrq6
+urGxsaKiora2tq6urq2traysrLu7u6+vr7i4uKampry8vKioqKOjo7m5ubOzs6GhobS0tKenp7Ky
+sr+/v7e3t729vdLS0s3Nzc7Ozs/Pz8jIyNHR0djY2NTU1MHBwdDQ0MzMzMPDw9/f397e3tPT08fH
+x8XFxdvb28TExNzc3Nra2t3d3cbGxsLCwsvLy8rKytfX18nJydXV1dnZ2cDAwNbW1vz8/P7+/ubm
+5uvr6+fn5/39/fj4+Pf39/Ly8vv7++Xl5fr6+uTk5PDw8Ojo6Pn5+e7u7vPz8+np6ePj4+zs7PT0
+9OLi4vb29uHh4fX19eDg4Orq6vHx8e3t7e/v7////wECAwECAwECAwECAwECAwECAwECAwECAwEC
+AwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwEC
+AwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwj/AAEIHEiwoMGDCBMqXMiwocOHECNKnEix
+osWLGDNq3Mixo0ePOgDsEElypMmSKEduSMnypEsAK1/KbAmT5syXIT/q3BmSBwCfPnH+HOpjp9Gj
+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
+
+------=_NextPart_01CC4DF5.1FA44F90
+Content-Location: file:///C:/EEB34A10/WdfMultiComp_files/oledata.mso
+Content-Transfer-Encoding: base64
+Content-Type: application/x-mso
+
+0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAA
+EAAA/v///wAAAAD+////AAAAAAAAAAD/////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////9
+/////v///wMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8A
+AAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAA
+AB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAAoAAAAKQAAACoAAAArAAAA
+LAAAAC0AAAAuAAAALwAAADAAAAAxAAAAMgAAADMAAAA0AAAANQAAADYAAAA3AAAAOAAAADkAAAA6
+AAAAOwAAADwAAAA9AAAAPgAAAD8AAABAAAAAQQAAAEIAAABDAAAARAAAAEUAAABGAAAARwAAAEgA
+AABJAAAASgAAAEsAAABMAAAATQAAAE4AAABPAAAAUAAAAFEAAABSAAAAUwAAAFQAAABVAAAAVgAA
+AFcAAABYAAAA/v//////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////1IA
+bwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAWAAUA//////////8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKASDcwvTswB
+/v///wAAAAAAAAAAXwAxADMANwAzADQANQAyADAAMAAyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAABgAAgH///////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAACAAAAta0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////wAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////
+////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALIC
+AHic7J0NfFxVnffPPVOSdho6bWYSum50yOzMOu2dJuHNjRpg4E5CfGEFFtjKFCxo17CioOCavmwk
+bjS7RqMuwWCW7BPXaLSAL6ywEesSV4txbVd2XXy6a1erNCWP4trSUimkyfP7/+fcm0M2ydxOOZMs
+n4yfHzP/5N75/c59Peeb0+vjP1xz4LNfe8XPxYzXRSIgJqdWiBLtZwHIcovVQkhVT05NTbk/XgYd
+h6aWXv8rXiehZ9U+/GPsTKppn5dCy6EVUBBaCZVBZ0KroFDuEBBroHIoDEWgCqgSOgtaC/0O9Aro
+d6Eq6JXQq6AodDZUDcWg34PiUAL6fejVUBJaB62HbCgFbYBqoFqoDjoHOhc6DzofugB6DfQHUD30
+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
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+
+------=_NextPart_01CC4DF5.1FA44F90
+Content-Location: file:///C:/EEB34A10/WdfMultiComp_files/filelist.xml
+Content-Transfer-Encoding: quoted-printable
+Content-Type: text/xml; charset="utf-8"
+
+<xml xmlns:o=3D"urn:schemas-microsoft-com:office:office">
+ <o:MainFile HRef=3D"../WdfMultiComp.htm"/>
+ <o:File HRef=3D"themedata.thmx"/>
+ <o:File HRef=3D"colorschememapping.xml"/>
+ <o:File HRef=3D"image001.emz"/>
+ <o:File HRef=3D"image002.gif"/>
+ <o:File HRef=3D"oledata.mso"/>
+ <o:File HRef=3D"filelist.xml"/>
+</xml>
+------=_NextPart_01CC4DF5.1FA44F90--
diff --git a/pofx/WDF/Driver/MultiComp/driver/HwSim.c b/pofx/WDF/Driver/MultiComp/driver/HwSim.c
new file mode 100644
index 00000000..b3c04d86
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/driver/HwSim.c
@@ -0,0 +1,251 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ HwSim.c
+
+Abstract:
+ This module implements a simple hardware simulator that simulates reading of
+ data from the device's components. In this sample, the "data" that is read
+ is simply the bitwise complement of the component number. In other words,
+ the data for component number 'X' is simply '~X'.
+
+ The hardware simulator also verifies that when a component is read, the
+ device is in D0 and the component is in F0. If not, it breaks into the
+ debugger.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "HwSim.h"
+#include "HwSim.tmh"
+
+NTSTATUS
+HwSimInitialize(
+ _In_ WDFDEVICE Device
+ )
+/*++
+Routine Description:
+
+ This routine initializes the hardware simulator
+
+Arguments:
+
+ Device - Handle to the framework device object
+
+Return Value:
+
+ An NTSTATUS value representing success or failure of the function.
+
+--*/
+{
+ NTSTATUS status;
+ WDF_OBJECT_ATTRIBUTES objectAttributes;
+ PHWSIM_CONTEXT devCtx;
+
+ //
+ // Allocate our context for this device
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes,
+ HWSIM_CONTEXT);
+ status = WdfObjectAllocateContext((WDFOBJECT) Device,
+ &objectAttributes,
+ (PVOID*) &devCtx);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfObjectAllocateContext failed with %!status!",
+ status);
+ goto exit;
+ }
+
+ devCtx->FirstD0Entry = TRUE;
+
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+VOID
+HwSimD0Entry(
+ _In_ WDFDEVICE Device
+ )
+/*++
+Routine Description:
+
+ This routine simulates the device entering D0
+
+Arguments:
+
+ Device - Handle to the framework device object
+
+Return Value:
+
+ None
+
+--*/
+{
+ PHWSIM_CONTEXT devCtx;
+ ULONG i;
+
+ devCtx = HwSimGetDeviceContext(Device);
+
+ if (devCtx->FirstD0Entry) {
+ devCtx->FirstD0Entry = FALSE;
+
+ //
+ // Put all components in F0
+ //
+ for (i=0; i < COMPONENT_COUNT; i++) {
+ devCtx->ComponentFState[i] = 0;
+ }
+ }
+
+ devCtx->DevicePoweredOn = TRUE;
+
+ return;
+}
+
+VOID
+HwSimD0Exit(
+ _In_ WDFDEVICE Device
+ )
+/*++
+Routine Description:
+
+ This routine simulates the device exiting D0
+
+Arguments:
+
+ Device - Handle to the framework device object
+
+Return Value:
+
+ None
+
+--*/
+{
+ PHWSIM_CONTEXT devCtx;
+
+ devCtx = HwSimGetDeviceContext(Device);
+
+ devCtx->DevicePoweredOn = FALSE;
+
+ return;
+}
+
+VOID
+HwSimFStateChange(
+ _In_ WDFDEVICE Device,
+ _In_ ULONG Component,
+ _In_ ULONG State
+ )
+/*++
+Routine Description:
+
+ This routine simulates a component changing its F-state
+
+Arguments:
+
+ Device - Handle to the framework device object
+
+ Component - Index of the component whose F-state is changing
+
+ State - New F-state for the component
+
+Return Value:
+
+ None
+
+--*/
+{
+ PHWSIM_CONTEXT devCtx;
+
+ devCtx = HwSimGetDeviceContext(Device);
+
+ //
+ // The caller should have ensured that this routine is called only when the
+ // device is powered on. Verify this. If false, we'll generate a verifier
+ // breakpoint.
+ //
+ if (FALSE == devCtx->DevicePoweredOn) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Expected device to be powered on, but it was not.");
+ WdfVerifierDbgBreakPoint();
+ }
+
+ //
+ // Put the component in the requested F-state
+ //
+ devCtx->ComponentFState[Component] = State;
+ return;
+}
+
+ULONG
+HwSimReadComponent(
+ _In_ WDFDEVICE Device,
+ _In_ ULONG Component
+ )
+/*++
+Routine Description:
+
+ This routine simulates the reading of data from a component
+
+Arguments:
+
+ Device - Handle to the framework device object
+
+ Component - Component from which data is being read
+
+Return Value:
+
+ A ULONG value representing the data that was read from the component
+
+--*/
+{
+ ULONG componentData;
+ PHWSIM_CONTEXT devCtx;
+
+ devCtx = HwSimGetDeviceContext(Device);
+
+ //
+ // The caller should have ensured that this routine is called only when the
+ // device is powered on. Verify this. If false, we'll generate a verifier
+ // breakpoint.
+ //
+ if (FALSE == devCtx->DevicePoweredOn) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Expected device to be powered on, but it was not.");
+ WdfVerifierDbgBreakPoint();
+ }
+
+ //
+ // The caller should have ensured that this routine is called only when the
+ // component is in F0. Verify this. If false, we'll generate a verifier
+ // breakpoint.
+ //
+ if (0 != devCtx->ComponentFState[Component]) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Expected component %d to be in F0, but it was in F%d.",
+ Component,
+ devCtx->ComponentFState[Component]);
+ WdfVerifierDbgBreakPoint();
+ }
+
+ //
+ // In this sample, component data is just a bit-wise complement of the
+ // component number.
+ //
+ componentData = ~Component;
+ return componentData;
+}
diff --git a/pofx/WDF/Driver/MultiComp/driver/HwSim.h b/pofx/WDF/Driver/MultiComp/driver/HwSim.h
new file mode 100644
index 00000000..f23395a3
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/driver/HwSim.h
@@ -0,0 +1,83 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ HwSim.h
+
+Abstract:
+ Header file for the hardware simulation module of the KMDF sample driver for
+ a multi-component device.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "WdfMultiComp.h"
+
+#if !defined(_HWSIM_H_)
+#define _HWSIM_H_
+
+NTSTATUS
+HwSimInitialize(
+ _In_ WDFDEVICE Device
+ );
+
+VOID
+HwSimD0Entry(
+ _In_ WDFDEVICE Device
+ );
+
+VOID
+HwSimD0Exit(
+ _In_ WDFDEVICE Device
+ );
+
+VOID
+HwSimFStateChange(
+ _In_ WDFDEVICE Device,
+ _In_ ULONG Component,
+ _In_ ULONG State
+ );
+
+ULONG
+HwSimReadComponent(
+ _In_ WDFDEVICE Device,
+ _In_ ULONG Component
+ );
+
+//
+// This structure represents the hardware simulation module's device context
+// space
+//
+typedef struct _HWSIM_CONTEXT {
+ //
+ // The following array tracks the F-state for each of the device's
+ // components. It is used for verification purposes only.
+ //
+ ULONG ComponentFState[COMPONENT_COUNT];
+
+ //
+ // The following member tracks whether or not the device is in D0. It is
+ // used for verification purposes only.
+ //
+ BOOLEAN DevicePoweredOn;
+
+ //
+ // The following member tracks whether or not we have previously entered the
+ // D0 state for this device
+ //
+ BOOLEAN FirstD0Entry;
+} HWSIM_CONTEXT, *PHWSIM_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(HWSIM_CONTEXT, HwSimGetDeviceContext)
+
+#endif // _HWSIM_H_
diff --git a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.c b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.c
new file mode 100644
index 00000000..f424c6b7
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.c
@@ -0,0 +1,1036 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ WdfMultiComp.c
+
+Abstract:
+ This module implements a KMDF sample driver for a multi-component device.
+ The driver uses the power framework to manage the power state of the
+ components of its device.
+
+ The device used in this sample is a root-enumerated device whose components
+ are simulated entirely in software. The simulation of the components is
+ implemented in HwSim.h and HwSim.c.
+
+ The driver's interaction with the power framework is encapsulated in a
+ separate library named WdfPoFx.lib. The driver statically links to this
+ library.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "WdfMultiComp.h"
+#include "HwSim.h"
+#include "WdfMultiComp.tmh"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text (INIT, DriverEntry)
+#pragma alloc_text (PAGE, MCompEvtDriverCleanup)
+#pragma alloc_text (PAGE, MCompEvtDeviceAdd)
+#pragma alloc_text (PAGE, MCompEvtDeviceD0Exit)
+#endif
+
+NTSTATUS
+DriverEntry(
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_ PUNICODE_STRING RegistryPath
+ )
+/*++
+
+Routine Description:
+
+ Driver initialization entry point. This entry point is called directly by
+ the I/O system.
+
+Arguments:
+
+ DriverObject - pointer to the driver object
+
+ RegistryPath - pointer to a unicode string representing the path to the
+ driver-specific key in the registry.
+
+Return Value:
+
+ An NTSTATUS value representing success or failure of the function.
+
+--*/
+{
+ WDF_DRIVER_CONFIG config;
+ WDF_OBJECT_ATTRIBUTES attributes;
+ NTSTATUS status;
+ WDFDRIVER hDriver;
+
+ WPP_INIT_TRACING(DriverObject, RegistryPath);
+
+ //
+ // Create a framework driver object to represent our driver.
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.EvtCleanupCallback = MCompEvtDriverCleanup;
+ WDF_DRIVER_CONFIG_INIT(
+ &config,
+ MCompEvtDeviceAdd
+ );
+
+ status = WdfDriverCreate(DriverObject,
+ RegistryPath,
+ &attributes,
+ &config,
+ &hDriver);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfDriverCreate failed with %!status!",
+ status);
+ WPP_CLEANUP(DriverObject);
+ }
+
+ return status;
+}
+
+VOID
+MCompEvtDriverCleanup(
+ _In_ WDFOBJECT Driver
+ )
+/*++
+
+Routine Description:
+
+ This routine is invoked when the framework driver object that was created in
+ DriverEntry is about to be deleted.
+
+Arguments:
+
+ Driver - Handle to the framework driver object created in DriverEntry
+
+Return Value:
+
+ None
+
+--*/
+{
+ PAGED_CODE();
+
+ WPP_CLEANUP(WdfDriverWdmGetDriverObject((WDFDRIVER) Driver));
+}
+
+NTSTATUS
+InitializePowerFrameworkSettings(
+ _In_ WDFDEVICE Device
+ )
+/*++
+Routine Description:
+
+ This routine initializes the power framework helper.
+
+Arguments:
+
+ Device - Handle to the framework device object
+
+Return Value:
+
+ An NTSTATUS value representing success or failure of the function.
+
+--*/
+{
+ NTSTATUS status;
+ PO_FX_DEVICE_MCOMP_EXT poFxDevice;
+ PPO_FX_DEVICE poFxDevicePtr;
+ PO_FX_COMPONENT_IDLE_STATE idleState[2]; // F0 and F1 only
+ ULONG i;
+
+ RtlZeroMemory(&poFxDevice, sizeof(poFxDevice));
+ RtlZeroMemory(idleState, sizeof(idleState));
+
+ //
+ // Specify callbacks and context
+ //
+ poFxDevicePtr = (PPO_FX_DEVICE) &poFxDevice;
+ poFxDevicePtr->Version = PO_FX_VERSION_V1;
+ poFxDevicePtr->ComponentIdleStateCallback =
+ MCompComponentIdleStateCallback;
+ poFxDevicePtr->ComponentActiveConditionCallback = NULL;
+ poFxDevicePtr->ComponentIdleConditionCallback = NULL;
+ poFxDevicePtr->DevicePowerRequiredCallback = NULL;
+ poFxDevicePtr->DevicePowerNotRequiredCallback = NULL;
+ poFxDevicePtr->DeviceContext = Device;
+ poFxDevicePtr->ComponentCount = COMPONENT_COUNT;
+
+ //
+ // Initialize F-state-related information
+ // NOTE: We're making the F1 specification be the same for all components,
+ // but it does not have to be that way.
+ //
+
+ //
+ // Transition latency should be 0 for F0
+ //
+ idleState[0].TransitionLatency = 0;
+
+ //
+ // Residency requirement should be 0 for F0
+ //
+ idleState[0].ResidencyRequirement = 0;
+
+ //
+ // Nominal power for F0
+ //
+ idleState[0].NominalPower = PO_FX_UNKNOWN_POWER;
+
+ //
+ // Transition latency for F1
+ //
+ idleState[1].TransitionLatency = TRANSITION_LATENCY_F1;
+
+ //
+ // Residency requirement for F1
+ //
+ idleState[1].ResidencyRequirement = RESIDENCY_REQUIREMENT_F1;
+
+ //
+ // Nominal power for F1
+ //
+ idleState[1].NominalPower = PO_FX_UNKNOWN_POWER;
+
+ for (i=0; i < COMPONENT_COUNT; i++) {
+ //
+ // We're declaring that each of components support only F0 and F1
+ //
+ poFxDevicePtr->Components[i].IdleStateCount = 2;
+
+ //
+ // Specify the F-state information
+ //
+ poFxDevicePtr->Components[i].IdleStates = idleState;
+ }
+
+ //
+ // Provide the power framework settings to the power framework helper
+ //
+ status = PfhInitializePowerFrameworkSettings(Device, poFxDevicePtr);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+NTSTATUS
+CreateDevice(
+ _In_ WDFOBJECT PfhInitializer,
+ _In_ PWDFDEVICE_INIT DeviceInit,
+ _Out_ WDFDEVICE * Device
+ )
+/*++
+Routine Description:
+
+ This routine creates the KMDF device object and also initializes the power
+ framework helper's settings for this device object.
+
+Arguments:
+
+ PfhInitializer - Handle to the initializer object for initializing the power
+ framework helper.
+
+ DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
+
+ Device - Handle to the KMDF device object created by this routine.
+
+Return Value:
+
+ An NTSTATUS value representing success or failure of the function.
+
+--*/
+{
+ NTSTATUS status;
+ WDF_OBJECT_ATTRIBUTES objectAttributes;
+ WDFDEVICE device = NULL;
+ WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks;
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS s0IdleSettings;
+
+ //
+ // Register the PNP/power callbacks
+ //
+ // Note: This is the function driver for the device, so the framework
+ // automatically enables power policy ownership.
+ //
+ WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks);
+ pnpCallbacks.EvtDeviceD0Entry = MCompEvtDeviceD0Entry;
+ pnpCallbacks.EvtDeviceD0Exit = MCompEvtDeviceD0Exit;
+
+ //
+ // Get the power framework helper to override our PNP/power callbacks
+ //
+ PfhInterceptWdfPnpPowerEventCallbacks(PfhInitializer, &pnpCallbacks);
+ WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpCallbacks);
+
+ //
+ // Give the power framework helper an opportunity to register WDM IRP
+ // preprocess callbacks for power IRPs
+ //
+ status = PfhAssignWdmPowerIrpPreProcessCallback(
+ PfhInitializer,
+ DeviceInit,
+ NULL, // EvtDeviceWdmPowerIrpPreprocess
+ NULL, // MinorFunctions
+ 0 // NumMinorFunctions
+ );
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+
+ //
+ // Specify the context for the device
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes,
+ DEVICE_EXTENSION);
+
+ //
+ // Create a framework device object
+ //
+ status = WdfDeviceCreate(&DeviceInit, &objectAttributes, &device);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfDeviceCreate failed with %!status!",
+ status);
+ goto exit;
+ }
+
+ //
+ // Specify the POHANDLE availability callbacks
+ //
+ PfhSetPoHandleAvailabilityCallbacks(PfhInitializer,
+ MCompPoHandleAvailable,
+ MCompPoHandleUnavailable);
+
+ //
+ // Enable S0-idle power management
+ //
+ // Note:
+ // * We set IdleTimeoutType = DriverManagedIdleTimeout to make sure KMDF
+ // doesn't register with the power framework on our behalf, because we
+ // will be directly registering with the power framework ourselves.
+ // * We set PowerUpIdleDeviceOnSystemWake = TRUE because when we register
+ // with power framework, we'd like to return to D0 immediately after the
+ // system returns to S0. It is a power framework requirement that once
+ // the system returns to S0, then device must return to D0 and the
+ // driver must invoke PoFxReportDevicePoweredOn (the power framework
+ // helper library does this on our behalf).
+ // * We pick a really small idle timeout value so that KMDF can power down
+ // the device almost immediately after the power framework allows it to
+ // do so.
+ //
+ // If S0-idle power management support is not required, then the following
+ // modifications should be made:
+ // 1. Recompile the power framework helper library with the value of the
+ // WDFPOFX_S0IDLE_SUPPORTED compiler switch set to 0. This omits the
+ // code that is specific to S0-idle power management, thereby reducing
+ // the size of the library.
+ // 2. Remove the calls to PfhSetS0IdleConfiguration and
+ // WdfDeviceAssignS0IdleSettings below.
+ //
+ PfhSetS0IdleConfiguration(PfhInitializer, PfhS0IdleSupportedPowerPageable);
+
+ WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&s0IdleSettings,
+ IdleCannotWakeFromS0);
+ s0IdleSettings.IdleTimeoutType = DriverManagedIdleTimeout;
+ s0IdleSettings.PowerUpIdleDeviceOnSystemWake = WdfTrue;
+ s0IdleSettings.IdleTimeout = 1; // 1 millisecond
+ status = WdfDeviceAssignS0IdleSettings(device, &s0IdleSettings);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfDeviceAssignS0IdleSettings failed with %!status!",
+ status);
+ goto exit;
+ }
+
+ //
+ // Get the power framework helper to initialize its settings for this device
+ //
+ status = PfhInitializeDeviceSettings(device, PfhInitializer);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+
+ *Device = device;
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+NTSTATUS
+CreateComponentQueues(
+ _In_ WDFOBJECT PfhInitializer,
+ _In_ WDFDEVICE Device
+ )
+/*++
+Routine Description:
+
+ This routine creates the component queues and also initializes the power
+ framework helper's settings for each of them.
+
+Arguments:
+
+ PfhInitializer - Handle to the initializer object for initializing the power
+ framework helper.
+
+ Device - Handle to the framework device object
+
+Return Value:
+
+ An NTSTATUS value representing success or failure of the function.
+
+--*/
+{
+ NTSTATUS status;
+ PDEVICE_EXTENSION devExt;
+ ULONG i;
+ WDF_IO_QUEUE_CONFIG ioQueueConfig;
+
+ //
+ // Get the device extension
+ //
+ devExt = DeviceGetData(Device);
+
+ for (i=0; i < COMPONENT_COUNT; i++) {
+
+ //
+ // Create the component-specific queues
+ //
+ WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchParallel);
+ ioQueueConfig.EvtIoDeviceControl = MCompEvtIoDeviceControlSecondary;
+
+ //
+ // Get the power framework helper to override the configuration for our
+ // component-specific queue
+ //
+ PfhInterceptComponentQueueConfig(PfhInitializer, &ioQueueConfig);
+
+ //
+ // Associate the queue with a component
+ //
+ PfhSetComponentForComponentQueue(PfhInitializer, i);
+
+ //
+ // Create the queue
+ //
+
+ //
+ // By default, Static Driver Verifier (SDV) displays a warning if it
+ // doesn't find the EvtIoStop callback on a power-managed queue.
+ // The 'assume' below causes SDV to suppress this warning. If the driver
+ // has not explicitly set PowerManaged to WdfFalse, the framework creates
+ // power-managed queues when the device is not a filter driver. Normally
+ // the EvtIoStop is required for power-managed queues, but for this driver
+ // it is not needed b/c the driver doesn't hold on to the requests or
+ // forward them to other drivers. This driver completes the requests
+ // directly in the queue's handlers. If the EvtIoStop callback is not
+ // implemented, the framework waits for all driver-owned requests to be
+ // done before moving in the Dx/sleep states or before removing the
+ // device, which is the correct behavior for this type of driver.
+ // If the requests were taking an indeterminate amount of time to complete,
+ // or if the driver forwarded the requests to a lower driver/another stack,
+ // the queue should have an EvtIoStop/EvtIoResume.
+ //
+ __analysis_assume(ioQueueConfig.EvtIoStop != 0);
+ status = WdfIoQueueCreate(Device,
+ &ioQueueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &(devExt->Queue[i]));
+ __analysis_assume(ioQueueConfig.EvtIoStop == 0);
+
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfIoQueueCreate failed with %!status! when "
+ "creating queue for component %d",
+ status,
+ i);
+ goto exit;
+ }
+
+ //
+ // Get the power framework helper to initialize its settings for this
+ // component-specific queue
+ //
+ status = PfhInitializeComponentQueueSettings(devExt->Queue[i],
+ PfhInitializer);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+ }
+
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+NTSTATUS
+MCompEvtDeviceAdd(
+ _In_ WDFDRIVER Driver,
+ _Inout_ PWDFDEVICE_INIT DeviceInit
+ )
+/*++
+Routine Description:
+
+ EvtDeviceAdd is called by the framework in response to AddDevice call from
+ the PnP manager.
+
+Arguments:
+
+ Driver - Handle to the framework driver object created in DriverEntry
+
+ DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
+
+Return Value:
+
+ An NTSTATUS value representing success or failure of the function.
+
+--*/
+{
+ NTSTATUS status;
+ WDFOBJECT pfhInitializer = NULL;
+ WDFDEVICE device = NULL;
+ WDF_IO_QUEUE_CONFIG ioQueueConfig;
+ WDFQUEUE sourceQueue = NULL;
+
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(Driver);
+
+ //
+ // Create an initializer object for the power framework helper
+ //
+ status = PfhInitializerCreate(&pfhInitializer);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+
+ //
+ // Create the device object
+ //
+ status = CreateDevice(pfhInitializer, DeviceInit, &device);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+
+ //
+ // Create the source queue.
+ //
+ WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig,
+ WdfIoQueueDispatchParallel);
+ ioQueueConfig.EvtIoDeviceControl = MCompEvtIoDeviceControlPrimary;
+
+ //
+ // By default, Static Driver Verifier (SDV) displays a warning if it
+ // doesn't find the EvtIoStop callback on a power-managed queue.
+ // The 'assume' below causes SDV to suppress this warning. If the driver
+ // has not explicitly set PowerManaged to WdfFalse, the framework creates
+ // power-managed queues when the device is not a filter driver. Normally
+ // the EvtIoStop is required for power-managed queues, but for this driver
+ // it is not needed b/c the driver doesn't hold on to the requests or
+ // forward them to other drivers. This driver completes the requests
+ // directly in the queue's handlers. If the EvtIoStop callback is not
+ // implemented, the framework waits for all driver-owned requests to be
+ // done before moving in the Dx/sleep states or before removing the
+ // device, which is the correct behavior for this type of driver.
+ // If the requests were taking an indeterminate amount of time to complete,
+ // or if the driver forwarded the requests to a lower driver/another stack,
+ // the queue should have an EvtIoStop/EvtIoResume.
+ //
+ __analysis_assume(ioQueueConfig.EvtIoStop != 0);
+ status = WdfIoQueueCreate(device,
+ &ioQueueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &sourceQueue);
+ __analysis_assume(ioQueueConfig.EvtIoStop == 0);
+
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfIoQueueCreate failed with %!status! when creating "
+ "source queue.",
+ status);
+ goto exit;
+ }
+
+ //
+ // Initialize power framework settings
+ //
+ status = InitializePowerFrameworkSettings(device);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+
+ //
+ // Create the component queues
+ //
+ status = CreateComponentQueues(pfhInitializer, device);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+
+ //
+ // Create a device interface so that applications can open a handle to this
+ // device.
+ //
+ status = WdfDeviceCreateDeviceInterface(device,
+ &GUID_DEVINTERFACE_POWERFX,
+ NULL /* ReferenceString */);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfDeviceCreateDeviceInterface failed with %!status!.",
+ status);
+ goto exit;
+ }
+
+ //
+ // Initialize the hardware simulator
+ //
+ status = HwSimInitialize(device);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+
+ status = STATUS_SUCCESS;
+
+exit:
+ if (NULL != pfhInitializer) {
+ WdfObjectDelete(pfhInitializer);
+ }
+ return status;
+}
+
+NTSTATUS
+MCompPoHandleAvailable(
+ _In_ WDFDEVICE Device,
+ _In_ POHANDLE PoHandle
+ )
+{
+ PDEVICE_EXTENSION devExt = NULL;
+ ULONG i;
+
+ //
+ // Get the device extension
+ //
+ devExt = DeviceGetData(Device);
+
+ //
+ // Save the POHANDLE
+ //
+ devExt->PoHandle = PoHandle;
+
+ //
+ // Provide latency and residency hints to enable the power framework to
+ // select lower-powered F-states when we are idle.
+ //
+ for (i=0; i < COMPONENT_COUNT; i++) {
+ PoFxSetComponentLatency(
+ PoHandle,
+ i,
+ (TRANSITION_LATENCY_F1 + 1)
+ );
+ PoFxSetComponentResidency(
+ PoHandle,
+ i,
+ (RESIDENCY_REQUIREMENT_F1 + 1)
+ );
+ }
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+MCompPoHandleUnavailable(
+ _In_ WDFDEVICE Device,
+ _In_ POHANDLE PoHandle
+ )
+{
+ PDEVICE_EXTENSION devExt = NULL;
+
+ //
+ // Get the device extension
+ //
+ devExt = DeviceGetData(Device);
+
+ //
+ // Clear the POHANDLE. It is not guaranteed to remain valid after we return
+ // from this callback.
+ //
+ devExt->PoHandle = PoHandle;
+
+ return;
+}
+
+NTSTATUS
+MCompEvtDeviceD0Entry(
+ _In_ WDFDEVICE Device,
+ _In_ WDF_POWER_DEVICE_STATE PreviousState
+ )
+/*++
+Routine Description:
+
+ In this routine the driver enters D0
+
+Arguments:
+
+ Device - Handle to the framework device object
+
+ PreviousState - Previous device power state
+
+Return Value:
+
+ An NTSTATUS value representing success or failure of the function.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(PreviousState);
+
+ HwSimD0Entry(Device);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+MCompEvtDeviceD0Exit(
+ _In_ WDFDEVICE Device,
+ _In_ WDF_POWER_DEVICE_STATE TargetState
+ )
+/*++
+Routine Description:
+
+ In this routine the driver leaves D0
+
+Arguments:
+
+ Device - Handle to the framework device object
+
+ TargetState - Device power state that the device is about to enter
+
+Return Value:
+
+ An NTSTATUS value representing success or failure of the function.
+
+--*/
+{
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER(TargetState);
+
+ HwSimD0Exit(Device);
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+MCompComponentIdleStateCallback(
+ _In_ PVOID Context,
+ _In_ ULONG Component,
+ _In_ ULONG State
+ )
+/*++
+Routine Description:
+
+ The power framework helper invokes this routine to change the F-state of one
+ of our components.
+
+Arguments:
+
+ Context - Context that we passed in to our power framework helper module
+ when we asked it to register with the power framework helper on our behalf
+
+ Component - Index of component for which the F-state change is to be made
+
+ State - The new F-state to transition the component to
+
+Return Value:
+
+ None
+
+--*/
+{
+ PDEVICE_EXTENSION devExt = NULL;
+ WDFDEVICE device = NULL;
+
+ //
+ // Get the device object handle
+ //
+ device = (WDFDEVICE) Context;
+
+ //
+ // Get the device extension
+ //
+ devExt = DeviceGetData(device);
+
+ //
+ // Change the F-state of the component
+ // This includes hardware-specific operations such as:
+ // * disabling/enabling DMA capabilities associated with the component
+ // * disabling/enabling interrupts associated with the component
+ // * saving/restoring component state
+ //
+ HwSimFStateChange(device, Component, State);
+
+ //
+ // F-state transition complete
+ //
+ PoFxCompleteIdleState(devExt->PoHandle, Component);
+ return;
+}
+
+VOID
+MCompEvtIoDeviceControlPrimary(
+ _In_ WDFQUEUE Queue,
+ _In_ WDFREQUEST Request,
+ _In_ size_t OutputBufferLength,
+ _In_ size_t InputBufferLength,
+ _In_ ULONG IoControlCode
+ )
+/*++
+Routine Description:
+
+ This routine is the IOCTL handler for the device's primary queue
+
+Arguments:
+
+ Queue - Handle to the framework queue object for the primary queue
+
+ Request - Handle to the framework request object for IOCTL being dispatched
+
+ OutputBufferLength - Output buffer length for IOCTL
+
+ InputBufferLength - Input buffer length for IOCTL
+
+ IoControlCode - IOCTL code
+
+Return Value:
+
+ None
+
+--*/
+{
+ NTSTATUS status;
+ PPOWERFX_READ_COMPONENT_INPUT inputBuffer = NULL;
+ WDFDEVICE device = NULL;
+ PDEVICE_EXTENSION devExt = NULL;
+ WDFQUEUE componentQueue = NULL;
+ ULONG component;
+
+ UNREFERENCED_PARAMETER(OutputBufferLength);
+
+ switch (IoControlCode) {
+ case IOCTL_POWERFX_READ_COMPONENT:
+ {
+ //
+ // Validate input buffer length
+ //
+ if (InputBufferLength != sizeof(*inputBuffer)) {
+ status = STATUS_INVALID_PARAMETER;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! -Invalid input buffer size. Expected: %d. "
+ "Actual: %I64u. %!status!.",
+ sizeof(*inputBuffer),
+ InputBufferLength,
+ status);
+ goto exit;
+ }
+
+ //
+ // Identify the component that we need to read in order to satisfy
+ // this request
+ //
+ status = WdfRequestRetrieveInputBuffer(Request,
+ sizeof(*inputBuffer),
+ (PVOID*) &inputBuffer,
+ NULL // Length
+ );
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfRequestRetrieveInputBuffer failed with "
+ "%!status!.",
+ status);
+ goto exit;
+ }
+ component = inputBuffer->ComponentNumber;
+
+ //
+ // Identify the queue corresponding to the above component
+ //
+ device = WdfIoQueueGetDevice(Queue);
+ devExt = DeviceGetData(device);
+ componentQueue = devExt->Queue[component];
+ }
+ break;
+
+ default:
+ {
+ status = STATUS_INVALID_PARAMETER;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unexpected IOCTL code %d. %!status!.",
+ IoControlCode,
+ status);
+ goto exit;
+ }
+ }
+
+ //
+ // Forward the request to the appropriate queue. We do this via the power
+ // framework helper so that it can take a power reference on behalf of the
+ // request.
+ //
+ PfhForwardRequestToQueue(Request, componentQueue);
+
+ status = STATUS_SUCCESS;
+
+exit:
+ if (FALSE == NT_SUCCESS(status)) {
+ //
+ // Complete the request in case of failure. In case of success, we would
+ // have dispatched the request to the component-specific queue, so we
+ // don't need to complete it here.
+ //
+ PfhCompleteRequest(Request, status, 0 /* Information */);
+ }
+
+ return;
+}
+
+VOID
+MCompEvtIoDeviceControlSecondary(
+ _In_ WDFQUEUE Queue,
+ _In_ WDFREQUEST Request,
+ _In_ size_t OutputBufferLength,
+ _In_ size_t InputBufferLength,
+ _In_ ULONG IoControlCode
+ )
+/*++
+Routine Description:
+
+ This routine is the IOCTL handler for the device's secondary queue (i.e.
+ component-specific queue).
+
+Arguments:
+
+ Queue - Handle to the framework queue object for the secondary queue
+
+ Request - Handle to the framework request object for IOCTL being dispatched
+
+ OutputBufferLength - Output buffer length for IOCTL
+
+ InputBufferLength - Input buffer length for IOCTL
+
+ IoControlCode - IOCTL code
+
+Return Value:
+
+ None
+
+--*/
+{
+ NTSTATUS status;
+ PPOWERFX_READ_COMPONENT_INPUT inputBuffer = NULL;
+ PPOWERFX_READ_COMPONENT_OUTPUT outputBuffer = NULL;
+ WDFDEVICE device = NULL;
+ ULONG component;
+ ULONG componentData;
+ ULONG_PTR information = 0;
+
+ //
+ // When we complete the request, make sure we don't get the I/O manager to
+ // copy any more data to the client address space than what we write to the
+ // output buffer. The only data that we write to the output buffer is the
+ // component data and the C_ASSERT below ensures that the output buffer does
+ // not have room to contain anything other than that.
+ //
+ C_ASSERT(sizeof(componentData) == sizeof(*outputBuffer));
+
+ UNREFERENCED_PARAMETER(InputBufferLength);
+ UNREFERENCED_PARAMETER(IoControlCode);
+
+ //
+ // The following ASSERTMSG checks should have already been made by the IOCTL
+ // dispatch routine for the primary queue. That routine should have
+ // completed the request with failure if the checks had failed. Therefore we
+ // only use ASSERTMSG here.
+ //
+ ASSERTMSG("Unexpected IOCTL code\n",
+ IOCTL_POWERFX_READ_COMPONENT == IoControlCode);
+ ASSERTMSG("Invalid input buffer size",
+ InputBufferLength == sizeof(*inputBuffer));
+
+ //
+ // Validate output buffer length
+ //
+ if (OutputBufferLength != sizeof(*outputBuffer)) {
+ status = STATUS_INVALID_PARAMETER;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! -Invalid input buffer size. Expected: %d. Actual: %I64u."
+ " %!status!.",
+ sizeof(*outputBuffer),
+ OutputBufferLength,
+ status);
+ goto exit;
+ }
+
+ //
+ // Identify the component that we need to read in order to satisfy
+ // this request.
+ //
+ status = WdfRequestRetrieveInputBuffer(Request,
+ sizeof(*inputBuffer),
+ (PVOID*) &inputBuffer,
+ NULL // Length
+ );
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfRequestRetrieveInputBuffer failed with %!status!.",
+ status);
+ goto exit;
+ }
+ component = inputBuffer->ComponentNumber;
+
+ //
+ // Get the output buffer
+ //
+ status = WdfRequestRetrieveOutputBuffer(Request,
+ sizeof(*outputBuffer),
+ (PVOID*) &outputBuffer,
+ NULL // Length
+ );
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfRequestRetrieveOutputBuffer failed with %!status!.",
+ status);
+ goto exit;
+ }
+
+ //
+ // Read the data from the component
+ //
+ device = WdfIoQueueGetDevice(Queue);
+ componentData = HwSimReadComponent(device, component);
+ outputBuffer->ComponentData = componentData;
+ information = sizeof(*outputBuffer);
+
+ status = STATUS_SUCCESS;
+
+exit:
+ //
+ // Complete the request
+ //
+ PfhCompleteRequest(Request, status, information);
+ return;
+}
diff --git a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.h b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.h
new file mode 100644
index 00000000..0d56edc8
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.h
@@ -0,0 +1,114 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ WdfMultiComp.h
+
+Abstract:
+ Header file for the KMDF sample driver for a multi-component device.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#if !defined(_WDFMULTICOMP_H_)
+#define _WDFMULTICOMP_H_
+
+
+#include <ntddk.h>
+#include <wdf.h>
+#include "AppInterface.h"
+#include "WdfPoFx.h"
+
+//
+// Some dummy values
+//
+#define TRANSITION_LATENCY_F1 WDF_ABS_TIMEOUT_IN_MS(5) // 5 milliseconds
+#define RESIDENCY_REQUIREMENT_F1 WDF_ABS_TIMEOUT_IN_MS(50) // 50 milliseconds
+
+//
+// This structure contains the driver's power framework settings
+//
+typedef struct _PO_FX_DEVICE_MCOMP_EXT {
+ PO_FX_DEVICE PoFxDevice;
+ PO_FX_COMPONENT AdditionalComponents[COMPONENT_COUNT-1];
+} PO_FX_DEVICE_MCOMP_EXT, *PPO_FX_DEVICE_MCOMP_EXT;
+
+//
+// This structure represents the driver's device context space
+//
+typedef struct _DEVICE_EXTENSION {
+ //
+ // Array of component queues
+ //
+ WDFQUEUE Queue[COMPONENT_COUNT];
+
+ //
+ // POHANDLE representing the device's registration with the power framework
+ //
+ POHANDLE PoHandle;
+
+} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_EXTENSION, DeviceGetData)
+
+//
+// Driver's KMDF callbacks
+//
+DRIVER_INITIALIZE DriverEntry;
+EVT_WDF_OBJECT_CONTEXT_CLEANUP MCompEvtDriverCleanup;
+EVT_WDF_DRIVER_DEVICE_ADD MCompEvtDeviceAdd;
+EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL MCompEvtIoDeviceControlPrimary;
+EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL MCompEvtIoDeviceControlSecondary;
+EVT_WDF_DEVICE_D0_ENTRY MCompEvtDeviceD0Entry;
+EVT_WDF_DEVICE_D0_EXIT MCompEvtDeviceD0Exit;
+
+//
+// Driver's power framework callbacks
+//
+PO_FX_COMPONENT_IDLE_STATE_CALLBACK MCompComponentIdleStateCallback;
+
+//
+// Driver's power framework helper callbacks
+//
+PFH_CALLBACK_POHANDLE_AVAILABLE MCompPoHandleAvailable;
+PFH_CALLBACK_POHANDLE_UNAVAILABLE MCompPoHandleUnavailable;
+
+//
+// Define the tracing flags.
+//
+// TODO: Use a different trace control GUID below
+//
+#define WPP_CONTROL_GUIDS \
+ WPP_DEFINE_CONTROL_GUID( \
+ WdfMCompTraceControl, (C3061C7C,5719,4700,BD4D,A12B40347632), \
+ WPP_DEFINE_BIT(WDFMCOMP_ALL_INFO) \
+ )
+
+#define WPP_FLAG_LEVEL_LOGGER(flag, level) \
+ WPP_LEVEL_LOGGER(flag)
+
+#define WPP_FLAG_LEVEL_ENABLED(flag, level) \
+ (WPP_LEVEL_ENABLED(flag) && \
+ WPP_CONTROL(WPP_BIT_ ## flag).Level >= level)
+
+//
+// This comment block is scanned by the trace preprocessor to define our
+// Trace function.
+//
+// begin_wpp config
+// FUNC Trace{FLAG=WDFMCOMP_ALL_INFO}(LEVEL, MSG, ...);
+// end_wpp
+//
+
+
+#endif // _WDFMULTICOMP_H_
diff --git a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.inx b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.inx
new file mode 100644
index 00000000..e6b93ed0
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.inx
@@ -0,0 +1,79 @@
+;
+; WdfMultiComp.inf
+;
+
+[Version]
+Signature="$Windows NT$"
+Class=Sample
+ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171}
+Provider=%MSFT%
+CatalogFile=KmdfSamples.cat
+DriverVer=03/27/2011,0.0.0.1
+
+[DestinationDirs]
+WdfMultiCompDriverCopy=12 ; copy to drivers
+CoInstaller_CopyFiles = 11; copy to system32
+
+[SourceDisksNames]
+1 = %MediaDescription%
+
+[SourceDisksFiles]
+WdfMultiComp.sys=1
+WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1
+
+; =================== Class install ==================================
+
+[ClassInstall32]
+AddReg=SampleClassReg
+
+[SampleClassReg]
+HKR,,,,%ClassName%
+HKR,,Icon,,-5
+
+; =================== KMDF Multi-Component Device ==================================
+
+[Manufacturer]
+%MSFT%=Microsoft,NT$ARCH$
+
+[Microsoft.NT$ARCH$]
+%WdfMultiCompDeviceName%=WdfMultiComp_Install,WDF\WdfMultiComp
+
+[WdfMultiComp_Install.NT]
+CopyFiles=WdfMultiCompDriverCopy
+
+[WdfMultiCompDriverCopy]
+WdfMultiComp.sys
+
+[WdfMultiComp_Install.NT.Services]
+AddService=WdfMultiComp,0x000001fa,WdfMultiComp_ServiceInstall
+
+[WdfMultiComp_ServiceInstall]
+ServiceType=1
+StartType=3
+ErrorControl=1
+ServiceBinary=%12%\WdfMultiComp.sys
+
+[WdfMultiComp_Install.NT.CoInstallers]
+AddReg=CoInstaller_AddReg
+CopyFiles=CoInstaller_CopyFiles
+
+[CoInstaller_AddReg]
+HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller"
+
+[CoInstaller_CopyFiles]
+WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll
+
+[WdfMultiComp_Install.NT.Wdf]
+KmdfService = WdfMultiComp, WdfMultiComp_wdfsect
+
+[WdfMultiComp_wdfsect]
+KmdfLibraryVersion=$KMDFVERSION$
+
+; =================== Generic ==================================
+
+[Strings]
+MSFT="Microsoft"
+MediaDescription="KMDF Multi-Component Device Driver Installation Media"
+ClassName="Sample Device"
+WdfMultiCompDeviceName="KMDF Multi-Component Device"
+
diff --git a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj
new file mode 100644
index 00000000..c0905b5a
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{45DBDC5F-2109-432B-8149-29D3693D57E1}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{5F84F53D-DFA0-47A1-9759-FC7CFFC9599C}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems">
+ <ClCompile Include="WdfMultiComp.c; HwSim.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppScanConfigurationData>WdfMultiComp.h</WppScanConfigurationData>
+ </ClCompile>
+ <Inf Include="WdfMultiComp.inx">
+ <Architecture>$(InfArch)</Architecture>
+ <SpecifyArchitecture>true</SpecifyArchitecture>
+ <CopyOutput>.\$(IntDir)\WdfMultiComp.inf</CopyOutput>
+ </Inf>
+ </ItemGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>WdfMultiComp</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>WdfMultiComp</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>WdfMultiComp</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>WdfMultiComp</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);.\..\lib\$(IntDir)\WdfPoFx.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);.\..\lib\$(IntDir)\WdfPoFx.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);.\..\lib\$(IntDir)\WdfPoFx.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);.\..\lib\$(IntDir)\WdfPoFx.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj.Filters b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj.Filters
new file mode 100644
index 00000000..dfe3aac9
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj.Filters
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
+ <UniqueIdentifier>{A5B06A84-D70F-4D83-B76D-FEA4F09EE4FC}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{B738D047-A23C-43C1-9D52-3E0AC54B5C3D}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
+ <UniqueIdentifier>{9A02D86B-B4BE-4C57-BD3E-ADD87EE33EE9}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{000E9CB4-5A60-438C-8F77-72AE7E890BB8}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="HwSim.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WdfMultiComp.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <FilesToPackage Include=".\Debug\\WdfMultiComp.inf">
+ <Filter>Driver Files</Filter>
+ </FilesToPackage>
+ <Inf Include="WdfMultiComp.inx">
+ <Filter>Driver Files</Filter>
+ </Inf>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/pofx/WDF/Driver/MultiComp/inc/WdfPoFx.h b/pofx/WDF/Driver/MultiComp/inc/WdfPoFx.h
new file mode 100644
index 00000000..bcecdc86
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/inc/WdfPoFx.h
@@ -0,0 +1,731 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ WdfPoFx.h
+
+Abstract:
+ This header file describes the interface between the driver layer and the
+ power framework helper library.
+
+Environment:
+
+ Kernel mode
+
+--*/
+#if !defined(_WDFPOFX_H_)
+#define _WDFPOFX_H_
+
+#include <ntddk.h>
+#include <wdf.h>
+
+//
+// Routine description:
+// This routine is invoked by the power framework helper library to notify
+// the driver layer that it has registered with the power framework. The
+// driver layer can provide this callback to the power framework helper
+// library by calling PfhSetPoHandleAvailabilityCallbacks. The power
+// framework helper library invokes this routine during the processing of
+// the first start IRP for the device. (Note that the device might receive
+// more than one start IRP if a resource rebalance occurs while it is
+// operational).
+//
+// Arguments:
+// Device - Handle to the KMDF device object
+//
+// PoHandle - Handle that represents the device's registration with the
+// power framework.
+//
+// Return value:
+// An NTSTATUS value representing success or failure of the function. If
+// the driver layer returns an NTSTATUS value representing failure, the
+// power framework helper library immediately unregisters with the power
+// framework and fails the start IRP.
+//
+typedef
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+PFH_CALLBACK_POHANDLE_AVAILABLE(
+ _In_ WDFDEVICE Device,
+ _In_ POHANDLE PoHandle
+ );
+
+typedef PFH_CALLBACK_POHANDLE_AVAILABLE *PPFH_CALLBACK_POHANDLE_AVAILABLE;
+
+//
+// Routine description:
+// This routine is invoked by the power framework helper library to notify
+// the driver layer that it is about to unregister with the power
+// framework. The driver layer can provide this callback to the power
+// framework helper library by calling
+// PfhSetPoHandleAvailabilityCallbacks. The POHANDLE representing the
+// device's registration with the power framework is not guaranteed to be
+// valid after the driver layer has returned from this routine. Therefore,
+// the driver layer must stop using the POHANDLE before it returns from
+// this routine.
+//
+// Arguments:
+// Device - Handle to the KMDF device object
+//
+// PoHandle - Handle that represents the device's registration with the
+// power framework.
+//
+// Return value:
+// None
+//
+typedef
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+PFH_CALLBACK_POHANDLE_UNAVAILABLE(
+ _In_ WDFDEVICE Device,
+ _In_ POHANDLE PoHandle
+ );
+
+typedef PFH_CALLBACK_POHANDLE_UNAVAILABLE *PPFH_CALLBACK_POHANDLE_UNAVAILABLE;
+
+//
+// PfhInitializerCreate
+// ====================
+// Routine description:
+// This routine is invoked by the driver layer to create a power framework
+// helper library initializer object. This initializer object helps the
+// power framework library to initialize its own settings for KMDF objects.
+// After creating the initializer object, the driver layer passes it in
+// as an argument to several initialization methods in the power framework
+// helper library.
+//
+// If the driver layer calls any of the following methods when creating a
+// KMDF device object, the initializer object needs to be passed in as an
+// argument:
+// PfhAssignWdmPowerIrpPreProcessCallback
+// PfhInterceptWdfPnpPowerEventCallbacks
+// PfhSetPoHandleAvailabilityCallbacks
+// PfhInitializeDeviceSettings
+//
+// If the driver layer calls any of the following methods when creating a
+// KMDF queue object, the initializer object needs to be passed in as an
+// argument:
+// PfhInterceptComponentQueueConfig
+// PfhSetComponentForComponentQueue
+// PfhInitializeComponentQueueSettings
+//
+// The same initializer object can be used to initialize more than one KMDF
+// object, but at any given time it must be used to initialize only one
+// KMDF object. For example, the driver layer could use an initializer
+// object to initialize a KMDF device object and when it is done
+// initializing the device object, the same object can be used to
+// initialize a different KMDF object (not necessarily a device object).
+//
+// The initializer object is a child of the KMDF driver object and is
+// deleted by KMDF when the driver object is deleted. This means that by
+// default the initializer object remains in memory until the driver is
+// unloaded. However, the driver layer can proactively delete the
+// initializer object using the WdfObjectDelete method when it has finished
+// using it.
+//
+// Arguments:
+// Initializer - Pointer to a location that receives a handle to the new
+// initializer object.
+//
+// Return value:
+// An NTSTATUS value representing success or failure of the function.
+//
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+PfhInitializerCreate(
+ _Out_ WDFOBJECT * Initializer
+ );
+
+//
+// PfhAssignWdmPowerIrpPreProcessCallback
+// ======================================
+// Routine description:
+// This routine is invoked by the driver layer to enable the power
+// framework helper library to register a WDM preprocess callback function
+// to handle power IRPs before KMDF has a chance to process them. The
+// driver layer must invoke this routine before creating the KMDF device
+// object.
+//
+// This routine must be invoked for any device whose power management is
+// done using the power framework helper library, even if the driver layer
+// is not interested in preprocessing power IRPs for that device.
+//
+// If the driver layer also wishes to pre-process power IRPs before KMDF
+// has a chance to preprocess them, it can specify this via the arguments
+// to this routine. The driver layer must *not* directly call the
+// WdfDeviceInitAssignWdmIrpPreprocessCallback method to register a pre-
+// process callback routine for power IRPs, i.e. IRPs with a major function
+// code of IRP_MJ_POWER. Instead it must use
+// PfhAssignWdmPowerIrpPreProcessCallback to accomplish this. However, for
+// preprocessing IRPs with other major function codes, the driver should
+// use WdfDeviceInitAssignWdmIrpPreprocessCallback.
+//
+// If the driver layer specifies a preprocess callback via
+// PfhAssignWdmPowerIrpPreProcessCallback, it gets to preprocess the power
+// IRP before the power framework helper library preprocesses it. Once the
+// driver layer is done preprocessing the power IRP, it must call
+// PfhWdmDispatchPreprocessedPowerIrp to allow the power framework helper
+// library and eventually KMDF to process the IRP. The driver layer must
+// *not* directly call WdfDeviceWdmDispatchPreprocessedIrp to forward a
+// pre-processed power IRP, i.e. an IRP with major function code
+// IRP_MJ_POWER, to KMDF. Preprocessed IRPs with other major function codes
+// should be forwarded to KMDF via WdfDeviceWdmDispatchPreprocessedIrp.
+//
+// Arguments:
+// Initializer - Handle to the initializer object that is being used to
+// initialize the power framework helper library's device object
+// settings.
+//
+// DeviceInit - Pointer to a WDFDEVICE_INIT structure
+//
+// EvtDeviceWdmPowerIrpPreprocess - Pointer to the driver layer's
+// EVT_WDFDEVICE_WDM_IRP_PREPROCESS callback function for power IRPs.
+// This argument is optional. If the driver layer does not want to
+// preprocess power IRPs, it can specify NULL.
+//
+// MinorFunctions - Pointer to an array of one or more minor function codes
+// for IRP_MJ_POWER that the driver layer wants to pre-process. This
+// argument is optional. If the driver layer does not want to
+// preprocess power IRPs, it can specify NULL.
+//
+// NumMinorFunctions - Number of minor function codes that are contained in
+// the MinorFunctions array.
+//
+// Return value:
+// An NTSTATUS value representing success or failure of the function.
+//
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+PfhAssignWdmPowerIrpPreProcessCallback(
+ _In_ WDFOBJECT Initializer,
+ _In_ PWDFDEVICE_INIT DeviceInit,
+ _In_opt_ PFN_WDFDEVICE_WDM_IRP_PREPROCESS EvtDeviceWdmPowerIrpPreprocess,
+ _In_reads_opt_(NumMinorFunctions) PUCHAR MinorFunctions,
+ _In_ ULONG NumMinorFunctions
+ );
+
+//
+// PfhWdmDispatchPreprocessedPowerIrp
+// ==================================
+// Routine description:
+// This routine is invoked by the driver layer to forward a pre-processed
+// power IRP to the power framework helper library. It is similar to KMDF's
+// WdfDeviceWdmDispatchPreprocessedIrp method. For power IRPs, the driver
+// layer must use PfhWdmDispatchPreprocessedPowerIrp instead of
+// WdfDeviceWdmDispatchPreprocessedIrp. The power framework helper library
+// eventually forwards it to KMDF after pre-processing it.
+//
+// Arguments:
+// Device - Handle to a KMDF device object
+//
+// Irp - Pointer to the pre-processed IRP
+//
+// Return value:
+// An NTSTATUS value that the power framework helper library provides as a
+// result of processing the IRP. The driver *must* use this return value as
+// return value for its EvtDeviceWdmPowerIrpPreprocess callback function.
+//
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+PfhWdmDispatchPreprocessedPowerIrp(
+ _In_ WDFDEVICE Device,
+ _Inout_ PIRP Irp
+ );
+
+//
+// PfhInterceptWdfPnpPowerEventCallbacks
+// =====================================
+// Routine description:
+// This routine is invoked by the driver layer to enable the power
+// framework helper library to register its PNP and power event callbacks
+// with KMDF. If the driver layer has specified a callback for a PNP or
+// power event that the power framework helper library is also interested
+// in, then the power framework helper library saves the driver layer's
+// callback and invokes it from when KMDF invokes its own callback. The
+// driver layer must call PfhInterceptWdfPnpPowerEventCallbacks after it
+// has initialized the WDF_PNPPOWER_EVENT_CALLBACKS structure but before it
+// has called WdfDeviceInitSetPnpPowerEventCallbacks.
+//
+// This routine must be invoked for any device whose power management is
+// done using the power framework helper library, even if the driver layer
+// does not want to specify any PNP or power event callbacks for that
+// device. After invoking this routine, the driver layer must invoke
+// WdfDeviceInitSetPnpPowerEventCallbacks so that the power framework
+// helper library's callbacks are registered with KMDF.
+//
+// Arguments:
+// Initializer - Handle to the initializer object that is being used to
+// initialize the power framework helper library's device object
+// settings.
+//
+// DriverLayerPnpPowerCallbacks - Pointer to a caller-initialized
+// WDF_PNPPOWER_EVENT_CALLBACKS structure.
+//
+// Return value:
+// None
+//
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+PfhInterceptWdfPnpPowerEventCallbacks(
+ _In_ WDFOBJECT Initializer,
+ _Inout_ PWDF_PNPPOWER_EVENT_CALLBACKS DriverLayerPnpPowerCallbacks
+ );
+
+//
+// PfhSetPoHandleAvailabilityCallbacks
+// ===================================
+// Routine description:
+// This routine is invoked by the driver layer to receive the POHANDLE that
+// represents the device's registration with the power framework and to be
+// notified when the handle is about to become invalid because of an
+// impending unregistration. The POHANDLE received in this manner can be
+// used to directly invoke power framework routines. However, the driver
+// layer must *not* invoke the following power framework routines because
+// they can interfere with the operation of the power framework helper
+// library:
+// PoFxStartDevicePowerManagement
+// PoFxUnregisterDevice
+//
+// PfhSetPoHandleAvailabilityCallbacks must be invoked before the driver
+// layer has created the KMDF device object. The driver layer need not
+// invoke this routine if it is not interested in using the POHANDLE
+// directly.
+//
+// Arguments:
+// Initializer - Handle to the initializer object that is being used to
+// initialize the power framework helper library's device object
+// settings.
+//
+// EvtPfhPoHandleAvailable - Pointer to the driver layer's
+// PFH_CALLBACK_POHANDLE_AVAILABLE callback function.
+//
+// EvtPfhPoHandleUnavailable - Pointer to the driver layer's
+// PFH_CALLBACK_POHANDLE_UNAVAILABLE callback function.
+//
+// Return value:
+// None
+//
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+PfhSetPoHandleAvailabilityCallbacks(
+ _In_ WDFOBJECT Initializer,
+ _In_ PPFH_CALLBACK_POHANDLE_AVAILABLE PfhCallbackPoHandleAvailable,
+ _In_ PPFH_CALLBACK_POHANDLE_UNAVAILABLE PfhCallbackPoHandleUnavailable
+ );
+
+//
+// The following enum defines the S0-idle power management configurations that
+// are supported by the power framework helper library
+//
+typedef enum _PFH_S0IDLE_CONFIG {
+ //
+ // S0-idle power management is not supported for the device
+ //
+ PfhS0IdleNotSupported,
+
+ //
+ // S0-idle power management is supported for the device and the driver can
+ // access pageable data during the device's power transitions. This implies
+ // that the device can *never* be in the paging path.
+ //
+ PfhS0IdleSupportedPowerPageable,
+
+ //
+ // S0-idle power management is supported for the device and the driver
+ // *cannot* access pageable data during the device's power transitions. This
+ // implies that the device can be in the paging path.
+ //
+ // If this configuration is chosen, the driver layer is responsible for
+ // supplying a PO_FX_DEVICE_POWER_REQUIRED_CALLBACK callback. This callback
+ // will be invoked when the power framework instructs the driver to power up
+ // the device. In this callback, the driver layer must initiate device power
+ // up. When the device has powered up, the driver layer must call
+ // PoFxReportDevicePoweredOn to inform the power framework about it. The
+ // driver layer should accomplish this in the following manner:
+ // - In the PO_FX_DEVICE_POWER_REQUIRED_CALLBACK callback it should queue
+ // work to its own worker thread (and not a system worker thread) to
+ // make a blocking call to WdfDeviceStopIdle. Since this is a blocking
+ // call, the driver layer cannot make it directly from within
+ // PO_FX_DEVICE_POWER_REQUIRED_CALLBACK for a couple of reasons. First,
+ // the PO_FX_DEVICE_POWER_REQUIRED_CALLBACK callback can be invoked at
+ // dispatch level. Second, the PO_FX_DEVICE_POWER_REQUIRED_CALLBACK
+ // callback might get invoked in the context of the I/O dispatch
+ // routine of a power-managed queue and a blocking call to
+ // WdfDeviceStopIdle in such a context can lead to a deadlock. Hence,
+ // the blocking call to WdfDeviceStopIdle must be made from a worker
+ // thread. The driver cannot use a system worker thread because queuing
+ // a system work item to the system worker thread can cause pageable
+ // data to be accessed. But by definition, pageable data cannot be
+ // accessed in the PfhS0IdleSupportedNotPowerPageable configuration.
+ // Hence the need for a driver-created worker thread.
+ // - Once the blocking call to WdfDeviceStopIdle has returned, the device
+ // is guaranteed to have returned to D0 and/or to remain in D0.
+ // Therefore the driver layer should call PoFxReportDevicePoweedOn at
+ // this point. If the call to WdfDeviceStopIdle failed, KMDF would have
+ // already declared the device to be in a failed state and initiated a
+ // PNP removal. However, even in the failure case, it is necessary to
+ // call PoFxReportDevicePoweredOn in order to unblock the power
+ // framework and move it to a consistent state.
+ //
+ PfhS0IdleSupportedNotPowerPageable
+
+} PFH_S0IDLE_CONFIG, *PPFH_S0IDLE_CONFIG;
+
+//
+// PfhSetS0IdleConfiguration
+// =========================
+// Routine description:
+// This routine is invoked by the driver layer to specify whether or not
+// the device supports S0-idle power management. PfhSetS0IdleConfiguration
+// must be invoked before the driver layer has created the KMDF device
+// object. If the driver layer does not invoke this routine, the power
+// framework helper library assumes the device's S0-idle power management
+// configuration to be PfhS0IdleNotSupported.
+//
+// Arguments:
+// Initializer - Handle to the initializer object that is being used to
+// initialize the power framework helper library's device object
+// settings.
+// S0IdleConfig - A PFH_S0IDLE_CONFIG value that specifies the device's
+// S0-idle power management configuration.
+//
+// Return value:
+// None
+//
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+PfhSetS0IdleConfiguration(
+ _In_ WDFOBJECT Initializer,
+ _In_ PFH_S0IDLE_CONFIG S0IdleConfig
+ );
+
+//
+// PfhInitializeDeviceSettings
+// ===========================
+// Routine description:
+// This routine is invoked by the driver layer to enable the power
+// framework helper library to initialize the latter's settings for a KMDF
+// device object. The driver layer must invoke this routine for any KMDF
+// device object whose power management is done using the power framework
+// helper library.
+//
+// This routine must be invoked after the KMDF device object has been
+// created and before the initializer object that is being used to
+// initialize the device object is reused to initialize some other KMDF
+// object. After calling this routine, the initializer object can be used
+// for initializing other KMDF objects if needed.
+//
+// Arguments:
+// Device - Handle to the KMDF device object
+//
+// Initializer - Handle to the initializer object that is being used to
+// initialize the power framework helper library's device object
+// settings.
+//
+// Return value:
+// An NTSTATUS value representing success or failure of the function.
+//
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+PfhInitializeDeviceSettings(
+ _In_ WDFDEVICE Device,
+ _In_ WDFOBJECT Initializer
+ );
+
+//
+// PfhInitializePowerFrameworkSettings
+// ===================================
+// Routine description:
+// This routine is invoked by the driver layer to specify its power
+// framework settings. These settings are used by the power framework
+// helper library when it registers with the power framework.
+//
+// This routine must be invoked before the driver layer's
+// EvtDeviceSelfManagedIoInit callback is invoked or from within the driver
+// layer's EvtDeviceSelfManagedIoInit callback. Typically, the driver layer
+// would invoke this routine from with its EvtDriverDeviceAdd callback or
+// from one of the following callbacks that are invoked the first time a
+// device is being started:
+// EvtDevicePrepareHardware
+// EvtDeviceD0Entry
+// EvtDeviceD0EntryPostInterruptsEnabled
+// EvtDeviceSelfManagedIoInit
+//
+// The driver layer must not invoke PfhInitializePowerFrameworkSettings
+// more than once for a given device. Note that a device might be started
+// more than once if a resource rebalance occurs while the device is
+// operational. In this case, the EvtDevicePrepareHardware,
+// EvtDeviceD0Entry and EvtDeviceD0EntryPostInterruptsEnabled callbacks can
+// be invoked more than once. Therefore, if the driver layer chooses to
+// call PfhInitializePowerFrameworkSettings from within one of these
+// callbacks, it must take care to call it only when the callback is
+// invoked for the first time.
+//
+// Arguments:
+// Device - Handle to the KMDF device object
+//
+// PoFxDeviceInfo - Pointer to a caller-initialized
+// PO_FX_DEVICE structure that contains the driver layer's power
+// framework settings.
+//
+// Return value:
+// An NTSTATUS value representing success or failure of the function.
+//
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+PfhInitializePowerFrameworkSettings(
+ _In_ WDFDEVICE Device,
+ _In_ PPO_FX_DEVICE PoFxDeviceInfo
+ );
+
+//
+// PfhInterceptComponentQueueConfig
+// ================================
+// Routine description:
+// This routine is invoked by the driver layer to enable the power
+// framework helper library to register its queue callbacks with KMDF. If
+// the driver layer has specified a queue callback that the power framework
+// helper library is also interested in, then the power framework helper
+// library saves the driver layer's callback and invokes it from when KMDF
+// invokes its own callback. The driver layer must call
+// PfhInterceptComponentQueueConfig after it has initialized the
+// WDF_IO_QUEUE_CONFIG structure but before it has created the KMDF queue
+// object.
+//
+// This routine must be invoked for each of the driver layer's component
+// queues. Component queues are described in the routine description for
+// PfhInitializeComponentQueueSettings.
+//
+// Arguments:
+// Initializer - Handle to the initializer object that is being used to
+// initialize the power framework helper library's queue object
+// settings.
+//
+// DriverLayerQueueConfig - Pointer to a caller-initialized
+// WDF_IO_QUEUE_CONFIG structure.
+//
+// Return value:
+// None
+//
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+PfhInterceptComponentQueueConfig(
+ _In_ WDFOBJECT Initializer,
+ _Inout_ PWDF_IO_QUEUE_CONFIG DriverLayerQueueConfig
+ );
+
+//
+// PfhSetComponentForComponentQueue
+// ================================
+// Routine description:
+// This routine is invoked by the driver layer to specify the component
+// that is associated with a component queue. The current implementation of
+// the power framework helper library does not support associating a queue
+// with more than one component, so this routine must be called no more
+// than once for a given queue.
+//
+// This routine must be invoked for each queue that acts as a component
+// queue. Component queues are described in the routine description for
+// PfhInitializeComponentQueueSettings.
+//
+// Arguments:
+// Initializer - Handle to the initializer object that is being used to
+// initialize the power framework helper library's queue object
+// settings.
+//
+// Component - Component associated with the queue.
+//
+// Return value:
+// None
+//
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+PfhSetComponentForComponentQueue(
+ _In_ WDFOBJECT Initializer,
+ _In_ ULONG Component
+ );
+
+//
+// PfhInitializeComponentQueueSettings
+// ===================================
+// Routine description:
+// This routine is invoked by the driver layer to enable the power
+// framework helper library to initialize the latter's settings for a KMDF
+// queue object.
+//
+// The driver layer must invoke this routine for any KMDF queue object that
+// acts as a component queue. A component queue is a queue whose state is
+// tied to the active/idle state for a component. The queue can dispatch
+// requests when the component is active and cannot dispatch requests when
+// the component is idle. A component queue must be power-managed. The
+// driver layer must not explicitly change the state of any of its
+// component queues.
+//
+// This routine must be invoked after the KMDF queue object has been
+// created and before the initializer object that is being used to
+// initialize the queue object is reused to initialize some other KMDF
+// object. After calling this routine, the initializer object can be used
+// for initializing other KMDF objects if needed.
+//
+// Arguments:
+// Queue - Handle to the KMDF queue object for the component queue
+//
+// Initializer - Handle to the initializer object that is being used to
+// initialize the power framework helper library's queue object
+// settings.
+//
+// Return value:
+// An NTSTATUS value representing success or failure of the function.
+//
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+PfhInitializeComponentQueueSettings(
+ _In_ WDFQUEUE Queue,
+ _In_ WDFOBJECT Initializer
+ );
+
+//
+// PfhRegisterDeviceProactive
+// ==========================
+// Routine description:
+// This routine is invoked by the driver layer to in order to proactively
+// register with the power framework. By default, the power framework
+// helper library registers with the power framework in its
+// EvtDeviceSelfManagedIoInit allback, after it has invoked the driver
+// layer's EvtDeviceSelfManagedIoInit callback (if present). If the driver
+// layer is satisfied with this default behavior, it need not invoke
+// PfhRegisterDeviceProactive at all.
+//
+// However, if the driver layer needs to interact with the power framework
+// before the point at which the power framework helper library registers
+// by default, it can proactively register by calling
+// PfhRegisterDeviceProactive. Before calling PfhRegisterDeviceProactive,
+// the driver layer must ensure that it has already provided its power
+// framework settings to the power framework helper library by calling
+// PfhInitializePowerFrameworkSettings.
+//
+// The power framework helper library unregisters with the power framework
+// helper library during device removal, in the EvtDeviceSelfManagedIoFlush
+// callback.
+//
+// The driver layer must ensure that it does not call
+// PfhRegisterDeviceProactive if the device is already registered with
+// the power framework. This means that if the driver layer calls
+// PfhRegisterDeviceProactive in one of the following callbacks, it needs
+// to handle cases where the callback is called more than once.
+// EvtDevicePrepareHardware
+// EvtDeviceD0Entry
+// EvtDeviceD0EntryPostInterruptsEnabled
+//
+// If the device is being started for the first time or if the device is a
+// PDO that is being restarted after being removed, the above callbacks
+// would get invoked while the device is not yet registered with the power
+// framework.
+//
+// If the device is being restarted after being stopped for resource
+// rebalance, the above callbacks would get invoked while the device is
+// already registered with the power framework.
+//
+// If the device returns to the working state (D0) after being in a low-
+// power state (Dx), then EvtDeviceD0Entry and
+// EvtDeviceD0EntryPostInterruptsEnabled would get invoked while the device
+// is already registered with the power framework (and
+// EvtDevicePrepareHardware is not invoked at all).
+//
+// Therefore, the driver layer must be careful to invoke
+// PfhRegisterDeviceProactive only if the device is not already registered
+// with the power framework. It can determine whether or not the device is
+// already registered by supplying the PFH_CALLBACK_POHANDLE_AVAILABLE and
+// PFH_CALLBACK_POHANDLE_UNAVAILABLE callbacks via
+// PfhSetPoHandleAvailabilityCallbacks.
+//
+// Note that even if the driver layer invokes PfhRegisterDeviceProactive
+// more than once, it must invoke PfhInitializePowerFrameworkSettings only
+// once. The power framework settings that are supplied once get used for
+// subsequent registrations as well.
+//
+// Arguments:
+// Device - Handle to the KMDF device object
+//
+// Return value:
+// An NTSTATUS value representing success or failure of the function.
+//
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+PfhRegisterDeviceProactive(
+ _In_ WDFDEVICE Device
+ );
+
+//
+// PfhForwardRequestToQueue
+// ========================
+// Routine description:
+// This routine is invoked by the driver layer to forward a request to an
+// I/O queue. It is similar to the WdfRequestForwardToIoQueue method. At a
+// minimum, the driver layer must use PfhForwardRequestToQueue instead
+// of WdfRequestForwardToIoQueue when forwarding a request to or from a
+// component queue. However, the driver layer can use
+// PfhForwardRequestToQueue to forward requests between any two queues.
+// The driver layer might find it convenient to always use
+// PfhForwardRequestToQueue instead of using it only in some places and
+// using WdfRequestForwardToIoQueue in others.
+//
+// Arguments:
+// Request - Handle to the KMDF request object for the request that is to
+// be forwarded.
+//
+// Queue - Handle to the KMDF queue object for the destination queue
+//
+// Return value:
+// None
+//
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+PfhForwardRequestToQueue(
+ _In_ WDFREQUEST Request,
+ _In_ WDFQUEUE Queue
+ );
+
+//
+// PfhCompleteRequest
+// ==================
+// Routine description:
+// This routine is invoked by the driver layer to complete a request. It is
+// similar to the WdfRequestCompleteWithInformation method. At a minimum,
+// the driver layer must use PfhCompleteRequest instead of
+// WdfRequestComplete/WdfRequestCompleteWithInformation when it is
+// completing a request that is associated with a component queue. However,
+// the driver layer can use PfhCompleteRequest to complete any request.
+// The driver layer might find it convenient to always use
+// PfhCompleteRequest instead of using it only in some places and using
+// WdfRequestComplete/WdfRequestCompleteWithInformation in others.
+//
+// Arguments:
+// Request - Handle to the KMDF request object for the request that is to
+// be completed.
+//
+// Status - An NTSTATUS value that is used as the completion status of the
+// request.
+//
+// Information - Driver-defined completion status information for the
+// request, such as the number of bytes that were transferred.
+//
+// Return value:
+// None
+//
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+PfhCompleteRequest(
+ _In_ WDFREQUEST Request,
+ _In_ NTSTATUS Status,
+ _In_ ULONG_PTR Information
+ );
+
+#endif // _WDFPOFX_H_
diff --git a/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj
new file mode 100644
index 00000000..ec187a39
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{21F832DD-6A4C-4E5F-941E-15BF98CE4137}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{568CF874-EF69-46EF-AAE1-E59FAE847FBA}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems">
+ <ClCompile Include="init.c; device.c; component.c; s0idle.c">
+ <WppEnabled>true</WppEnabled>
+ <WppKernelMode>true</WppKernelMode>
+ <WppScanConfigurationData>WdfPoFxPriv.h</WppScanConfigurationData>
+ </ClCompile>
+ </ItemGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>WdfPoFx</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>WdfPoFx</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>WdfPoFx</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>WdfPoFx</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters
new file mode 100644
index 00000000..50376050
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
+ <UniqueIdentifier>{A155D773-8FFA-4E28-9C42-A363BDD03866}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{7CBF3FE5-FDC1-4483-B02B-EDCEEAF60743}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
+ <UniqueIdentifier>{D4BDD463-7BCE-4779-856D-4D42D6FBB5E1}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{29D299CC-0D9F-4BB4-8E92-8A1E35E7A85E}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="component.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="device.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="init.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="s0idle.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/pofx/WDF/Driver/MultiComp/lib/WdfPoFxPriv.h b/pofx/WDF/Driver/MultiComp/lib/WdfPoFxPriv.h
new file mode 100644
index 00000000..6e294572
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/lib/WdfPoFxPriv.h
@@ -0,0 +1,382 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ WdfPoFxPriv.h
+
+Abstract:
+ Private header file for the power framework helper library. Contains
+ structures and function headers that are used by various source files in the
+ library.
+
+Environment:
+
+ Kernel mode
+
+--*/
+#if !defined(_WDFPOFXPRIV_H_)
+#define _WDFPOFXPRIV_H_
+
+#include <ntddk.h>
+#include <ntintsafe.h>
+#include "WdfPoFx.h"
+
+#define DO_NOTHING() (0)
+
+//
+// The following structure is used to capture the driver layer's settings for a
+// device
+//
+typedef struct _HELPER_DEVICE_INIT {
+ //
+ // KMDF callbacks registered by the driver layer. The power framework helper
+ // library invokes these callbacks when KMDF invokes that corresponding
+ // callback that the library has registered.
+ //
+ PFN_WDF_DEVICE_SELF_MANAGED_IO_INIT EvtDeviceSelfManagedIoInit;
+ PFN_WDF_DEVICE_SELF_MANAGED_IO_FLUSH EvtDeviceSelfManagedIoFlush;
+ PFN_WDF_DEVICE_SELF_MANAGED_IO_RESTART EvtDeviceSelfManagedIoRestart;
+ PFN_WDF_DEVICE_D0_ENTRY EvtDeviceD0Entry;
+
+ //
+ // Callbacks that notify the driver layer about registration and
+ // unregistration with the power framework. The power framework helper
+ // library invokes these callbacks after registering with the power
+ // framework and before unregistering from the power framework.
+ //
+ PPFH_CALLBACK_POHANDLE_AVAILABLE PfhCallbackPoHandleAvailable;
+ PPFH_CALLBACK_POHANDLE_UNAVAILABLE PfhCallbackPoHandleUnavailable;
+
+ //
+ // The driver layer's WDM pre-process callback for power IRPs. The power
+ // framework helper library invokes this callback when KMDF invokes the
+ // corresponding callback that the library has registered.
+ //
+ PFN_WDFDEVICE_WDM_IRP_PREPROCESS EvtDeviceWdmPowerIrpPreprocess;
+
+ //
+ // Memory object containing the array of power IRP minor functions that the
+ // driver layer is interested in preprocessing.
+ //
+ WDFMEMORY PowerIrpPreprocessMinorFunctions;
+
+ //
+ // The following members track the status of the initialization performed by
+ // the driver layer for a given device. They are used in verification.
+ //
+ BOOLEAN PnpPowerEventCallbacksIntercepted;
+ BOOLEAN PoHandleAvailabilityCallbacksSet;
+ BOOLEAN PowerIrpPreprocessCallbackAssigned;
+#if PFH_S0IDLE_SUPPORTED
+ BOOLEAN S0IdleConfigSet;
+
+ //
+ // The driver's S0-idle configuration
+ //
+ PFH_S0IDLE_CONFIG S0IdleConfig;
+#endif
+
+} HELPER_DEVICE_INIT, *PHELPER_DEVICE_INIT;
+
+//
+// The following structure is used to capture the driver layer's settings for a
+// component queue
+//
+typedef struct _HELPER_QUEUE_INIT {
+ //
+ // Component number of the component that the queue is associated with
+ //
+ ULONG Component;
+
+ //
+ // The driver layer's EvtIoCanceledOnQueue callback for the component queue.
+ // The power framework helper library invokes this callback when KMDF
+ // invokes the corresponding callback that the library has registered.
+ //
+ PFN_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE EvtIoCanceledOnQueue;
+
+ //
+ // The following members track the status of the initialization performed by
+ // the driver layer for a given queue. They are used in verification.
+ //
+ BOOLEAN ComponentQueueConfigIntercepted;
+ BOOLEAN ComponentSet;
+} HELPER_QUEUE_INIT, *PHELPER_QUEUE_INIT;
+
+//
+// Enumeration of KMDF objects that can be initialized using the power framework
+// helper library's initializer object.
+//
+typedef enum _HELPER_INIT_TYPE {
+ HelperInitTypeNone,
+ HelperInitTypeDevice,
+ HelperInitTypeQueue,
+} HELPER_INIT_TYPE, *PHELPER_INIT_TYPE;
+
+//
+// The following structure represents the object context space for the power
+// framework helper library's initializer object.
+//
+typedef struct _HELPER_INIT {
+ //
+ // Type of KMDF object being initialized using the power framework helper
+ // library's initializer object.
+ //
+ HELPER_INIT_TYPE InitType;
+ union {
+ //
+ // Driver layer's device initialization settings
+ //
+ HELPER_DEVICE_INIT DeviceInit;
+
+ //
+ // Driver layer's queue initialization settings
+ //
+ HELPER_QUEUE_INIT QueueInit;
+ } u;
+} HELPER_INIT, *PHELPER_INIT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(HELPER_INIT,
+ HelperGetInitContext)
+
+//
+// The following structure represents the array of power IRP minor functions
+// that the driver layer is interested in preprocessing
+//
+#pragma warning(push)
+#pragma warning(disable:4200)
+typedef struct _POFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO {
+ //
+ // Number of power IRP minor functions that the driver layer is interested
+ // in preprocessing
+ //
+ ULONG NumMinorFunctions;
+
+ //
+ // Array of power IRP minor functions that the driver layer is interested in
+ // preprocessing
+ //
+ UCHAR MinorFunctions[];
+} POFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO,
+ *PPOFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO;
+#pragma warning(pop)
+
+//
+// Per-component information maintained by the power framework helper library
+//
+typedef struct _POFX_COMPONENT_INFO {
+ //
+ // Queue associated with the component
+ //
+ WDFQUEUE Queue;
+
+ //
+ // Whether or not the component is active
+ //
+ BOOLEAN IsActive;
+} POFX_COMPONENT_INFO, *PPOFX_COMPONENT_INFO;
+
+//
+// The following structure represents the driver layer's power framework
+// callbacks. The power framework helper library invokes these callbacks when
+// the power framework invokes the corresponding callbacks that the library has
+// registered.
+//
+typedef struct _POFX_DRIVER_LAYER_POWER_CALLBACKS {
+ PPO_FX_COMPONENT_IDLE_STATE_CALLBACK ComponentIdleStateCallback;
+ PPO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK ComponentActiveConditionCallback;
+ PPO_FX_COMPONENT_IDLE_CONDITION_CALLBACK ComponentIdleConditionCallback;
+ PPO_FX_DEVICE_POWER_REQUIRED_CALLBACK DevicePowerRequiredCallback;
+ PPO_FX_DEVICE_POWER_NOT_REQUIRED_CALLBACK DevicePowerNotRequiredCallback;
+} POFX_DRIVER_LAYER_POWER_CALLBACKS, *PPOFX_DRIVER_LAYER_POWER_CALLBACKS;
+
+//
+// The following structure represents the power framework helper library's
+// device context space.
+//
+typedef struct _POFX_DEVICE_CONTEXT {
+ //
+ // Pointer to the power framework settings registered by the power framework
+ // helper library
+ //
+ PPO_FX_DEVICE PoFxDeviceInfo;
+
+ //
+ // Pointer to the driver layer's power framework callbacks
+ //
+ POFX_DRIVER_LAYER_POWER_CALLBACKS DriverLayerPoFxCallbacks;
+
+ //
+ // Pointer to the driver layer's context for power framework callbacks
+ //
+ PVOID DriverLayerPoFxContext;
+
+ //
+ // Pointer to the information that is tracked by the power framework helper
+ // library for each component
+ //
+ PPOFX_COMPONENT_INFO ComponentInfo;
+
+ //
+ // POHANDLE representing the registration with the power framework
+ //
+ POHANDLE PoHandle;
+
+ //
+ // Array of power IRP minor functions that the driver layer is interested in
+ // preprocessing.
+ //
+ PPOFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO
+ DriverLayerPowerIrpPreprocessInfo;
+
+ //
+ // The driver layer's settings for the device
+ //
+ HELPER_DEVICE_INIT DeviceInitSettings;
+
+ //
+ // Whether or not we should call PoFxReportDevicePoweredOn from our next
+ // EvtDeviceD0Entry callback
+ //
+ BOOLEAN ShouldReportDevicePoweredOn;
+
+#if PFH_S0IDLE_SUPPORTED
+ //
+ // Whether or not we invoked WdfDeviceStopIdle during device start so that
+ // the device remains in D0 until the power framework permits it to go to Dx
+ //
+ BOOLEAN StopIdleInvokedOnDeviceStart;
+
+ //
+ // Work item that is queued in response to device-power-required callback.
+ // When the work item callback is invoked, we make a blocking call to
+ // WdfDeviceStopIdle in order to bring the device to D0. This blocking call
+ // cannot be made within the device-power-required callback because it can
+ // be invoked at dispatch level. Therefore, we make this call at passive
+ // level from a work item callback.
+ //
+ WDFWORKITEM PowerRequiredWorkItem;
+#endif
+
+} POFX_DEVICE_CONTEXT, *PPOFX_DEVICE_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(POFX_DEVICE_CONTEXT, HelperGetDeviceContext)
+
+//
+// The following structure represents the power framework helper library's
+// component queue context space.
+//
+typedef struct _POFX_QUEUE_CONTEXT {
+ //
+ // The driver layer's settings for the component queue
+ //
+ HELPER_QUEUE_INIT QueueInitSettings;
+} POFX_QUEUE_CONTEXT, *PPOFX_QUEUE_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(POFX_QUEUE_CONTEXT, HelperGetQueueContext)
+
+//
+// Power framework helper library's KMDF callbacks
+//
+EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT _PfhEvtSelfManagedIoInit;
+EVT_WDF_DEVICE_SELF_MANAGED_IO_FLUSH _PfhEvtSelfManagedIoFlush;
+EVT_WDF_DEVICE_SELF_MANAGED_IO_RESTART _PfhEvtSelfManagedIoRestart;
+
+EVT_WDF_DEVICE_D0_ENTRY _PfhEvtD0Entry;
+
+EVT_WDFDEVICE_WDM_IRP_PREPROCESS _PfhEvtWdmPowerIrpPreprocess;
+
+EVT_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE _PfhEvtRequestCanceledOnComponentQueue;
+
+//
+// Power framework helper library's power framework callbacks
+//
+PO_FX_COMPONENT_IDLE_STATE_CALLBACK _PfhComponentIdleStateCallback;
+PO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK _PfhComponentActiveConditionCallback;
+PO_FX_COMPONENT_IDLE_CONDITION_CALLBACK _PfhComponentIdleConditionCallback;
+PO_FX_DEVICE_POWER_REQUIRED_CALLBACK _PfhDevicePowerRequiredCallback;
+PO_FX_DEVICE_POWER_NOT_REQUIRED_CALLBACK _PfhDevicePowerNotRequiredCallback;
+
+//
+// Routines used by source files within the power framework helper library
+//
+PHELPER_DEVICE_INIT
+GetDeviceInitSettings(
+ _In_ WDFOBJECT Initializer
+ );
+
+VOID
+UnregisterWithPowerFrameworkWorker(
+ _In_ WDFDEVICE Device
+ );
+
+BOOLEAN
+IsDeviceInitialized(
+ _In_ WDFDEVICE Device
+ );
+
+BOOLEAN
+IsQueueInitialized(
+ _In_ WDFQUEUE Queue
+ );
+
+BOOLEAN
+ArePowerFrameworkSettingsAvailable(
+ _In_ WDFDEVICE Device
+ );
+
+NTSTATUS
+CopyAndUpdateMinorFunctionsArray(
+ _In_ WDFOBJECT Initializer,
+ _In_reads_opt_(NumMinorFunctions) PUCHAR MinorFunctions,
+ _In_ ULONG NumMinorFunctions,
+ _Out_ WDFMEMORY * MinorFunctionsMemory,
+ _Outptr_result_buffer_(*UpdatedNumMinorFunctions) PUCHAR *UpdatedMinorFunctions,
+ _Out_ PULONG UpdatedNumMinorFunctions
+ );
+
+NTSTATUS
+CopyMinorFunctionsArray(
+ _In_ WDFDEVICE Device,
+ _In_ WDFMEMORY SourceMemory,
+ _Out_ WDFMEMORY * DestinationMemory
+ );
+
+#include "s0idle.h"
+
+//
+// Define the tracing flags.
+//
+// The (0,0,0,0,0) GUID below is a dummy. The driver layer's GUID is the one
+// that gets used.
+//
+#define WPP_CONTROL_GUIDS \
+ WPP_DEFINE_CONTROL_GUID( \
+ WdfPoFxTraceControl, (0,0,0,0,0), \
+ WPP_DEFINE_BIT(WDFPOFX_ALL_INFO) \
+ )
+
+#define WPP_FLAG_LEVEL_LOGGER(flag, level) \
+ WPP_LEVEL_LOGGER(flag)
+
+#define WPP_FLAG_LEVEL_ENABLED(flag, level) \
+ (WPP_LEVEL_ENABLED(flag) && \
+ WPP_CONTROL(WPP_BIT_ ## flag).Level >= level)
+
+//
+// This comment block is scanned by the trace preprocessor to define our
+// Trace function.
+//
+// begin_wpp config
+// FUNC Trace{FLAG=WDFPOFX_ALL_INFO}(LEVEL, MSG, ...);
+// end_wpp
+//
+
+#endif // _WDFPOFXPRIV_H_
diff --git a/pofx/WDF/Driver/MultiComp/lib/component.c b/pofx/WDF/Driver/MultiComp/lib/component.c
new file mode 100644
index 00000000..b0e55f1f
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/lib/component.c
@@ -0,0 +1,562 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ component.c
+
+Abstract:
+ This module contains routines that implement power management for a
+ component of the device
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "WdfPoFxPriv.h"
+#include "component.tmh"
+
+VOID
+AcquirePowerReferenceForRequest(
+ _In_ WDFQUEUE Queue
+ )
+/*++
+Routine description:
+ This routine acquires a power reference on behalf of a request, if the
+ request is being forwarded to a component queue. If the request is not being
+ forwarded to a component queue, then this routine does nothing.
+
+Arguments:
+ Queue - Handle to the KMDF queue object to which the request is being
+ forwarded
+
+Return value:
+ None
+--*/
+{
+ PPOFX_QUEUE_CONTEXT qCtx = NULL;
+ WDFDEVICE device = NULL;
+ PPOFX_DEVICE_CONTEXT devCtx = NULL;
+ ULONG component;
+
+ //
+ // Get the queue context
+ //
+ qCtx = HelperGetQueueContext(Queue);
+ if (NULL == qCtx) {
+ //
+ // The request is not being forwarded to a component queue. So there is
+ // no need to take a power reference at this time.
+ //
+ return;
+ }
+
+ //
+ // Get the component on which the power reference needs to be taken
+ //
+ component = qCtx->QueueInitSettings.Component;
+
+ //
+ // Get the device that the queue is associated with
+ //
+ device = WdfIoQueueGetDevice(Queue);
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(device);
+
+ //
+ // Take the power reference
+ //
+ PoFxActivateComponent(devCtx->PoHandle, component, 0 /* Flags */);
+ return;
+}
+
+VOID
+ReleasePowerReferenceForRequest(
+ _In_ WDFREQUEST Request
+ )
+/*++
+Routine description:
+ This routine releases a power reference on behalf of a request, if a power
+ reference had been taken previously. If no power reference had been taken
+ previously for this request, then this routine does nothing.
+
+Arguments:
+ Request - Handle to the KMDF request object on behalf of which the power
+ reference needs to be released
+
+Return value:
+ None
+--*/
+{
+ WDFQUEUE queue = NULL;
+ PPOFX_QUEUE_CONTEXT qCtx = NULL;
+ WDFDEVICE device = NULL;
+ PPOFX_DEVICE_CONTEXT devCtx = NULL;
+ ULONG component;
+
+ //
+ // Get the queue that the request is associated with
+ //
+ queue = WdfRequestGetIoQueue(Request);
+ if (NULL == queue) {
+ return;
+ }
+
+ //
+ // Get the queue context
+ //
+ qCtx = HelperGetQueueContext(queue);
+ if (NULL == qCtx) {
+ //
+ // The request is currently not associated with a component queue. So
+ // no power reference has been taken on behalf of the request and hence
+ // there is no power reference to release at this time.
+ //
+ return;
+ }
+
+ //
+ // Get the component on which the power reference has been taken on behalf
+ // of this request
+ //
+ component = qCtx->QueueInitSettings.Component;
+
+ //
+ // Get the device that the queue is associated with
+ //
+ device = WdfIoQueueGetDevice(queue);
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(device);
+
+ //
+ // Release the power reference that was previously taken
+ //
+ PoFxIdleComponent(devCtx->PoHandle, component, 0 /* Flags */);
+
+ return;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+PfhForwardRequestToQueue(
+ _In_ WDFREQUEST Request,
+ _In_ WDFQUEUE Queue
+ )
+// See comments in WdfPoFx.h
+{
+ NTSTATUS status;
+
+ //
+ // If the request currently has a power reference on a component, then
+ // release the power reference.
+ //
+ ReleasePowerReferenceForRequest(Request);
+
+ //
+ // If the destination queue requires a power reference on a component, then
+ // take the power reference now.
+ //
+ AcquirePowerReferenceForRequest(Queue);
+
+ //
+ // Queue the request to the caller-specified queue
+ //
+ status = WdfRequestForwardToIoQueue(Request, Queue);
+ if (FALSE == NT_SUCCESS(status)) {
+ //
+ // If the queue has not been purged or drained, forwarding a request to
+ // a queue should always succeeded.
+ //
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfRequestForwardToIoQueue failed with %!status!.",
+ status);
+ WdfVerifierDbgBreakPoint();
+ }
+
+ return;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+PfhCompleteRequest(
+ _In_ WDFREQUEST Request,
+ _In_ NTSTATUS Status,
+ _In_ ULONG_PTR Information
+ )
+// See comments in WdfPoFx.h
+{
+ //
+ // If the request currently has a power reference on a component, then
+ // release the power reference.
+ //
+ ReleasePowerReferenceForRequest(Request);
+
+ //
+ // Complete the request
+ //
+ WdfRequestCompleteWithInformation(Request, Status, Information);
+}
+
+VOID
+_PfhEvtRequestCanceledOnComponentQueue(
+ _In_ WDFQUEUE Queue,
+ _In_ WDFREQUEST Request
+ )
+/*++
+Routine Description:
+
+ This routine is invoked when an IO request is canceled while it is in a
+ component queue
+
+Arguments:
+
+ Queue - Handle to the framework queue object for the component queue
+
+ Request - Handle to the framework request object for the request being
+ canceled
+
+Return Value:
+
+ None
+
+--*/
+{
+ PPOFX_QUEUE_CONTEXT qCtx = NULL;
+ PFN_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE evtIoCanceledOnQueue;
+
+ if (FALSE == IsQueueInitialized(Queue)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInitializeComponentQueueSettings has not yet been "
+ "called for WDFQUEUE %p.",
+ Queue);
+ WdfVerifierDbgBreakPoint();
+ }
+
+ //
+ // Get the queue context
+ //
+ qCtx = HelperGetQueueContext(Queue);
+
+ evtIoCanceledOnQueue = qCtx->QueueInitSettings.EvtIoCanceledOnQueue;
+ if (NULL != evtIoCanceledOnQueue) {
+ //
+ // The driver layer supplied a canceled-on-queue callback, so invoke it
+ //
+ evtIoCanceledOnQueue(Queue, Request);
+
+ } else {
+ //
+ // Just complete the request
+ //
+ PfhCompleteRequest(Request, STATUS_CANCELLED, 0 /* Information */);
+ }
+ return;
+}
+
+VOID
+ComponentActive(
+ _In_ PPOFX_DEVICE_CONTEXT DevCtx,
+ _In_ ULONG Component
+ )
+/*++
+Routine Description:
+
+ In this routine, we perform operations that are needed when a component
+ becomes active.
+
+Arguments:
+
+ DevCtx - Pointer to our context space for the device object
+
+ Component - The component that has become active
+
+Return Value:
+
+ None
+
+--*/
+{
+ //
+ // Mark the component as active
+ //
+ DevCtx->ComponentInfo[Component].IsActive = TRUE;
+
+ if (NULL != DevCtx->ComponentInfo[Component].Queue) {
+ //
+ // Start the component-specific queue
+ //
+ WdfIoQueueStart(DevCtx->ComponentInfo[Component].Queue);
+ }
+
+ return;
+}
+
+VOID
+ComponentIdle(
+ _In_ PPOFX_DEVICE_CONTEXT DevCtx,
+ _In_ ULONG Component
+ )
+/*++
+Routine Description:
+
+ In this routine, we perform operations that are needed when a component
+ becomes idle.
+
+Arguments:
+
+ DevCtx - Pointer to our context space for the device object
+
+ Component - The component that has become idle
+
+Return Value:
+
+ None
+
+--*/
+{
+ ULONG queueRequests;
+
+ //
+ // Mark the component as idle
+ //
+ DevCtx->ComponentInfo[Component].IsActive = FALSE;
+
+ if (NULL != DevCtx->ComponentInfo[Component].Queue) {
+ //
+ // NOTE: Given that the component is idle, there shouldn't be any
+ // outstanding requests sitting in the component queue. There may be
+ // some driver-owned requests, but they would all have dropped their
+ // power references by now and would be about to complete. We also know
+ // that no new requests will get added to the queue while this callback
+ // is running. The reason is described below.
+ //
+ // We do not add any new request to this queue until the
+ // PoFxActivateComponent call for that request has returned. If
+ // PoFxActivateComponent is called on another thread while this callback
+ // is running or about to run, then the power framework makes sure
+ // that PoFxActivateComponent does not return until this callback has
+ // finished running. Therefore we know that no new request would get
+ // added to this queue while we are attempting to stop the queue
+ // within this callback.
+ //
+ WdfIoQueueGetState(DevCtx->ComponentInfo[Component].Queue,
+ &queueRequests,
+ NULL /* DriverRequests */);
+ if (0 != queueRequests) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Expected the queue for component %d to have no "
+ "requests, but there are currently %d requests in the queue.",
+ Component,
+ queueRequests);
+ WdfVerifierDbgBreakPoint();
+ }
+
+ //
+ // Stop the component-specific queue.
+ //
+ // No need for synchronous stop. Given that the component is idle,
+ // there anyway shouldn't be any outstanding requests in sitting in
+ // the queue. As explained above, there can be up to one outstanding
+ // request that is delivered to the driver layer, but that would
+ // also be just about to complete. We stop this queue only to
+ // prevent new requests that get added to the queue from being
+ // dispatched to the driver layer until the component becomes active
+ // again.
+ //
+ WdfIoQueueStop(DevCtx->ComponentInfo[Component].Queue,
+ NULL, // StopComplete
+ NULL // Context
+ );
+ }
+
+ return;
+}
+
+VOID
+_PfhComponentActiveConditionCallback(
+ _In_ PVOID Context,
+ _In_ ULONG Component
+ )
+/*++
+Routine Description:
+
+ The power framework invokes this routine to notify us that one of our
+ components has become active.
+
+Arguments:
+
+ Context - Context that we passed in to the power framework
+
+ Component - Index of component that has become active or idle
+
+Return Value:
+
+ None
+
+--*/
+{
+ WDFDEVICE device = NULL;
+ PPOFX_DEVICE_CONTEXT devCtx = NULL;
+ PPO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK
+ componentActiveConditionCallback = NULL;
+
+ //
+ // Get the handle to the framework device object
+ //
+ device = (WDFDEVICE) Context;
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(device);
+
+ //
+ // Component is active
+ //
+ ComponentActive(devCtx, Component);
+
+ //
+ // If the driver layer supplied a component-active-condition callback,
+ // invoke it
+ //
+ componentActiveConditionCallback =
+ devCtx->DriverLayerPoFxCallbacks.ComponentActiveConditionCallback;
+ if (NULL != componentActiveConditionCallback) {
+ componentActiveConditionCallback(devCtx->DriverLayerPoFxContext,
+ Component);
+ }
+
+ return;
+}
+
+VOID
+_PfhComponentIdleConditionCallback(
+ _In_ PVOID Context,
+ _In_ ULONG Component
+ )
+/*++
+Routine Description:
+
+ The power framework invokes this routine to notify us that one of our
+ components has become idle.
+
+Arguments:
+
+ Context - Context that we passed in to the power framework
+
+ Component - Index of component that has become active or idle
+
+Return Value:
+
+ None
+
+--*/
+{
+ WDFDEVICE device = NULL;
+ PPOFX_DEVICE_CONTEXT devCtx = NULL;
+ PPO_FX_COMPONENT_IDLE_CONDITION_CALLBACK
+ componentIdleConditionCallback = NULL;
+
+ //
+ // Get the handle to the framework device object
+ //
+ device = (WDFDEVICE) Context;
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(device);
+
+ //
+ // Component is idle
+ //
+ ComponentIdle(devCtx, Component);
+
+ //
+ // If the driver layer supplied a component-idle-condition callback, invoke
+ // it
+ //
+ componentIdleConditionCallback =
+ devCtx->DriverLayerPoFxCallbacks.ComponentIdleConditionCallback;
+ if (NULL != componentIdleConditionCallback) {
+ componentIdleConditionCallback(devCtx->DriverLayerPoFxContext,
+ Component);
+ } else {
+ //
+ // Complete the transition to idle
+ //
+ PoFxCompleteIdleCondition(devCtx->PoHandle, Component);
+ }
+
+ return;
+}
+
+VOID
+_PfhComponentIdleStateCallback(
+ _In_ PVOID Context,
+ _In_ ULONG Component,
+ _In_ ULONG State
+ )
+/*++
+Routine Description:
+
+ The power framework invokes this routine to change the F-state of one of our
+ components.
+
+Arguments:
+
+ Context - Context that we passed in to the power framework
+
+ Component - Index of component for which the F-state change is to be made
+
+ State - The new F-state to transition the component to
+
+Return Value:
+
+ None
+
+--*/
+{
+ WDFDEVICE device = NULL;
+ PPOFX_DEVICE_CONTEXT devCtx = NULL;
+ PPO_FX_COMPONENT_IDLE_STATE_CALLBACK componentIdleStateCallback = NULL;
+ //
+ // Get the handle to the framework device object
+ //
+ device = (WDFDEVICE) Context;
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(device);
+
+ //
+ // If the driver layer supplied a component-idle-state callback, invoke it
+ //
+ componentIdleStateCallback =
+ devCtx->DriverLayerPoFxCallbacks.ComponentIdleStateCallback;
+ if (NULL != componentIdleStateCallback) {
+ componentIdleStateCallback(devCtx->DriverLayerPoFxContext,
+ Component,
+ State);
+ } else {
+ //
+ // Complete the idle state transition
+ //
+ PoFxCompleteIdleState(devCtx->PoHandle, Component);
+ }
+}
diff --git a/pofx/WDF/Driver/MultiComp/lib/device.c b/pofx/WDF/Driver/MultiComp/lib/device.c
new file mode 100644
index 00000000..aa17eaea
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/lib/device.c
@@ -0,0 +1,868 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ device.c
+
+Abstract:
+ This module contains routines that implement power management for the device
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "WdfPoFxPriv.h"
+#include "device.tmh"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, _PfhEvtSelfManagedIoInit)
+#pragma alloc_text(PAGE, _PfhEvtSelfManagedIoFlush)
+#endif
+
+VOID
+UnregisterWithPowerFrameworkWorker(
+ _In_ WDFDEVICE Device
+ )
+/*++
+Routine description:
+ This routine unregisters a device with the power framework. This routine
+ can be called in EvtDeviceSelfManagedioRestart, which can not be made
+ pageable, therefore this function should also not be pageable.
+
+Arguments:
+ Device - Handle to the KMDF device object
+
+Return value:
+ None
+--*/
+{
+ PPOFX_DEVICE_CONTEXT devCtx;
+ PPFH_CALLBACK_POHANDLE_UNAVAILABLE pfhCallbackPoHandleUnavailable;
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(Device);
+
+ if (NULL == devCtx->PoHandle) {
+ //
+ // We didn't successfully register with the power framework, so nothing
+ // to do here.
+ //
+ return;
+ }
+
+ //
+ // Notify the driver layer that the POHANDLE is about to be invalidated
+ //
+ pfhCallbackPoHandleUnavailable =
+ devCtx->DeviceInitSettings.PfhCallbackPoHandleUnavailable;
+ if (NULL != pfhCallbackPoHandleUnavailable) {
+ pfhCallbackPoHandleUnavailable(Device, devCtx->PoHandle);
+ }
+
+ //
+ // Unregister with the power framework
+ //
+ PoFxUnregisterDevice(devCtx->PoHandle);
+ devCtx->PoHandle = NULL;
+
+ return;
+}
+
+NTSTATUS
+RegisterWithPowerFrameworkWorker(
+ _In_ WDFDEVICE Device
+ )
+/*++
+Routine description:
+ This routine registers a device with the power framework.
+
+Arguments:
+ Device - Handle to the KMDF device object
+
+Return value:
+ An NTSTATUS value representing success or failure of the function.
+--*/
+{
+ NTSTATUS status;
+ PPOFX_DEVICE_CONTEXT devCtx;
+ PPFH_CALLBACK_POHANDLE_AVAILABLE pfhCallbackPoHandleAvailable;
+ ULONG i;
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(Device);
+
+ if (NULL != devCtx->PoHandle) {
+ //
+ // Already registered with power framework, nothing to do here.
+ //
+ status = STATUS_SUCCESS;
+ goto exit;
+ }
+
+ for (i=0; i < (devCtx->PoFxDeviceInfo->ComponentCount); i++) {
+ //
+ // Initially, all components are active
+ //
+ devCtx->ComponentInfo[i].IsActive = TRUE;
+
+ //
+ // For PDOs, we unregister when the device is disabled and re-register
+ // when the device is re-enabled. Before re-registering, we need to
+ // start the component queues. They would all be in the purged state
+ // when the device was disabled.
+ // NOTE: It is okay to start an already-started queue, so we can handle
+ // first registration and re-registration in the same way.
+ //
+ if (NULL != devCtx->ComponentInfo[i].Queue) {
+ WdfIoQueueStart(devCtx->ComponentInfo[i].Queue);
+ }
+ }
+
+ //
+ // Register with the power framework
+ //
+ status = PoFxRegisterDevice(
+ WdfDeviceWdmGetPhysicalDevice(Device),
+ devCtx->PoFxDeviceInfo,
+ &(devCtx->PoHandle)
+ );
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PoFxRegisterDevice failed with %!status!.",
+ status);
+ goto exit;
+ }
+
+ //
+ // Notify the driver layer that the POHANDLE is available
+ //
+ pfhCallbackPoHandleAvailable =
+ devCtx->DeviceInitSettings.PfhCallbackPoHandleAvailable;
+ if (NULL != pfhCallbackPoHandleAvailable) {
+ status = pfhCallbackPoHandleAvailable(Device, devCtx->PoHandle);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+ }
+
+ //
+ // Tell the power framework to start its power management
+ //
+ PoFxStartDevicePowerManagement(devCtx->PoHandle);
+
+ status = STATUS_SUCCESS;
+
+exit:
+ if (FALSE == NT_SUCCESS(status)) {
+ //
+ // If an error occurred, we need to unregister with the power framework.
+ // Note: the function below handles the case where we haven't registered
+ // yet.
+ //
+ UnregisterWithPowerFrameworkWorker(Device);
+ }
+ return status;
+}
+
+NTSTATUS
+_PfhEvtSelfManagedIoInit(
+ _In_ WDFDEVICE Device
+ )
+/*++
+Routine Description:
+
+ In this routine, we initialize self-managed I/O operations.
+
+Arguments:
+
+ Device - Handle to the KMDF device object
+
+Return Value:
+
+ An NTSTATUS value representing success or failure of the function.
+
+--*/
+{
+ NTSTATUS status;
+ PPOFX_DEVICE_CONTEXT devCtx;
+ PFN_WDF_DEVICE_SELF_MANAGED_IO_INIT evtDeviceSelfManagedIoInit;
+
+ PAGED_CODE();
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(Device);
+
+ //
+ // If the driver layer supplied a self-managed-IO-init callback, invoke it.
+ // We need to do this before checking if power framework settings are
+ // available so that the driver has the opportunity to specify power
+ // framework settings in the self-managed-IO-init callback.
+ //
+ evtDeviceSelfManagedIoInit =
+ devCtx->DeviceInitSettings.EvtDeviceSelfManagedIoInit;
+ if (NULL != evtDeviceSelfManagedIoInit) {
+ status = evtDeviceSelfManagedIoInit(Device);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+ }
+
+ if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet "
+ "been called for WDFDEVICE %p. %!status!.",
+ Device,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ //
+ // If S0-idle power management is supported, prevent the device from
+ // powering down to Dx due to S0-idle. We will allow the device to power
+ // down to Dx only after our device-power-not-required callback is invoked.
+ //
+ STOP_DEVICE_IDLE_ON_DEVICE_START(status, Device, devCtx, exit);
+
+ //
+ // Register with the power framework.
+ //
+ // The EvtDeviceSelfManagedIoInit callback is called after the first D0
+ // entry, but not after subsequent D0 entries. This means that it is not
+ // called after D0 entries that occur due to S0-idle, system resuming from
+ // sleep or resource rebalance. Therefore, we register with the power
+ // framework here. We retain the registration until the device is removed.
+ //
+ // Note: The function below handles the case where the driver layer already
+ // registered with the power framework by calling
+ // PfhRegisterDeviceProactive.
+ //
+ status = RegisterWithPowerFrameworkWorker(Device);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+VOID
+_PfhEvtSelfManagedIoFlush(
+ _In_ WDFDEVICE Device
+ )
+/*++
+Routine Description:
+
+ In this routine, we flush self-managed I/O operations.
+
+Arguments:
+
+ Device - Handle to the KMDF device object
+
+Return Value:
+
+ None
+
+--*/
+{
+ PPOFX_DEVICE_CONTEXT devCtx;
+ PFN_WDF_DEVICE_SELF_MANAGED_IO_FLUSH evtDeviceSelfManagedIoFlush;
+
+ PAGED_CODE();
+
+ if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet "
+ "been called for WDFDEVICE %p.",
+ Device);
+ WdfVerifierDbgBreakPoint();
+ }
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(Device);
+
+ if (devCtx->ShouldReportDevicePoweredOn) {
+ //
+ // We received an S0 IRP and we were supposed to call
+ // PoFxReportDevicePoweredOn after entering D0 in response to the S0
+ // IRP. But an error occurred that prevented us from entering D0 and
+ // KMDF has initiated a device teardown as a result of this error.
+ // Before we attempt to unregister with the power framework, we should
+ // call PoFxReportDevicePoweredOn in order to bring the power framework
+ // to a consistent state.
+ //
+ devCtx->ShouldReportDevicePoweredOn = FALSE;
+ PoFxReportDevicePoweredOn(devCtx->PoHandle);
+ }
+
+ //
+ // The EvtDeviceSelfManagedIoFlush callback is called when the device is
+ // being removed. In this callback, we unregister with the power framework.
+ // Note: the function below handles the case where we did not register
+ // successfully.
+ //
+ UnregisterWithPowerFrameworkWorker(Device);
+
+ //
+ // Perform tasks specific to S0-idle power management (if enabled)
+ //
+ SELF_MANAGED_IO_FLUSH_FOR_S0_IDLE(devCtx);
+
+ //
+ // If the driver layer supplied a self-managed-IO-flush callback, invoke it
+ //
+ evtDeviceSelfManagedIoFlush =
+ devCtx->DeviceInitSettings.EvtDeviceSelfManagedIoFlush;
+ if (NULL != evtDeviceSelfManagedIoFlush) {
+ evtDeviceSelfManagedIoFlush(Device);
+ }
+
+ return;
+}
+
+BOOLEAN
+IsS0Irp(
+ _In_ PIRP Irp,
+ _In_ BOOLEAN StackLocationAdjusted
+ )
+/*++
+Routine description:
+ This routine determines whether the given IRP is an S0 IRP.
+
+Arguments:
+ Irp - Pointer to the IRP
+
+ StackLocationAdjusted - A BOOLEAN value that indicates whether or not the
+ IRP's stack location has already been adjusted for forwarding to the
+ next layer in the device stack. If TRUE, it means that the IRP stack
+ location has already been adjusted and this routine must use the next
+ IRP stack location. If FALSE, it means that the IRP stack location has
+ not yet been adjusted and this routine must use the previous IRP stack
+ location.
+
+Return value:
+ TRUE if the IRP is an S0 IRP, FALSE otherwise
+--*/
+{
+ PIO_STACK_LOCATION stackLoc;
+
+ if (StackLocationAdjusted) {
+ stackLoc = IoGetNextIrpStackLocation(Irp);
+ } else {
+ stackLoc = IoGetCurrentIrpStackLocation(Irp);
+ }
+
+ if ((IRP_MJ_POWER == stackLoc->MajorFunction) &&
+ (IRP_MN_SET_POWER == stackLoc->MinorFunction) &&
+ (SystemPowerState == stackLoc->Parameters.Power.Type) &&
+ (PowerSystemWorking == stackLoc->Parameters.Power.State.SystemState)) {
+
+ return TRUE;
+
+ } else {
+
+ return FALSE;
+ }
+}
+
+NTSTATUS
+PreprocessIrpAndDispatch(
+ _In_ WDFDEVICE Device,
+ _Inout_ PIRP Irp,
+ _In_ BOOLEAN StackLocationAdjusted
+ )
+/*++
+Routine description:
+ This routine pre-processes a power IRP and dispatches it to KMDF.
+
+Arguments:
+ Device - Handle to the KMDF device object
+
+ Irp - Pointer to the IRP
+
+ StackLocationAdjusted - A BOOLEAN value that indicates whether or not the
+ IRP's stack location has already been adjusted for forwarding to the
+ next layer in the device stack. If TRUE, it means that the IRP stack
+ location has already been adjusted and this routine must use the next
+ IRP stack location. If FALSE, it means that the IRP stack location has
+ not yet been adjusted and this routine must use the previous IRP stack
+ location.
+
+Return value:
+ The NTSTATUS value that is returned from the
+ WdfDeviceWdmDispatchPreprocessedIrp call made by this routine to dispatch
+ the IRP to KMDF.
+--*/
+{
+ NTSTATUS status;
+ PPOFX_DEVICE_CONTEXT devCtx = NULL;
+
+ if (IsS0Irp(Irp, StackLocationAdjusted)) {
+ //
+ // We have received an S0 IRP. The power framework requires that we call
+ // PoFxReportDevicePoweredOn after we have returned to D0 as a result of
+ // receiving an S0 IRP. Therefore, we make a note in our device context
+ // that we need to call PoFxReportDevicePoweredOn from our next
+ // EvtDeviceD0Entry callback.
+ //
+ devCtx = HelperGetDeviceContext(Device);
+ devCtx->ShouldReportDevicePoweredOn = TRUE;
+ }
+
+ //
+ // Forward the IRP for KMDF to handle
+ //
+ if (FALSE == StackLocationAdjusted) {
+ IoSkipCurrentIrpStackLocation(Irp);
+ }
+ status = WdfDeviceWdmDispatchPreprocessedIrp(Device, Irp);
+
+ return status;
+}
+
+NTSTATUS
+_PfhEvtWdmPowerIrpPreprocess(
+ _In_ WDFDEVICE Device,
+ _Inout_ PIRP Irp
+ )
+/*++
+Routine description:
+ This routine receives power IRPs before KMDF has processed them. It pre-
+ processes the power IRP before eventually forwarding it to KMDF.
+
+Arguments:
+ Device - Handle to the KMDF device object
+
+ Irp - Pointer to the IRP
+
+Return value:
+ If the driver layer is not interested in pre-processing the IRP, the return
+ value is the NTSTATUS value that is returned from the
+ WdfDeviceWdmDispatchPreprocessedIrp call made by this routine to dispatch
+ the IRP to KMDF.
+
+ If the driver layer is interested in pre-processing the IRP, the return
+ value is the NTSTATUS value that is returned by the driver layer's WDM pre-
+ process routine for this IRP.
+--*/
+{
+ NTSTATUS status;
+ PIO_STACK_LOCATION stackLoc;
+ UCHAR minorFunction;
+ PPOFX_DEVICE_CONTEXT devCtx = NULL;
+ BOOLEAN invokeDriverLayerCallback = FALSE;
+ ULONG i;
+ PFN_WDFDEVICE_WDM_IRP_PREPROCESS evtDeviceWdmPowerIrpPreprocess = NULL;
+
+ if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet "
+ "been called for WDFDEVICE %p.",
+ Device);
+ WdfVerifierDbgBreakPoint();
+ }
+
+ //
+ // Get the minor function
+ //
+ stackLoc = IoGetCurrentIrpStackLocation(Irp);
+ minorFunction = stackLoc->MinorFunction;
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(Device);
+
+ //
+ // Check if the driver layer is interested in pre-processing an IRP with
+ // this minor function code.
+ //
+ for (i=0;
+ i < devCtx->DriverLayerPowerIrpPreprocessInfo->NumMinorFunctions;
+ i++) {
+
+ if (minorFunction ==
+ devCtx->DriverLayerPowerIrpPreprocessInfo->MinorFunctions[i]) {
+ //
+ // The driver layer is interested in this minor function code
+ //
+ invokeDriverLayerCallback = TRUE;
+ break;
+ }
+ }
+
+ if (invokeDriverLayerCallback) {
+ //
+ // Invoke the driver layer's callback. After the driver layer has
+ // pre-processed the IRP, it will call
+ // PfhWdmDispatchPreprocessedPowerIrp. We will pre-process the IRP
+ // at that time.
+ //
+ evtDeviceWdmPowerIrpPreprocess =
+ devCtx->DeviceInitSettings.EvtDeviceWdmPowerIrpPreprocess;
+ ASSERT(NULL != evtDeviceWdmPowerIrpPreprocess);
+
+ status = evtDeviceWdmPowerIrpPreprocess(Device, Irp);
+
+ } else {
+ //
+ // Preprocess IRP and dispatch to KMDF
+ //
+ status = PreprocessIrpAndDispatch(Device,
+ Irp,
+ FALSE /* StackLocationAdjusted */);
+ }
+
+ return status;
+}
+
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+PfhWdmDispatchPreprocessedPowerIrp(
+ _In_ WDFDEVICE Device,
+ _Inout_ PIRP Irp
+ )
+// See comments in WdfPoFx.h
+{
+ PIO_STACK_LOCATION stackLoc;
+
+ if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet "
+ "been called for WDFDEVICE %p.",
+ Device);
+ WdfVerifierDbgBreakPoint();
+ }
+
+ //
+ // Verify that this is a power IRP
+ //
+ stackLoc = IoGetNextIrpStackLocation(Irp);
+ if (IRP_MJ_POWER != stackLoc->MajorFunction) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhWdmDispatchPreprocessedPowerIrp must be called "
+ "for power IRPs only. It has been called for IRP %p, which is not"
+ " a power IRP.",
+ Irp);
+ WdfVerifierDbgBreakPoint();
+ }
+
+ //
+ // The driver layer would have already adjusted the stack location for KMDF
+ //
+ return PreprocessIrpAndDispatch(Device,
+ Irp,
+ TRUE /* StackLocationAdjusted */);
+}
+
+NTSTATUS
+_PfhEvtD0Entry(
+ _In_ WDFDEVICE Device,
+ _In_ WDF_POWER_DEVICE_STATE PreviousState
+ )
+/*++
+Routine Description:
+
+ In this routine, we perform operations that are needed when the device
+ enters the D0 power state.
+
+Arguments:
+
+ Device - Handle to the KMDF device object
+
+ PreviousState - A WDF_POWER_DEVICE_STATE-typed enumerator that identifies
+ the previous device power state.
+
+Return Value:
+
+ An NTSTATUS value representing success or failure of the function.
+
+--*/
+{
+ NTSTATUS status;
+ PPOFX_DEVICE_CONTEXT devCtx = NULL;
+ PFN_WDF_DEVICE_D0_ENTRY evtDeviceD0Entry;
+
+ if (FALSE == IsDeviceInitialized(Device)) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInitializeDeviceSettings has not yet been "
+ "called for WDFDEVICE %p. %!status!.",
+ Device,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(Device);
+
+ //
+ // If the driver layer supplied a D0Entry callback, invoke it
+ //
+ evtDeviceD0Entry = devCtx->DeviceInitSettings.EvtDeviceD0Entry;
+ if (NULL != evtDeviceD0Entry) {
+ status = evtDeviceD0Entry(Device, PreviousState);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+ }
+
+ if (devCtx->ShouldReportDevicePoweredOn) {
+ //
+ // This is the first time we are entering D0 after receiving an S0 IRP.
+ // We should call PoFxReportDevicePoweredOn.
+ //
+ devCtx->ShouldReportDevicePoweredOn = FALSE;
+ PoFxReportDevicePoweredOn(devCtx->PoHandle);
+ }
+
+ status = STATUS_SUCCESS;
+
+exit:
+
+ return status;
+}
+
+NTSTATUS
+_PfhEvtSelfManagedIoRestart(
+ _In_ WDFDEVICE Device
+ )
+/*++
+Routine Description:
+
+ In this routine, we restart self-managed I/O operations.
+
+Arguments:
+
+ Device - Handle to the KMDF device object
+
+Return Value:
+
+ An NTSTATUS value representing success or failure of the function.
+
+--*/
+{
+ NTSTATUS status;
+ PPOFX_DEVICE_CONTEXT devCtx = NULL;
+ PFN_WDF_DEVICE_SELF_MANAGED_IO_RESTART evtDeviceSelfManagedIoRestart;
+
+ if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet "
+ "been called for WDFDEVICE %p. %!status!.",
+ Device,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(Device);
+
+ //
+ // If the driver layer supplied a self-managed-IO-restart callback, invoke
+ // it
+ //
+ evtDeviceSelfManagedIoRestart =
+ devCtx->DeviceInitSettings.EvtDeviceSelfManagedIoRestart;
+ if (NULL != evtDeviceSelfManagedIoRestart) {
+ status = evtDeviceSelfManagedIoRestart(Device);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+ }
+
+ //
+ // For a PDO, if this is the first start after the device was disabled and
+ // if S0-idle power management is supported for the device, then we prevent
+ // the device from powering down to Dx due to S0-idle. We will allow the
+ // device to power down to Dx only after our device-power-not-required
+ // callback is invoked.
+ //
+ // NOTE: The function below figures out whether this is the first start
+ // after the device was disabled and does the right thing based on that.
+ //
+ STOP_DEVICE_IDLE_ON_DEVICE_START(status, Device, devCtx, exit);
+
+ //
+ // For a PDO, we need to register with the power framework in the
+ // self-managed-IO-restart callback if the device was disabled and then
+ // re-enabled. In this case, we would have unregistered with the power
+ // framework in the self-managed-IO-flush callback. Therefore, we need to
+ // re-register now.
+ //
+ // NOTE: The function below handles the case where we are already registered
+ // with the power framework.
+ //
+ status = RegisterWithPowerFrameworkWorker(Device);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+VOID
+_PfhDevicePowerRequiredCallback(
+ _In_ PVOID Context
+ )
+/*++
+Routine Description:
+
+ The power framework invokes this routine to notify us that we need to
+ enter/remain in the D0 state
+
+Arguments:
+
+ Context - Context that we passed in to the power framework
+
+Return Value:
+
+ None
+
+--*/
+{
+ WDFDEVICE device = NULL;
+ PPOFX_DEVICE_CONTEXT devCtx = NULL;
+
+ //
+ // Get the handle to the KMDF device object
+ //
+ device = (WDFDEVICE) Context;
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(device);
+
+ //
+ // Perform actions specific to S0-idle power management (if enabled)
+ //
+ DEVICE_POWER_REQUIRED_FOR_S0_IDLE(device, devCtx);
+ return;
+}
+
+VOID
+_PfhDevicePowerNotRequiredCallback(
+ _In_ PVOID Context
+ )
+/*++
+Routine Description:
+
+ The power framework invokes this routine to notify us that we may enter
+ a low-power Dx state
+
+Arguments:
+
+ Context - Context that we passed in to the power framework
+
+Return Value:
+
+ None
+
+--*/
+{
+ WDFDEVICE device = NULL;
+ PPOFX_DEVICE_CONTEXT devCtx = NULL;
+
+ //
+ // Get the handle to the KMDF device object
+ //
+ device = (WDFDEVICE) Context;
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(device);
+
+ //
+ // Perform actions specific to S0-idle power management (if enabled)
+ //
+ DEVICE_POWER_NOT_REQUIRED_FOR_S0_IDLE(device, devCtx);
+
+ //
+ // Tell the power framework that we've finished processing device-power-
+ // not-required
+ //
+ PoFxCompleteDevicePowerNotRequired(devCtx->PoHandle);
+
+ return;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+PfhRegisterDeviceProactive(
+ _In_ WDFDEVICE Device
+ )
+// See comments in WdfPoFx.h
+{
+ NTSTATUS status;
+ PPOFX_DEVICE_CONTEXT devCtx;
+
+ if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet "
+ "been called for WDFDEVICE %p. %!status!.",
+ Device,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(Device);
+
+ if (NULL != devCtx->PoHandle) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhRegisterDeviceProactive was called when the "
+ "device was already registered with the power framework. "
+ "%!status!.",
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ status = RegisterWithPowerFrameworkWorker(Device);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
diff --git a/pofx/WDF/Driver/MultiComp/lib/init.c b/pofx/WDF/Driver/MultiComp/lib/init.c
new file mode 100644
index 00000000..780434b5
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/lib/init.c
@@ -0,0 +1,1336 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ init.c
+
+Abstract:
+ This module contains routines that are used to initialize the power
+ framework helper library
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "WdfPoFxPriv.h"
+#include "init.tmh"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, GetDeviceInitSettings)
+#pragma alloc_text(PAGE, PfhInitializerCreate)
+#pragma alloc_text(PAGE, CopyAndUpdateMinorFunctionsArray)
+#pragma alloc_text(PAGE, PfhAssignWdmPowerIrpPreProcessCallback)
+#pragma alloc_text(PAGE, PfhInterceptWdfPnpPowerEventCallbacks)
+#pragma alloc_text(PAGE, PfhSetPoHandleAvailabilityCallbacks)
+#pragma alloc_text(PAGE, CopyMinorFunctionsArray)
+#pragma alloc_text(PAGE, PfhInitializeDeviceSettings)
+#pragma alloc_text(PAGE, PfhInitializePowerFrameworkSettings)
+#endif
+
+PHELPER_DEVICE_INIT
+GetDeviceInitSettings(
+ _In_ WDFOBJECT Initializer
+ )
+/*++
+Routine description:
+ This routine gets a pointer to the location in the initializer object's
+ context space where we store the driver layer's device object settings.
+
+Arguments:
+ Initializer - Handle to the initializer object that is being used to
+ initialize our device object settings.
+
+Return value:
+ Pointer to a HELPER_DEVICE_INIT structure where the driver layer's device
+ object settings are stored.
+--*/
+{
+ PHELPER_INIT initContext = NULL;
+
+ PAGED_CODE();
+
+ initContext = HelperGetInitContext(Initializer);
+
+ if ((initContext->InitType != HelperInitTypeNone) &&
+ (initContext->InitType != HelperInitTypeDevice)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Cannot get device initialization settings for "
+ "initializer object %p because it is currently being used to "
+ "initialize a different object.",
+ Initializer);
+ WdfVerifierDbgBreakPoint();
+ return NULL;
+ }
+
+ if (initContext->InitType == HelperInitTypeNone) {
+ initContext->InitType = HelperInitTypeDevice;
+ }
+
+ return &(initContext->u.DeviceInit);
+}
+
+PHELPER_QUEUE_INIT
+GetQueueInitSettings(
+ _In_ WDFOBJECT Initializer
+ )
+/*++
+Routine description:
+ This routine gets a pointer to the location in the initializer object's
+ context space where we store the driver layer's queue object settings.
+
+Arguments:
+ Initializer - Handle to the initializer object that is being used to
+ initialize our queue object settings.
+
+Return value:
+ Pointer to a HELPER_QUEUE_INIT structure where the driver layer's queue
+ object settings are stored.
+--*/
+{
+ PHELPER_INIT initContext = NULL;
+
+ initContext = HelperGetInitContext(Initializer);
+
+ if ((initContext->InitType != HelperInitTypeNone) &&
+ (initContext->InitType != HelperInitTypeQueue)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Cannot get queue initialization settings for "
+ "initializer object %p because it is currently being used to "
+ "initialize a different object.",
+ Initializer);
+ WdfVerifierDbgBreakPoint();
+ return NULL;
+ }
+
+ if (initContext->InitType == HelperInitTypeNone) {
+ initContext->InitType = HelperInitTypeQueue;
+ }
+
+ return &(initContext->u.QueueInit);
+}
+
+BOOLEAN
+IsDeviceInitialized(
+ _In_ WDFDEVICE Device
+ )
+/*++
+Routine description:
+ This routine determines whether we have initialized our settings for the
+ given device object.
+
+Arguments:
+ Device - Handle to the KMDF device object
+
+Return value:
+ TRUE if we have initialized our settings for the device object, FALSE
+ otherwise.
+--*/
+{
+ PPOFX_DEVICE_CONTEXT devCtx;
+ devCtx = HelperGetDeviceContext(Device);
+ return (NULL != devCtx);
+}
+
+BOOLEAN
+ArePowerFrameworkSettingsAvailable(
+ _In_ WDFDEVICE Device
+ )
+/*++
+Routine description:
+ This routine determines whether the driver layer has provided us with its
+ power framework settings for the given device object.
+
+Arguments:
+ Device - Handle to the KMDF device object
+
+Return value:
+ TRUE if we the driver layer has provided us with its power framework
+ settings for the device object, FALSE otherwise.
+--*/
+{
+ PPOFX_DEVICE_CONTEXT devCtx;
+ devCtx = HelperGetDeviceContext(Device);
+ return ((NULL != devCtx) && (NULL != devCtx->PoFxDeviceInfo));
+}
+
+BOOLEAN
+IsQueueInitialized(
+ _In_ WDFQUEUE Queue
+ )
+/*++
+Routine description:
+ This routine determines whether we have initialized our settings for the
+ given queue object.
+
+Arguments:
+ Queue - Handle to the KMDF queue object
+
+Return value:
+ TRUE if we have initialized our settings for the queue object, FALSE
+ otherwise.
+--*/
+{
+ PPOFX_QUEUE_CONTEXT qCtx;
+ qCtx = HelperGetQueueContext(Queue);
+ return (NULL != qCtx);
+}
+
+VOID
+ResetInitializer(
+ _In_ WDFOBJECT Initializer
+ )
+/*++
+Routine description:
+ This routine prepares the given initializer object for initializing a new
+ KMDF object.
+
+Arguments:
+ Initializer - Handle to the initializer object
+
+Return value:
+ None
+--*/
+{
+ PHELPER_INIT initContext = NULL;
+
+ //
+ // We want to make sure that when the initializer context is zeroed, the
+ // default S0-idle power management configuration for the device is
+ // PfhS0IdleNotSupported.
+ //
+ C_ASSERT(0 == PfhS0IdleNotSupported);
+
+ //
+ // Reset the initializer context
+ //
+ initContext = HelperGetInitContext(Initializer);
+ RtlZeroMemory(initContext, sizeof(*initContext));
+ initContext->InitType = HelperInitTypeNone;
+
+ return;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+PfhInitializerCreate(
+ _Out_ WDFOBJECT * Initializer
+ )
+// See comments in WdfPoFx.h
+{
+ NTSTATUS status;
+ WDFOBJECT initializer = NULL;
+ WDF_OBJECT_ATTRIBUTES objectAttributes;
+
+ PAGED_CODE();
+
+ //
+ // Create an initializer object
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes, HELPER_INIT);
+ status = WdfObjectCreate(&objectAttributes, &initializer);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfObjectCreate failed with %!status!.",
+ status);
+ goto exit;
+ }
+
+ //
+ // Initialize context
+ //
+ ResetInitializer(initializer);
+
+ *Initializer = initializer;
+
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+NTSTATUS
+CopyAndUpdateMinorFunctionsArray(
+ _In_ WDFOBJECT Initializer,
+ _In_reads_opt_(NumMinorFunctions) PUCHAR MinorFunctions,
+ _In_ ULONG NumMinorFunctions,
+ _Out_ WDFMEMORY * MinorFunctionsMemory,
+ _Outptr_result_buffer_(*UpdatedNumMinorFunctions) PUCHAR *UpdatedMinorFunctions,
+ _Out_ PULONG UpdatedNumMinorFunctions
+ )
+/*++
+Routine description:
+ This routine takes an array of power IRP minor functions and makes a copy of
+ it. If the input array does not include IRP_MN_SET_POWER as one of the minor
+ functions, the copy of the array made by this routine is expanded to include
+ IRP_MN_SET_POWER too. This enables us to register a WDM pre-process callback
+ for IRP_MN_SET_POWER, regardless of whether the driver layer is interested
+ in that minor function.
+
+Arguments:
+ Initializer - Handle to the initializer object
+
+ MinorFunctions - Caller-initialized array of minor functions that the driver
+ layer is interested in for the IRP_MJ_POWER major function.
+
+ NumMinorFunctions - The number of minor functions in the MinorFunctions
+ array
+
+ MinorFunctionsMemory - Pointer to a location that receives a handle to the
+ memory object created by this routine to store a copy of the minor
+ functions
+
+ UpdatedMinorFunctions - Pointer to a location that receives an array of
+ minor functions that the driver layer is interested in, plus
+ IRP_MN_SET_POWER if it was not present in the original array
+
+ UpdatedNumMinorFunctions - Pointer to a location that receives the number of
+ minor functions in the UpdatedMinorFunctions array
+
+Return value:
+ An NTSTATUS value representing success or failure of the function.
+--*/
+{
+ NTSTATUS status;
+ BOOLEAN driverLayerPreprocessesSetPower = FALSE;
+ UCHAR setPowerMinorFunction;
+ ULONG i;
+ ULONG driverLayerMinorFunctions = 0;
+ ULONG extraMinorFunctions = 0;
+ ULONG totalMinorFunctions = 0;
+ ULONG totalMinorFunctionsSize = 0;
+ ULONG memorySize = 0;
+ WDF_OBJECT_ATTRIBUTES objectAttributes;
+ WDFMEMORY memory = NULL;
+ PPOFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO memoryBuffer = NULL;
+ ULONG minorFunctionsOffset;
+ ULONG minorFunctionsSize;
+ ULONG extraMinorFunctionOffset;
+
+ PAGED_CODE();
+
+ //
+ // Check if the driver layer needs to preprocess IRP_MN_SET_POWER
+ //
+ if ((NULL != MinorFunctions) && (0 != NumMinorFunctions)) {
+
+ driverLayerMinorFunctions = NumMinorFunctions;
+
+ for (i=0; i < NumMinorFunctions; i++) {
+ if (IRP_MN_SET_POWER == MinorFunctions[i]) {
+ driverLayerPreprocessesSetPower = TRUE;
+ break;
+ }
+ }
+ }
+
+ //
+ // If the driver layer does not preprocess IRP_MN_SET_POWER, then we need to
+ // update the minor functions array with an extra entry for IRP_MN_SET_POWER
+ //
+ extraMinorFunctions = driverLayerPreprocessesSetPower ? 0 : 1;
+
+ //
+ // Allocate memory to store a copy of the minor functions array
+ //
+ status = RtlULongAdd(driverLayerMinorFunctions,
+ extraMinorFunctions,
+ &totalMinorFunctions);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to compute total minor functions count for "
+ "power IRP preprocessing. RtlUlongAdd failed with %!status!.",
+ status);
+ goto exit;
+ }
+
+ ASSERT(totalMinorFunctions > 0);
+
+ status = RtlULongMult(sizeof(MinorFunctions[0]),
+ totalMinorFunctions,
+ &totalMinorFunctionsSize);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to compute buffer size needed to store the "
+ "functions array. RtlULongMult failed with %!status!.",
+ status);
+ goto exit;
+ }
+ status = RtlULongAdd(sizeof(POFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO),
+ totalMinorFunctionsSize,
+ &memorySize);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to compute buffer size needed to store the "
+ "minor functions array and count. RtlUlongAdd failed with "
+ "%!status!.",
+ status);
+ goto exit;
+ }
+ if (0 == memorySize) {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to set memorySize. Failed with %!status!.",
+ status);
+ goto exit;
+ }
+ WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes);
+ objectAttributes.ParentObject = Initializer; // auto-delete when parent
+ // deleted
+ #pragma prefast(suppress:28160, "memorySize > 0")
+ status = WdfMemoryCreate(&objectAttributes,
+ NonPagedPool,
+ 0, // PoolTag
+ memorySize,
+ &memory,
+ (PVOID*) &memoryBuffer);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to allocate memory to store the minor functions"
+ " array and count. WdfMemoryCreate failed with %!status!.",
+ status);
+ goto exit;
+ }
+
+ //
+ // We'll remember only the minor functions that the driver layer is
+ // interested in. If we add IRP_MN_SET_POWER to that list, we won't need to
+ // remember that beyond initialization time.
+ //
+ memoryBuffer->NumMinorFunctions = driverLayerMinorFunctions;
+
+ //
+ // Copy the minor functions array
+ //
+ minorFunctionsOffset =
+ FIELD_OFFSET(POFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO, MinorFunctions);
+ minorFunctionsSize = driverLayerMinorFunctions *
+ sizeof(MinorFunctions[0]); // already performed
+ // intsafe math above
+ // won't overflow
+ if (driverLayerMinorFunctions > 0) {
+ if (0 == minorFunctionsSize) {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to set minorFunctionsSize. Failed with %!status!.",
+ status);
+ goto exit;
+ }
+ #pragma prefast(suppress:28160, "minorFunctionsSize > 0")
+ status = WdfMemoryCopyFromBuffer(memory,
+ minorFunctionsOffset,
+ memoryBuffer->MinorFunctions,
+ minorFunctionsSize);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to copy driver layer's minor functions "
+ "array. WdfMemoryCopyFromBuffer failed with %!status!.",
+ status);
+ goto exit;
+ }
+ }
+
+ if (extraMinorFunctions > 0) {
+
+ ASSERT(extraMinorFunctions == 1);
+
+ //
+ // Add an extra minor function for IRP_MN_SET_POWER since it was not in
+ // the driver layer's list.
+ // NOTE: This minor function is added at the end and is not reflected in
+ // memoryBuffer.NumMinorFunctions.
+ //
+ extraMinorFunctionOffset =
+ minorFunctionsOffset + minorFunctionsSize;// already performed intsafe
+ // math above so this won't
+ // overflow
+ setPowerMinorFunction = IRP_MN_SET_POWER;
+ status = WdfMemoryCopyFromBuffer(memory,
+ extraMinorFunctionOffset,
+ &setPowerMinorFunction,
+ sizeof(setPowerMinorFunction));
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to add an extra minor function "
+ "(IRP_MN_SET_POWER) to the minor functions array. "
+ "WdfMemoryCopyFromBuffer failed with %!status!.",
+ status);
+ goto exit;
+ }
+ }
+
+ *MinorFunctionsMemory = memory;
+ *UpdatedMinorFunctions = memoryBuffer->MinorFunctions;
+ *UpdatedNumMinorFunctions =
+ totalMinorFunctions;// Do not use memoryBuffer.NumMinorFunctions
+ // here because it does not include the extra
+ // minor function that we might have added.
+
+ //
+ // If we added an extra minor function, it is present in the buffer but not
+ // reflected in memoryBuffer.NumMinorFunctions
+ //
+ ASSERT((memoryBuffer->NumMinorFunctions + extraMinorFunctions) ==
+ totalMinorFunctions);
+
+ status = STATUS_SUCCESS;
+
+exit:
+ if (FALSE == NT_SUCCESS(status)) {
+ if (NULL != memory) {
+ WdfObjectDelete(memory);
+ }
+ }
+
+ return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+PfhAssignWdmPowerIrpPreProcessCallback(
+ _In_ WDFOBJECT Initializer,
+ _In_ PWDFDEVICE_INIT DeviceInit,
+ _In_opt_ PFN_WDFDEVICE_WDM_IRP_PREPROCESS EvtDeviceWdmPowerIrpPreprocess,
+ _In_reads_opt_(NumMinorFunctions) PUCHAR MinorFunctions,
+ _In_ ULONG NumMinorFunctions
+ )
+// See comments in WdfPoFx.h
+{
+ NTSTATUS status;
+ WDFMEMORY minorFunctionsMemory = NULL;
+ ULONG numMinorFunctions = 0;
+ PUCHAR minorFunctions = NULL;
+ PHELPER_DEVICE_INIT deviceInitSettings = NULL;
+
+ PAGED_CODE();
+
+ deviceInitSettings = GetDeviceInitSettings(Initializer);
+
+ if (deviceInitSettings->PowerIrpPreprocessCallbackAssigned) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhAssignWdmPowerIrpPreProcessCallback has already"
+ " been called on initialier %p. It should not be called again "
+ "before the initializer has been used to initialize a KMDF device"
+ " object. %!status!.",
+ Initializer,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ //
+ // Copy the minor functions array and, if needed, update it to include
+ // IRP_MN_SET_POWER.
+ //
+ status = CopyAndUpdateMinorFunctionsArray(Initializer,
+ MinorFunctions,
+ NumMinorFunctions,
+ &minorFunctionsMemory,
+ &minorFunctions,
+ &numMinorFunctions);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+
+ //
+ // Assign the WDM preprocess callback
+ //
+ status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
+ DeviceInit,
+ _PfhEvtWdmPowerIrpPreprocess,
+ IRP_MJ_POWER,
+ minorFunctions,
+ numMinorFunctions
+ );
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfDeviceInitAssignWdmIrpPreprocessCallback failed "
+ "with %!status!.",
+ status);
+ goto exit;
+ }
+
+ deviceInitSettings->PowerIrpPreprocessMinorFunctions = minorFunctionsMemory;
+ deviceInitSettings->EvtDeviceWdmPowerIrpPreprocess =
+ EvtDeviceWdmPowerIrpPreprocess;
+ deviceInitSettings->PowerIrpPreprocessCallbackAssigned = TRUE;
+
+ status = STATUS_SUCCESS;
+
+exit:
+ if (FALSE == NT_SUCCESS(status)) {
+ if (NULL != minorFunctionsMemory) {
+ WdfObjectDelete(minorFunctionsMemory);
+ }
+ }
+ return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+PfhInterceptWdfPnpPowerEventCallbacks(
+ _In_ WDFOBJECT Initializer,
+ _Inout_ PWDF_PNPPOWER_EVENT_CALLBACKS DriverLayerPnpPowerCallbacks
+ )
+// See comments in WdfPoFx.h
+{
+ PHELPER_DEVICE_INIT deviceInitSettings = NULL;
+
+ PAGED_CODE();
+
+ deviceInitSettings = GetDeviceInitSettings(Initializer);
+
+ if (deviceInitSettings->PnpPowerEventCallbacksIntercepted) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInterceptWdfPnpPowerEventCallbacks has already been"
+ " called on initialier %p. It should not be called again before "
+ "the initializer has been used to initialize a KMDF device "
+ "object.",
+ Initializer);
+ WdfVerifierDbgBreakPoint();
+ }
+
+ //
+ // Save the driver layer's callbacks that we are going to replace
+ //
+ deviceInitSettings->EvtDeviceSelfManagedIoInit =
+ DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoInit;
+ deviceInitSettings->EvtDeviceSelfManagedIoFlush =
+ DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoFlush;
+ deviceInitSettings->EvtDeviceSelfManagedIoRestart =
+ DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoRestart;
+ deviceInitSettings->EvtDeviceD0Entry =
+ DriverLayerPnpPowerCallbacks->EvtDeviceD0Entry;
+
+ //
+ // Replace the driver layer's callbacks with our own
+ //
+ DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoInit =
+ _PfhEvtSelfManagedIoInit;
+ DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoFlush =
+ _PfhEvtSelfManagedIoFlush;
+ DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoRestart =
+ _PfhEvtSelfManagedIoRestart;
+ DriverLayerPnpPowerCallbacks->EvtDeviceD0Entry =
+ _PfhEvtD0Entry;
+
+ deviceInitSettings->PnpPowerEventCallbacksIntercepted = TRUE;
+
+ return;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+PfhSetPoHandleAvailabilityCallbacks(
+ _In_ WDFOBJECT Initializer,
+ _In_ PPFH_CALLBACK_POHANDLE_AVAILABLE PfhCallbackPoHandleAvailable,
+ _In_ PPFH_CALLBACK_POHANDLE_UNAVAILABLE PfhCallbackPoHandleUnavailable
+ )
+// See comments in WdfPoFx.h
+{
+ PHELPER_DEVICE_INIT deviceInitSettings = NULL;
+
+ PAGED_CODE();
+
+ deviceInitSettings = GetDeviceInitSettings(Initializer);
+
+ if (deviceInitSettings->PoHandleAvailabilityCallbacksSet) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhSetPoHandleAvailabilityCallbacks has already been "
+ "called on initialier %p. It should not be called again before "
+ "the initializer has been used to initialize a KMDF device "
+ "object.",
+ Initializer);
+ WdfVerifierDbgBreakPoint();
+ }
+
+ //
+ // Save the driver layer's callbacks
+ //
+ deviceInitSettings->PfhCallbackPoHandleAvailable =
+ PfhCallbackPoHandleAvailable;
+ deviceInitSettings->PfhCallbackPoHandleUnavailable =
+ PfhCallbackPoHandleUnavailable;
+
+ deviceInitSettings->PoHandleAvailabilityCallbacksSet = TRUE;
+ return;
+}
+
+NTSTATUS
+CopyMinorFunctionsArray(
+ _In_ WDFDEVICE Device,
+ _In_ WDFMEMORY SourceMemory,
+ _Out_ WDFMEMORY * DestinationMemory
+ )
+/*++
+Routine description:
+ This routine creates a new memory object to store the array of power IRP
+ minor functions that the driver layer is interested in. The array is
+ currently stored in a memory object that we had created as a child of the
+ initializer object that was being used to initialize the device object. Now
+ that the initialization of the device object is about to complete, we need
+ to make a copy of the contents of that memory object. Once the device object
+ initialization is complete, the driver layer is free to delete the
+ initializer object, which will result in the original memory object also
+ getting delete. That is why we copy the contents to a new memory object.
+
+Arguments:
+ Device - Handle to the KMDF device object
+
+ SourceMemory - Handle to the memory object that contains the array of power
+ IRP minor functions that the driver layer is interested in
+
+ DestinationMemory - Pointer to a location that receives a handle to a new
+ memory object created by this routine. The new memory object contains a
+ copy of the a
+
+Return value:
+ An NTSTATUS value representing success or failure of the function.
+--*/
+{
+ NTSTATUS status;
+ PPOFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO powerIrpPreprocessInfo = NULL;
+ size_t powerIrpPreprocessInfoSize;
+ WDF_OBJECT_ATTRIBUTES objectAttributes;
+ WDFMEMORY memory = NULL;
+
+ PAGED_CODE();
+
+ //
+ // Get the source memory buffer
+ //
+ powerIrpPreprocessInfo = WdfMemoryGetBuffer(SourceMemory,
+ &powerIrpPreprocessInfoSize);
+ if (0 == powerIrpPreprocessInfoSize) {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to get powerIrpPreprocessInfoSize. Failed with %!status!.",
+ status);
+ goto exit;
+ }
+
+ //
+ // Create a new memory object to hold a copy of the buffer
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes);
+ objectAttributes.ParentObject = Device; // auto-delete when parent deleted
+ status = WdfMemoryCreate(&objectAttributes,
+ NonPagedPool,
+ 0, // PoolTag
+ powerIrpPreprocessInfoSize,
+ &memory,
+ NULL // Buffer
+ );
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to allocate memory for the driver layer's minor"
+ " functions array. WdfMemoryCreate failed with %!status!.",
+ status);
+ goto exit;
+ }
+
+ //
+ // Copy the buffer into the new memory object
+ //
+ status = WdfMemoryCopyFromBuffer(memory,
+ 0, // DestinationOffset
+ powerIrpPreprocessInfo,
+ powerIrpPreprocessInfoSize);
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+
+ *DestinationMemory = memory;
+
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+PfhInitializeDeviceSettings(
+ _In_ WDFDEVICE Device,
+ _In_ WDFOBJECT Initializer
+ )
+// See comments in WdfPoFx.h
+{
+ NTSTATUS status;
+ WDF_OBJECT_ATTRIBUTES objectAttributes;
+ PPOFX_DEVICE_CONTEXT devCtx;
+ PHELPER_DEVICE_INIT deviceInitSettings = NULL;
+ WDFMEMORY memory = NULL;
+
+ PAGED_CODE();
+
+ deviceInitSettings = GetDeviceInitSettings(Initializer);
+
+ if (IsDeviceInitialized(Device)) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInitializeDeviceSettings has already been called "
+ "for WDFDEVICE %p. It should not be called again. %!status!.",
+ Device,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ if (FALSE == deviceInitSettings->PowerIrpPreprocessCallbackAssigned) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhAssignWdmPowerIrpPreProcessCallback has not yet "
+ "been called for WDFDEVICE %p. %!status!.",
+ Device,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ if (FALSE == deviceInitSettings->PnpPowerEventCallbacksIntercepted) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInterceptWdfPnpPowerEventCallbacks has not yet been"
+ " called for WDFDEVICE %p. %!status!.",
+ Device,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ //
+ // Allocate our context for this device
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes,
+ POFX_DEVICE_CONTEXT);
+ status = WdfObjectAllocateContext((WDFOBJECT) Device,
+ &objectAttributes,
+ (PVOID*) &devCtx);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfObjectAllocateContext failed with %!status!",
+ status);
+ goto exit;
+ }
+
+ //
+ // Copy the device init settings
+ //
+ devCtx->DeviceInitSettings = *deviceInitSettings;
+
+ //
+ // Copy the minor functions array into a new memory object that is a child
+ // of the device object. The array is currently in a memory object that is
+ // a child of the initializer object. The initializer object can be deleted
+ // by the driver layer any time after initialization is complete, so we need
+ // to make a copy now.
+ //
+ status = CopyMinorFunctionsArray(
+ Device,
+ devCtx->DeviceInitSettings.PowerIrpPreprocessMinorFunctions,
+ &memory
+ );
+ if (FALSE == NT_SUCCESS(status)) {
+ goto exit;
+ }
+
+ //
+ // Perform initialization specific to S0-idle support (if enabled)
+ //
+ INITIALIZE_DEVICE_SETTINGS_FOR_S0_IDLE(Device, devCtx);
+
+ //
+ // In our device context, replace the pointer to the initializer's memory
+ // object with a pointer to the new memory object that we just created.
+ //
+ devCtx->DeviceInitSettings.PowerIrpPreprocessMinorFunctions = memory;
+
+ //
+ // For convenient access, also save the memory object's buffer in our device
+ // context.
+ //
+ devCtx->DriverLayerPowerIrpPreprocessInfo = WdfMemoryGetBuffer(
+ memory,
+ NULL // BufferSize
+ );
+
+ //
+ // By default, we do not need to call PoFxReportDevicePoweredOn from our
+ // EvtDeviceD0Entry callback. We only need to call it in the
+ // EvtDeviceD0Entry callback that is invoked right after we receive an S0
+ // IRP.
+ //
+ devCtx->ShouldReportDevicePoweredOn = FALSE;
+
+ //
+ // The initializer can now be used to initialize some other object
+ //
+ ResetInitializer(Initializer);
+
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+PfhInitializePowerFrameworkSettings(
+ _In_ WDFDEVICE Device,
+ _In_ PPO_FX_DEVICE PoFxDeviceInfo
+ )
+// See comments in WdfPoFx.h
+{
+ NTSTATUS status;
+ WDF_OBJECT_ATTRIBUTES objectAttributes;
+ PPOFX_DEVICE_CONTEXT devCtx;
+ ULONG i;
+ ULONG pofxExtraComponentsSize;
+ ULONG pofxDeviceInfoSize;
+ WDFMEMORY memory = NULL;
+ ULONG idleStatesSize;
+ PVOID idleStates = NULL;
+ PPO_FX_DEVICE poFxDeviceInfo = NULL;
+ PPOFX_COMPONENT_INFO componentInfo = NULL;
+ ULONG componentInfoSize;
+
+ PAGED_CODE();
+
+ if (FALSE == IsDeviceInitialized(Device)) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInitializeDeviceSettings has not yet been called "
+ "for WDFDEVICE %p. %!status!.",
+ Device,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ if (ArePowerFrameworkSettingsAvailable(Device)) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInitializePowerFrameworkSettings has already been "
+ "called for WDFDEVICE %p. It should not be called again. "
+ "%!status!.",
+ Device,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(Device);
+
+ //
+ // Allocate memory to store power framework settings
+ //
+ status = RtlULongMult(sizeof(PO_FX_COMPONENT),
+ (PoFxDeviceInfo->ComponentCount - 1),
+ &pofxExtraComponentsSize);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to compute buffer size needed for extra "
+ "components. RtlULongMult failed with %!status!.",
+ status);
+ goto exit;
+ }
+ status = RtlULongAdd(sizeof(PO_FX_DEVICE),
+ pofxExtraComponentsSize,
+ &pofxDeviceInfoSize);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to compute buffer size needed for power "
+ "framework settings. RtlUlongAdd failed with %!status!.",
+ status);
+ goto exit;
+ }
+ if (0 == pofxDeviceInfoSize) {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to set pofxDeviceInfoSize. Failed with %!status!.",
+ status);
+ goto exit;
+ }
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes);
+ objectAttributes.ParentObject = Device; // auto-delete when parent deleted
+ status = WdfMemoryCreate(&objectAttributes,
+ NonPagedPool,
+ 0, // PoolTag
+ pofxDeviceInfoSize,
+ &memory,
+ (PVOID*) &poFxDeviceInfo);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to allocate memory for power framework "
+ "settings. WdfMemoryCreate failed with %!status!.",
+ status);
+ goto exit;
+ }
+
+ devCtx->PoFxDeviceInfo = poFxDeviceInfo;
+
+ //
+ // Copy power framework settings
+ //
+ status = WdfMemoryCopyFromBuffer(memory,
+ 0, // DestinationOffset
+ PoFxDeviceInfo,
+ pofxDeviceInfoSize);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to copy power framework settings. "
+ "WdfMemoryCopyFromBuffer failed with %!status!.",
+ status);
+ goto exit;
+ }
+
+ //
+ // Save the driver layer's callbacks and replace them with our own
+ //
+ devCtx->DriverLayerPoFxCallbacks.ComponentIdleStateCallback =
+ devCtx->PoFxDeviceInfo->ComponentIdleStateCallback;
+ devCtx->DriverLayerPoFxCallbacks.ComponentActiveConditionCallback =
+ devCtx->PoFxDeviceInfo->ComponentActiveConditionCallback;
+ devCtx->DriverLayerPoFxCallbacks.ComponentIdleConditionCallback =
+ devCtx->PoFxDeviceInfo->ComponentIdleConditionCallback;
+ devCtx->DriverLayerPoFxCallbacks.DevicePowerRequiredCallback =
+ devCtx->PoFxDeviceInfo->DevicePowerRequiredCallback;
+ devCtx->DriverLayerPoFxCallbacks.DevicePowerNotRequiredCallback =
+ devCtx->PoFxDeviceInfo->DevicePowerNotRequiredCallback;
+ devCtx->PoFxDeviceInfo->ComponentIdleStateCallback =
+ _PfhComponentIdleStateCallback;
+ devCtx->PoFxDeviceInfo->ComponentActiveConditionCallback =
+ _PfhComponentActiveConditionCallback;
+ devCtx->PoFxDeviceInfo->ComponentIdleConditionCallback =
+ _PfhComponentIdleConditionCallback;
+ devCtx->PoFxDeviceInfo->DevicePowerRequiredCallback =
+ _PfhDevicePowerRequiredCallback;
+ devCtx->PoFxDeviceInfo->DevicePowerNotRequiredCallback =
+ _PfhDevicePowerNotRequiredCallback;
+
+ //
+ // Save the driver layer's context and replace it with our own
+ //
+ devCtx->DriverLayerPoFxContext = devCtx->PoFxDeviceInfo->DeviceContext;
+ devCtx->PoFxDeviceInfo->DeviceContext = (PVOID) Device;
+
+ //
+ // Store the idle states for each component
+ //
+ for (i=0; i < devCtx->PoFxDeviceInfo->ComponentCount; i++) {
+
+ status = RtlULongMult(sizeof(PO_FX_COMPONENT_IDLE_STATE),
+ PoFxDeviceInfo->Components[i].IdleStateCount,
+ &idleStatesSize);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to compute buffer size needed for idle "
+ "states for component %d. RtlUlongMult failed with "
+ "%!status!.",
+ i,
+ status);
+ goto exit;
+ }
+ if (0 == idleStatesSize) {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to set idleStatesSize. Failed with %!status!.",
+ status);
+ goto exit;
+ }
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes);
+ objectAttributes.ParentObject = Device;//auto-delete when parent deleted
+ status = WdfMemoryCreate(&objectAttributes,
+ NonPagedPool,
+ 0, // PoolTag
+ idleStatesSize,
+ &memory,
+ &idleStates);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to allocate memory for idle states for "
+ "component %d. WdfMemoryCreate failed with %!status!.",
+ i,
+ status);
+ goto exit;
+ }
+
+ status = WdfMemoryCopyFromBuffer(
+ memory,
+ 0, // DestinationOffset
+ devCtx->PoFxDeviceInfo->Components[i].IdleStates,
+ idleStatesSize
+ );
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to copy idle states for component %d. "
+ "WdfMemoryCopyFromBuffer failed with %!status!.",
+ i,
+ status);
+ goto exit;
+ }
+ devCtx->PoFxDeviceInfo->Components[i].IdleStates = idleStates;
+ }
+
+ //
+ // Allocate memory to store our private, per-component information
+ //
+ status = RtlULongMult(sizeof(POFX_COMPONENT_INFO),
+ devCtx->PoFxDeviceInfo->ComponentCount,
+ &componentInfoSize);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to compute buffer size needed for storing "
+ "private, per-component information. RtlULongMult failed with "
+ "%!status!.",
+ status);
+ goto exit;
+ }
+ if (0 == componentInfoSize) {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to set componentInfoSize. Failed with %!status!.",
+ status);
+ goto exit;
+ }
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes);
+ objectAttributes.ParentObject = Device; // auto-delete when parent deleted
+ status = WdfMemoryCreate(&objectAttributes,
+ NonPagedPool,
+ 0, // PoolTag
+ componentInfoSize,
+ &memory,
+ (PVOID*) &componentInfo);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Unable to allocate memory for storing private, per-"
+ "component information. WdfMemoryCreate failed with %!status!.",
+ status);
+ goto exit;
+ }
+ RtlZeroMemory(componentInfo, componentInfoSize);
+ devCtx->ComponentInfo = componentInfo;
+
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+PfhInterceptComponentQueueConfig(
+ _In_ WDFOBJECT Initializer,
+ _Inout_ PWDF_IO_QUEUE_CONFIG DriverLayerQueueConfig
+ )
+// See comments in WdfPoFx.h
+{
+ PHELPER_QUEUE_INIT queueInitSettings = NULL;
+
+ queueInitSettings = GetQueueInitSettings(Initializer);
+
+ if (queueInitSettings->ComponentQueueConfigIntercepted) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInterceptComponentQueueConfig has already been "
+ "called on initialier %p. It should not be called again before "
+ "the initializer has been used to initialize a KMDF queue "
+ "object.",
+ Initializer);
+ WdfVerifierDbgBreakPoint();
+ }
+
+ //
+ // Save the driver layer's callbacks that we are going to replace
+ //
+ queueInitSettings->EvtIoCanceledOnQueue =
+ DriverLayerQueueConfig->EvtIoCanceledOnQueue;
+
+ //
+ // Replace the driver layer's callbacks with our own
+ //
+ DriverLayerQueueConfig->EvtIoCanceledOnQueue =
+ _PfhEvtRequestCanceledOnComponentQueue;
+
+ queueInitSettings->ComponentQueueConfigIntercepted = TRUE;
+
+ return;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+PfhSetComponentForComponentQueue(
+ _In_ WDFOBJECT Initializer,
+ _In_ ULONG Component
+ )
+// See comments in WdfPoFx.h
+{
+ PHELPER_QUEUE_INIT queueInitSettings = NULL;
+
+ queueInitSettings = GetQueueInitSettings(Initializer);
+
+ if (queueInitSettings->ComponentSet) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhSetComponentForComponentQueue has already been "
+ "called on initialier %p. It should not be called again before "
+ "the initializer has been used to initialize a KMDF queue "
+ "object.",
+ Initializer);
+ WdfVerifierDbgBreakPoint();
+ }
+
+ queueInitSettings->Component = Component;
+
+ queueInitSettings->ComponentSet = TRUE;
+
+ return;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+PfhInitializeComponentQueueSettings(
+ _In_ WDFQUEUE Queue,
+ _In_ WDFOBJECT Initializer
+ )
+// See comments in WdfPoFx.h
+{
+ NTSTATUS status;
+ WDFDEVICE device = NULL;
+ ULONG component;
+ WDF_OBJECT_ATTRIBUTES objectAttributes;
+ PPOFX_DEVICE_CONTEXT devCtx;
+ PPOFX_QUEUE_CONTEXT qCtx;
+ PHELPER_QUEUE_INIT queueInitSettings = NULL;
+
+ queueInitSettings = GetQueueInitSettings(Initializer);
+
+ if (IsQueueInitialized(Queue)) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInitializeComponentQueueSettings has already been "
+ "called for WDFQUEUE %p. It should not be called again. "
+ "%!status!.",
+ Queue,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ device = WdfIoQueueGetDevice(Queue);
+ if (FALSE == ArePowerFrameworkSettingsAvailable(device)) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet been "
+ "called for WDFDEVICE %p. %!status!.",
+ device,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ if (FALSE == queueInitSettings->ComponentQueueConfigIntercepted) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhInterceptComponentQueueConfig has not yet been "
+ "called for WDFQUEUE %p. %!status!.",
+ Queue,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ if (FALSE == queueInitSettings->ComponentSet) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhSetComponentForComponentQueue has not yet been "
+ "called for WDFQUEUE %p. %!status!.",
+ Queue,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(device);
+
+ //
+ // Validate the component number
+ //
+ component = queueInitSettings->Component;
+ if (component >= devCtx->PoFxDeviceInfo->ComponentCount) {
+ status = STATUS_INVALID_PARAMETER;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Component number %d is invalid. Component count is %d."
+ " %!status!.",
+ component,
+ devCtx->PoFxDeviceInfo->ComponentCount,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ //
+ // Allocate our context for this queue
+ //
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes,
+ POFX_QUEUE_CONTEXT);
+ status = WdfObjectAllocateContext((WDFOBJECT) Queue,
+ &objectAttributes,
+ (PVOID*) &qCtx);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfObjectAllocateContext failed with %!status!",
+ status);
+ goto exit;
+ }
+
+ if (NULL != devCtx->ComponentInfo[component].Queue) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - Component %d has already been associated with WDFQUEUE"
+ " %p. Associating a component with more than one queue is not "
+ "supported. %!status!.",
+ component,
+ devCtx->ComponentInfo[component].Queue,
+ status);
+ WdfVerifierDbgBreakPoint();
+ goto exit;
+ }
+
+ //
+ // Copy the queue init settings
+ //
+ qCtx->QueueInitSettings = *queueInitSettings;
+
+ //
+ // The initializer can now be used to initialize some other object
+ //
+ ResetInitializer(Initializer);
+
+ //
+ // Save the queue handle in our private component information
+ //
+ devCtx->ComponentInfo[component].Queue = Queue;
+
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
diff --git a/pofx/WDF/Driver/MultiComp/lib/s0idle.c b/pofx/WDF/Driver/MultiComp/lib/s0idle.c
new file mode 100644
index 00000000..59ab1bd3
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/lib/s0idle.c
@@ -0,0 +1,471 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ s0idle.c
+
+Abstract:
+ This module contains routines that are used to implement support for
+ S0-idle power management. Note the use of the compile-time switch
+ PFH_S0IDLE_SUPPORTED to control whether or not S0-idle power management
+ support is implemented in the power framework helper library. Drivers that
+ do not require S0-idle power management support should set the
+ PFH_S0IDLE_SUPPORTED compile-time switch to 0 in order to reduce the
+ code size of the power framework helper library.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#if PFH_S0IDLE_SUPPORTED
+
+#include "WdfPoFxPriv.h"
+#include "s0idle.tmh"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, PfhSetS0IdleConfiguration)
+#pragma alloc_text(PAGE, InitializeDeviceSettingsForS0Idle)
+#pragma alloc_text(PAGE, _PowerRequiredPassiveHandler)
+#endif
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+PfhSetS0IdleConfiguration(
+ _In_ WDFOBJECT Initializer,
+ _In_ PFH_S0IDLE_CONFIG S0IdleConfig
+ )
+// See comments in WdfPoFx.h
+{
+ PHELPER_DEVICE_INIT deviceInitSettings = NULL;
+
+ PAGED_CODE();
+
+ deviceInitSettings = GetDeviceInitSettings(Initializer);
+
+ if (deviceInitSettings->S0IdleConfigSet) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - PfhSetS0IdleConfiguration has already been called on "
+ "initialier %p. It should not be called again before the "
+ "initializer has been used to initialize a KMDF device object.",
+ Initializer);
+ WdfVerifierDbgBreakPoint();
+ }
+
+ //
+ // Save the driver layer's S0-idle configuration
+ //
+ deviceInitSettings->S0IdleConfig = S0IdleConfig;
+
+ deviceInitSettings->S0IdleConfigSet = TRUE;
+ return;
+}
+
+NTSTATUS
+InitializeDeviceSettingsForS0Idle(
+ _In_ WDFDEVICE Device,
+ _Inout_ PPOFX_DEVICE_CONTEXT DevCtx
+ )
+/*++
+Routine Description:
+ In this routine we initialize our S0-idle power management related settings
+ for a device.
+
+Arguments:
+
+ Device - Handle to the KMDF device object
+
+ DevCtx - Pointer to our context space for the device object
+
+Return Value:
+
+ An NTSTATUS value representing success or failure of the function.
+--*/
+{
+ NTSTATUS status;
+ WDF_OBJECT_ATTRIBUTES objectAttributes;
+ WDF_WORKITEM_CONFIG workItemConfig;
+
+ PAGED_CODE();
+
+ //
+ // Create a work item that we'll queue in response to the device-power-
+ // required callback. This is only needed if S0-idle power management is
+ // supported. Also, queuing a work item can cause pageable data to be
+ // accessed, so we do this only if the device is not in the paging path.
+ // Therefore, we create the work item only if we know that the device will
+ // never be in the paging path.
+ //
+ if (PfhS0IdleSupportedPowerPageable ==
+ DevCtx->DeviceInitSettings.S0IdleConfig) {
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes);
+ objectAttributes.ParentObject = Device;//auto-delete when device deleted
+
+ WDF_WORKITEM_CONFIG_INIT(&workItemConfig, _PowerRequiredPassiveHandler);
+ workItemConfig.AutomaticSerialization = FALSE;
+
+ status = WdfWorkItemCreate(&workItemConfig,
+ &objectAttributes,
+ &(DevCtx->PowerRequiredWorkItem));
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfWorkItemCreate failed with %!status!",
+ status);
+ goto exit;
+ }
+ }
+
+
+ //
+ // Initialize StopIdleInvokedOnDeviceStart to FALSE so that when the device
+ // is started, we invoke WdfDeviceStopIdle to prevent device idling until
+ // the power framework permits it.
+ //
+ DevCtx->StopIdleInvokedOnDeviceStart = FALSE;
+
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+NTSTATUS
+StopDeviceIdleOnDeviceStart(
+ _In_ WDFDEVICE Device,
+ _In_ PPOFX_DEVICE_CONTEXT DevCtx
+ )
+/*++
+Routine Description:
+ This routine is invoked during device start. In this routine, we invoke
+ WdfDeviceStopIdle in order to prevent the device from going to Dx as a
+ result of S0-idle power management. We do this only if S0-idle power
+ management is enabled and then only in the following cases:
+ 1. the device is being started for the first time after creation
+ 2. the device is being started for the first time after being disabled
+ (applicable to PDOs only)
+
+ When WdfDeviceStopIdle is called KMDF increments its power idle reference
+ count. Device idling can occur only when the power idle reference count
+ drops to 0. Thus by calling WdfDeviceStopIdle, we prevent device idling
+ until we make a balancing call to WdfDeviceResumeIdle to decrement the power
+ idle reference count.
+
+ When a device is disabled, KMDF resets the power idle reference count of the
+ device to 0. This is the reason we need to call WdfDeviceStopIdle in case #2
+ above.
+
+Arguments:
+
+ Device - Handle to the KMDF device object
+
+ DevCtx - Pointer to our context space for the device object
+
+Return Value:
+
+ An NTSTATUS value representing success or failure of the function.
+
+--*/
+{
+ NTSTATUS status;
+
+ if (DevCtx->StopIdleInvokedOnDeviceStart) {
+ //
+ // This is neither the first device start after creation nor the first
+ // device start after it was disabled. So nothing to do here.
+ //
+ status = STATUS_SUCCESS;
+ goto exit;
+ }
+
+ DevCtx->StopIdleInvokedOnDeviceStart = TRUE;
+
+ //
+ // If S0-idle power management is supported, prevent device idling until the
+ // power framework permits.
+ //
+ if ((PfhS0IdleSupportedPowerPageable ==
+ DevCtx->DeviceInitSettings.S0IdleConfig) ||
+ (PfhS0IdleSupportedNotPowerPageable ==
+ DevCtx->DeviceInitSettings.S0IdleConfig)) {
+ //
+ // We are currently in the process of entering D0. Therefore we specify
+ // 'FALSE' for the WaitForD0 parameter in the WdfDeviceDeviceStopIdle
+ // call below. Specifying 'TRUE' would result in the deadlock because we
+ // can't block waiting for D0 while we are in the process of entering
+ // D0. However, the call to WdfDeviceStopIdle will ensure that once we
+ // enter D0, we will remain in D0 until WdfDeviceResumeIdle is called.
+ //
+ status = WdfDeviceStopIdle(Device, FALSE /* WaitForD0 */);
+ if (FALSE == NT_SUCCESS(status)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfDeviceStopIdle failed for WDFDEVICE %p. "
+ "%!status!.",
+ Device,
+ status);
+ goto exit;
+ }
+ }
+
+ status = STATUS_SUCCESS;
+
+exit:
+ return status;
+}
+
+VOID
+DevicePowerRequiredForS0Idle(
+ _In_ WDFDEVICE Device,
+ _In_ PPOFX_DEVICE_CONTEXT DevCtx
+ )
+/*++
+Routine Description:
+
+ In this routine we perform actions that are necessary in response to the
+ PO_FX_DEVICE_POWER_REQUIRED_CALLBACK callback when S0-idle power management
+ support is enabled for the device.
+
+Arguments:
+
+ Device - Handle to the KMDF device object
+
+ DevCtx - Pointer to our context space for the device object
+
+Return Value:
+
+ None
+
+--*/
+{
+ PPO_FX_DEVICE_POWER_REQUIRED_CALLBACK devicePowerRequiredCallback = NULL;
+ PDEVICE_OBJECT wdmDeviceObject = NULL;
+
+ //
+ // If the driver layer supplied a device-power-required callback, invoke it
+ //
+ devicePowerRequiredCallback =
+ DevCtx->DriverLayerPoFxCallbacks.DevicePowerRequiredCallback;
+ if (NULL != devicePowerRequiredCallback) {
+ devicePowerRequiredCallback(DevCtx->DriverLayerPoFxContext);
+ }
+
+ if (PfhS0IdleNotSupported == DevCtx->DeviceInitSettings.S0IdleConfig) {
+ //
+ // S0-idle power management is not supported. This means that the device
+ // always remains in D0, unless there is a system sleep transition,
+ // resource rebalance or device removal. In other words, the device
+ // never leaves D0 because of S0-idle. So we can immediately report to
+ // the power framework that the device is powered on.
+ //
+ PoFxReportDevicePoweredOn(DevCtx->PoHandle);
+
+ } else if (PfhS0IdleSupportedPowerPageable ==
+ DevCtx->DeviceInitSettings.S0IdleConfig) {
+ //
+ // S0-idle power management is supported and the device is not in the
+ // paging path. This means that we can access pageable data during the
+ // power transition.
+ //
+ // We need to bring the device to D0 and invoke
+ // PoFxReportDevicePoweredOn when the device is in D0. In order to
+ // achieve this we need to make a blocking call to WdfDeviceStopIdle,
+ // which causes KMDF to bring the device to D0.
+ //
+ // We cannot make a blocking call here because:
+ // * Device-power-required can be invoked at dispatch level
+ // * Even if device-power-required is invoked at passive level, it
+ // might get invoked in the context of an I/O dispatch routine of a
+ // power-managed queue. KMDF does not support blocking calls to
+ // WdfDeviceStopIdle from within the context of an I/O dispatch
+ // routine of a power-managed queue because it can lead to a
+ // deadlock in some situations.
+ // Therefore we queue a system work item for making the blocking call to
+ // WdfDeviceStopIdle.
+ //
+ // !!! IMPORTANT NOTE !!!
+ //
+ // Queuing a system work item can cause pageable data to be accessed.
+ // Therefore, we can do it only if we know that the device is not in the
+ // paging path and that we can safely access pageable data during power
+ // transitions.
+ //
+
+ //
+ // Verify that the DO_POWER_PAGABLE flag is set in the device object
+ //
+ wdmDeviceObject = WdfDeviceWdmGetDeviceObject(Device);
+ if (0 == (wdmDeviceObject->Flags & DO_POWER_PAGABLE)) {
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WDFDEVICE %p is in the paging path, but "
+ "PfhSetNotPowerPageable has not been invoked for it.",
+ Device);
+ WdfVerifierDbgBreakPoint();
+ }
+
+ //
+ // As described in the above comments, we queue a work item to bring the
+ // device to D0.
+ //
+ WdfWorkItemEnqueue(DevCtx->PowerRequiredWorkItem);
+
+ } else {
+ //
+ // !!! IMPORTANT NOTE !!!
+ //
+ // S0-idle power management is supported and the device might be in the
+ // paging path. This means that we cannot access pageable data during
+ // the power transition. As described in the comments above, queueing a
+ // system work item to bring the device to D0 can cause pageable data to
+ // be accessed.
+ //
+ // Therefore, the current implementation of the power framework helper
+ // library does not automatically bring the device to D0 in response to
+ // a device-power-required callback, if the device is in the paging path
+ // and cannot access pageable data during power transitions. In such
+ // cases the driver layer should supply its own device-power-required
+ // callback. In this callback, it should queue work to its own dedicated
+ // worker thread (instead of a system work item) to make a blocking call
+ // to WdfDeviceStopIdle. When WdfDeviceStopIdle returns, the device is
+ // guaranteed to be in D0 so the driver can call
+ // PoFxReportDevicePoweredOn at that point.
+ //
+ ASSERT(PfhS0IdleSupportedNotPowerPageable ==
+ DevCtx->DeviceInitSettings.S0IdleConfig);
+
+ DO_NOTHING();
+ }
+
+ return;
+}
+
+VOID
+DevicePowerNotRequiredForS0Idle(
+ _In_ WDFDEVICE Device,
+ _In_ PPOFX_DEVICE_CONTEXT DevCtx
+ )
+/*++
+Routine Description:
+
+ In this routine we perform actions that are necessary in response to the
+ PO_FX_DEVICE_POWER_NOT_REQUIRED_CALLBACK callback when S0-idle power
+ management support is enabled for the device.
+
+Arguments:
+
+ Device - Handle to the KMDF device object
+
+ DevCtx - Pointer to our context space for the device object
+
+Return Value:
+
+ None
+
+--*/
+{
+ PPO_FX_DEVICE_POWER_NOT_REQUIRED_CALLBACK
+ devicePowerNotRequiredCallback = NULL;
+
+ //
+ // If the driver layer supplied a device-power-not-required callback, invoke
+ // it
+ //
+ devicePowerNotRequiredCallback =
+ DevCtx->DriverLayerPoFxCallbacks.DevicePowerNotRequiredCallback;
+ if (NULL != devicePowerNotRequiredCallback) {
+ devicePowerNotRequiredCallback(DevCtx->DriverLayerPoFxContext);
+ }
+
+ if ((PfhS0IdleSupportedPowerPageable ==
+ DevCtx->DeviceInitSettings.S0IdleConfig) ||
+ (PfhS0IdleSupportedNotPowerPageable ==
+ DevCtx->DeviceInitSettings.S0IdleConfig)) {
+ //
+ // If S0-idle power management is supported, enable KMDF to power down
+ // the device to Dx.
+ //
+ WdfDeviceResumeIdle(Device);
+ }
+
+ return;
+}
+
+VOID
+_PowerRequiredPassiveHandler(
+ _In_ WDFWORKITEM WorkItem
+ )
+/*++
+Routine Description:
+
+ This is the callback function invoked at passive level in the context of a
+ system worker thread when we queue a work item in response to the device-
+ power-required callback.
+
+Arguments:
+
+ WorkItem - Handle to the KMDF work item object
+
+Return Value:
+
+ None
+
+--*/
+{
+ NTSTATUS status;
+ WDFDEVICE device = NULL;
+ PPOFX_DEVICE_CONTEXT devCtx = NULL;
+
+ PAGED_CODE();
+
+ //
+ // Get the handle to the KMDF device object
+ //
+ device = WdfWorkItemGetParentObject(WorkItem);
+
+ //
+ // Get the device context
+ //
+ devCtx = HelperGetDeviceContext(device);
+
+ //
+ // Make a blocking call to WdfDeviceStopIdle in order to get KMDF to bring
+ // the device to D0 and/or keep it in D0.
+ //
+ status = WdfDeviceStopIdle(device, TRUE /*WaitForD0*/);
+ if (FALSE == NT_SUCCESS(status)) {
+ //
+ // The call to WdfDeviceStopIdle failed. However, we need to call
+ // PoFxReportDevicePoweredOn regardless of success or failure because
+ // we need to unblock the power framework which is waiting for us to
+ // make this call. In case of failure, KMDF would have already declared
+ // the device to be in a failed state and initiated a PNP removal.
+ //
+ Trace(TRACE_LEVEL_ERROR,
+ "%!FUNC! - WdfDeviceStopIdle failed for WDFDEVICE %p. "
+ "%!status!.",
+ device,
+ status);
+
+ //
+ // Fall through to PoFxReportDevicePoweredOn call below
+ // || || ||
+ // || || ||
+ // \/ \/ \/
+ //
+ }
+
+ //
+ // Inform the power framework that the device is now powered on
+ //
+ PoFxReportDevicePoweredOn(devCtx->PoHandle);
+ return;
+}
+
+#endif // PFH_S0IDLE_SUPPORTED \ No newline at end of file
diff --git a/pofx/WDF/Driver/MultiComp/lib/s0idle.h b/pofx/WDF/Driver/MultiComp/lib/s0idle.h
new file mode 100644
index 00000000..0ad10327
--- /dev/null
+++ b/pofx/WDF/Driver/MultiComp/lib/s0idle.h
@@ -0,0 +1,113 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ s0idle.h
+
+Abstract:
+ Private header file that enables the power framework library to be
+ conditionally compiled either with or without S0-idle power management
+ support, based on the value of the PFH_S0IDLE_SUPPORTED compile-time
+ switch.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#if !defined(_S0IDLE_H_)
+#define _S0IDLE_H_
+
+EVT_WDF_WORKITEM _PowerRequiredPassiveHandler;
+
+#if PFH_S0IDLE_SUPPORTED
+
+//
+// The power framework library is compiled with S0-idle power management support
+//
+NTSTATUS
+InitializeDeviceSettingsForS0Idle(
+ _In_ WDFDEVICE Device,
+ _Inout_ PPOFX_DEVICE_CONTEXT DevCtx
+ );
+
+NTSTATUS
+StopDeviceIdleOnDeviceStart(
+ _In_ WDFDEVICE Device,
+ _In_ PPOFX_DEVICE_CONTEXT DevCtx
+ );
+
+VOID
+DevicePowerRequiredForS0Idle(
+ _In_ WDFDEVICE Device,
+ _In_ PPOFX_DEVICE_CONTEXT DevCtx
+ );
+
+VOID
+DevicePowerNotRequiredForS0Idle(
+ _In_ WDFDEVICE Device,
+ _In_ PPOFX_DEVICE_CONTEXT DevCtx
+ );
+
+#define INITIALIZE_DEVICE_SETTINGS_FOR_S0_IDLE(_Device, _DevCtx) \
+ InitializeDeviceSettingsForS0Idle(_Device, _DevCtx)
+
+#define STOP_DEVICE_IDLE_ON_DEVICE_START(_Status, _Device, _DevCtx, _ExitLabel)\
+{ \
+ _Status = StopDeviceIdleOnDeviceStart(_Device, _DevCtx); \
+ if (FALSE == NT_SUCCESS(_Status)) { \
+ goto _ExitLabel; \
+ } \
+}
+
+//
+// For PDOs, if the device is restarted after being disabled, we need to
+// prevent device idle until the power framework permits it. Therefore, we
+// reset the StopIdleInvokedOnDeviceStart value here, so that a subsequent
+// restart causes us to invoke WdfDeviceStopIdle to prevent device idling.
+//
+#define SELF_MANAGED_IO_FLUSH_FOR_S0_IDLE(_DevCtx) \
+{ \
+ _DevCtx->StopIdleInvokedOnDeviceStart = FALSE; \
+}
+
+#define DEVICE_POWER_REQUIRED_FOR_S0_IDLE(_Device, _DevCtx) \
+ DevicePowerRequiredForS0Idle(_Device,_DevCtx)
+
+#define DEVICE_POWER_NOT_REQUIRED_FOR_S0_IDLE(_Device, _DevCtx) \
+ DevicePowerNotRequiredForS0Idle(_Device, _DevCtx)
+
+#else // PFH_S0IDLE_SUPPORTED
+
+
+//
+// The power framework library is compiled without S0-idle power management
+// support
+//
+#define INITIALIZE_DEVICE_SETTINGS_FOR_S0_IDLE(_Device, _DevCtx) (0)
+#define STOP_DEVICE_IDLE_ON_DEVICE_START(_Status, _Device, _DevCtx, _ExitLabel)\
+ (0)
+#define SELF_MANAGED_IO_FLUSH_FOR_S0_IDLE(_DevCtx) (0)
+
+//
+// The current implementation does not support S0-idle. It assumes that the
+// device always remains in D0 unless there is a system sleep transition,
+// resource rebalance or device removal. Therefore, we call
+// PoFxReportDevicePoweredOn immediately.
+//
+#define DEVICE_POWER_REQUIRED_FOR_S0_IDLE(_Device, _DevCtx) \
+ PoFxReportDevicePoweredOn(_DevCtx->PoHandle)
+
+#define DEVICE_POWER_NOT_REQUIRED_FOR_S0_IDLE(_Device, _DevCtx) (0)
+
+#endif // PFH_S0IDLE_SUPPORTED
+
+#endif // _S0IDLE_H_