/Drivers/xenvusb/Device.cpp

https://github.com/stefanopanella/xc-vusb · C++ · 1438 lines · 932 code · 153 blank · 353 comment · 79 complexity · 1ceb382e4454aaf7318be2fd31c9f617 MD5 · raw file

  1. //
  2. // Copyright (c) Citrix Systems, Inc.
  3. //
  4. /// @file Device.cpp USB FDO Device implementation.
  5. //
  6. //
  7. // Permission is hereby granted, free of charge, to any person obtaining a copy
  8. // of this software and associated documentation files (the "Software"), to deal
  9. // in the Software without restriction, including without limitation the rights
  10. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. // copies of the Software, and to permit persons to whom the Software is
  12. // furnished to do so, subject to the following conditions:
  13. //
  14. // The above copyright notice and this permission notice shall be included in
  15. // all copies or substantial portions of the Software.
  16. //
  17. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. // THE SOFTWARE.
  24. //
  25. #include "driver.h"
  26. #include "UsbConfig.h"
  27. #include <wdmguid.h>
  28. #include <devguid.h>
  29. #include <initguid.h>
  30. #include "RootHubPdo.h"
  31. struct USB_FDO_INTERRUPT_CONTEXT
  32. {
  33. PUSB_FDO_CONTEXT FdoContext;
  34. ULONGLONG IsrEntered;
  35. ULONGLONG IsrActive; //!< this was for us.
  36. };
  37. typedef struct USB_FDO_INTERRUPT_CONTEXT * PUSB_FDO_INTERRUPT_CONTEXT;
  38. // --XT-- WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(USB_FDO_INTERRUPT_CONTEXT, DeviceGetInterruptContext)
  39. EVT_WDF_DEVICE_PREPARE_HARDWARE FdoEvtDevicePrepareHardware;
  40. EVT_WDF_DEVICE_RELEASE_HARDWARE FdoEvtDeviceReleaseHardware;
  41. EVT_WDF_DEVICE_D0_ENTRY FdoEvtDeviceD0Entry;
  42. EVT_WDF_DEVICE_D0_ENTRY_POST_INTERRUPTS_ENABLED FdoEvtDeviceD0EntryPostInterruptsEnabled;
  43. EVT_WDF_DEVICE_D0_EXIT FdoEvtDeviceD0Exit;
  44. EVT_WDF_DEVICE_SURPRISE_REMOVAL FdoEvtDeviceSurpriseRemoval;
  45. EVT_WDF_DEVICE_CONTEXT_CLEANUP FdoEvtDeviceContextCleanup;
  46. // --XT-- EVT_WDF_DPC FdoEvtDeviceDpcFunc;
  47. // --XT-- EVT_WDF_INTERRUPT_ISR FdoEvtDeviceIsrFunc;
  48. // --XT-- EVT_WDF_INTERRUPT_ENABLE FdoEvtDeviceInterruptEnable;
  49. // --XT-- EVT_WDF_INTERRUPT_DISABLE FdoEvtDeviceInterruptDisable;
  50. EVT_WDF_TIMER FdoEvtTimerFunc;
  51. EVT_WDF_DEVICE_FILE_CREATE FdoEvtDeviceFileCreate;
  52. EVT_WDF_FILE_CLOSE FdoEvtFileClose;
  53. // --XT-- New callback type for the DPC
  54. EVTCHN_HANDLER_CB FdoEvtDeviceDpcFunc;
  55. NTSTATUS LateSetup(IN WDFDEVICE);
  56. NTSTATUS XenConfigure(IN PUSB_FDO_CONTEXT);
  57. NTSTATUS XenDeconfigure(IN PUSB_FDO_CONTEXT);
  58. VOID CleanupDisconnectedDevice(PUSB_FDO_CONTEXT fdoContext);
  59. PCHAR
  60. DbgDevicePowerString(
  61. IN WDF_POWER_DEVICE_STATE Type);
  62. NTSTATUS
  63. InitScratchpad(
  64. IN PUSB_FDO_CONTEXT fdoContext);
  65. VOID
  66. DeleteScratchpad(
  67. IN PUSB_FDO_CONTEXT fdoContext);
  68. NTSTATUS
  69. SetPdoDescriptors(
  70. IN PWDFDEVICE_INIT DeviceInit,
  71. USB_DEVICE_DESCRIPTOR& descriptor,
  72. PUSB_CONFIGURATION_DESCRIPTOR config,
  73. PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor,
  74. POS_COMPAT_ID compatIds);
  75. /**
  76. * @brief Wraps WDFDEVICE lock acquire operations.
  77. * Records the lock owner for debugging. Sanity tests are DBG only.
  78. *
  79. * @param[in] fdoContext. The context for the FDO device.
  80. *
  81. */
  82. _Acquires_lock_(fdoContext->WdfDevice)
  83. VOID
  84. AcquireFdoLock(
  85. IN PUSB_FDO_CONTEXT fdoContext)
  86. {
  87. PETHREAD caller = PsGetCurrentThread();
  88. if (!HTSASSERT(fdoContext->lockOwner != caller))
  89. {
  90. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
  91. __FUNCTION__": Assertion failure lockowner %p != caller %p\n",
  92. fdoContext->lockOwner,
  93. caller);
  94. }
  95. WdfObjectAcquireLock(fdoContext->WdfDevice);
  96. if (!HTSASSERT(fdoContext->lockOwner == NULL))
  97. {
  98. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
  99. __FUNCTION__": Assertion failure lockowner %p == NULL\n",
  100. fdoContext->lockOwner);
  101. }
  102. fdoContext->lockOwner = PsGetCurrentThread();
  103. }
  104. /**
  105. * @brief Wraps WDFDEVICE lock release operations.
  106. * Resets the lock owner to NULL. Sanity tests are DBG only.
  107. *
  108. * @param[in] fdoContext. The context for the FDO device.
  109. */
  110. _Requires_lock_held_(fdoContext->WdfDevice)
  111. VOID
  112. ReleaseFdoLock(
  113. IN PUSB_FDO_CONTEXT fdoContext)
  114. {
  115. PETHREAD caller = PsGetCurrentThread();
  116. if (!HTSASSERT(caller == fdoContext->lockOwner))
  117. {
  118. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER,
  119. __FUNCTION__": Assertion failure caller %p == lockOwner %p\n",
  120. caller,
  121. fdoContext->lockOwner);
  122. }
  123. fdoContext->lockOwner = NULL;
  124. WdfObjectReleaseLock(fdoContext->WdfDevice);
  125. }
  126. /**
  127. * @brief Called by the framework when a new PDO has arrived that this driver manages.
  128. * The device in question is not operational at this point in time.
  129. *
  130. * @param[in] Driver handle to WDFDRIVER object created by DriverEntry()
  131. * @param[in,out] DeviceInit device init object provided by framework.
  132. *
  133. * @returns NTSTATUS value indicating success or failure.
  134. *
  135. */
  136. NTSTATUS
  137. FdoEvtDeviceAdd(
  138. _In_ WDFDRIVER Driver,
  139. _Inout_ PWDFDEVICE_INIT DeviceInit
  140. )
  141. {
  142. UNREFERENCED_PARAMETER(Driver);
  143. WDF_OBJECT_ATTRIBUTES attributes;
  144. NTSTATUS status;
  145. TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE,
  146. __FUNCTION__"\n");
  147. WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER);
  148. WdfDeviceInitSetExclusive(DeviceInit, FALSE);
  149. WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
  150. WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, USB_FDO_CONTEXT);
  151. attributes.EvtCleanupCallback = FdoEvtDeviceContextCleanup;
  152. //
  153. // Device state callbacks.
  154. //
  155. WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
  156. WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
  157. pnpPowerCallbacks.EvtDevicePrepareHardware = FdoEvtDevicePrepareHardware;
  158. pnpPowerCallbacks.EvtDeviceReleaseHardware = FdoEvtDeviceReleaseHardware;
  159. pnpPowerCallbacks.EvtDeviceD0Entry = FdoEvtDeviceD0Entry;
  160. pnpPowerCallbacks.EvtDeviceD0EntryPostInterruptsEnabled = FdoEvtDeviceD0EntryPostInterruptsEnabled;
  161. pnpPowerCallbacks.EvtDeviceD0Exit = FdoEvtDeviceD0Exit;
  162. pnpPowerCallbacks.EvtDeviceSurpriseRemoval = FdoEvtDeviceSurpriseRemoval;
  163. WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
  164. //
  165. // establish a request context
  166. //
  167. WDF_OBJECT_ATTRIBUTES requestAttributes;
  168. WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&requestAttributes, FDO_REQUEST_CONTEXT);
  169. WdfDeviceInitSetRequestAttributes(DeviceInit, &requestAttributes);
  170. //
  171. // static verifier seems to have a rule that the FDO must call
  172. // WdfFdoInitSetDefaultChildListConfig if any component in the driver has
  173. // dynamic child devices, and the roothub has one if it is not operating in
  174. // connect usb hub mode.
  175. //
  176. WDF_CHILD_LIST_CONFIG config;
  177. WDF_CHILD_LIST_CONFIG_INIT(&config,
  178. sizeof(PDO_INDENTIFICATION_DESCRIPTION),
  179. FdoEvtChildListCreateDevice);
  180. WdfFdoInitSetDefaultChildListConfig(DeviceInit,
  181. &config,
  182. WDF_NO_OBJECT_ATTRIBUTES);
  183. //
  184. // add a preprocess callback for QueryInterface to support multi-version USBDI intefaces
  185. //
  186. UCHAR MinorFunctionTable[1] = {IRP_MN_QUERY_INTERFACE};
  187. status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
  188. DeviceInit,
  189. FdoPreProcessQueryInterface,
  190. IRP_MJ_PNP,
  191. MinorFunctionTable,
  192. 1);
  193. if (!NT_SUCCESS(status))
  194. {
  195. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  196. __FUNCTION__": WdfDeviceInitAssignWdmIrpPreprocessCallback failed error %x\n",
  197. status);
  198. return status;
  199. }
  200. //
  201. // Add create/close handlers
  202. //
  203. WDF_OBJECT_ATTRIBUTES fileAttributes;
  204. WDF_OBJECT_ATTRIBUTES_INIT(&fileAttributes);
  205. fileAttributes.SynchronizationScope = WdfSynchronizationScopeNone;
  206. WDF_FILEOBJECT_CONFIG FileObjectConfig;
  207. WDF_FILEOBJECT_CONFIG_INIT(
  208. &FileObjectConfig,
  209. FdoEvtDeviceFileCreate,
  210. FdoEvtFileClose,
  211. WDF_NO_EVENT_CALLBACK);
  212. WdfDeviceInitSetFileObjectConfig(
  213. DeviceInit,
  214. &FileObjectConfig,
  215. &fileAttributes);
  216. WDFDEVICE device;
  217. status = WdfDeviceCreate(&DeviceInit, &attributes, &device);
  218. if (!NT_SUCCESS(status))
  219. {
  220. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  221. __FUNCTION__": WdfDeviceCreate failed error %x\n",
  222. status);
  223. return status;
  224. }
  225. PUSB_FDO_CONTEXT fdoContext = DeviceGetFdoContext(device);
  226. RtlZeroMemory(fdoContext, sizeof(USB_FDO_CONTEXT));
  227. fdoContext->WdfDevice = device;
  228. KeInitializeEvent(&fdoContext->resetCompleteEvent, SynchronizationEvent, FALSE);
  229. //
  230. // allocate the dpc request collection.
  231. //
  232. WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
  233. attributes.ParentObject = device;
  234. status = WdfCollectionCreate(&attributes,
  235. &fdoContext->RequestCollection);
  236. if (!NT_SUCCESS(status))
  237. {
  238. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  239. __FUNCTION__": WdfCollectionCreate failed\n");
  240. return status;
  241. };
  242. //
  243. // The FDO is the USB Controller, create a device interface for that.
  244. //
  245. status = WdfDeviceCreateDeviceInterface(
  246. device,
  247. &GUID_DEVINTERFACE_USB_HOST_CONTROLLER,
  248. NULL);
  249. if (!NT_SUCCESS(status))
  250. {
  251. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  252. __FUNCTION__": WdfDeviceCreateDeviceInterface for device %p error %x\n",
  253. device,
  254. status);
  255. return status;
  256. }
  257. WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
  258. attributes.ParentObject = device;
  259. status = WdfStringCreate(NULL, &attributes, &fdoContext->hcdsymlink);
  260. if (!NT_SUCCESS(status))
  261. {
  262. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  263. __FUNCTION__": WdfStringCreate for device %p error %x\n",
  264. device,
  265. status);
  266. return status;
  267. }
  268. status = WdfDeviceRetrieveDeviceInterfaceString(device,
  269. &GUID_DEVINTERFACE_USB_HOST_CONTROLLER,
  270. NULL,
  271. fdoContext->hcdsymlink);
  272. if (!NT_SUCCESS(status))
  273. {
  274. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  275. __FUNCTION__": WdfStringCreate for device %p error %x\n",
  276. device,
  277. status);
  278. return status;
  279. }
  280. //
  281. // Some of our resources are independent of the device state and
  282. // can be allocated/initialized here.
  283. //
  284. status = InitScratchpad(fdoContext);
  285. if (!NT_SUCCESS(status))
  286. {
  287. return status;
  288. }
  289. //
  290. // Initialize the I/O Package and any Queues
  291. //
  292. status = FdoQueueInitialize(device);
  293. if (!NT_SUCCESS(status))
  294. {
  295. return status;
  296. }
  297. //
  298. // --XT-- All of the WDF ISR and DPC setup code was removed
  299. // here. The DPC is now setup through the Xen interface in the
  300. // previous call. Note the event channel is setup but not active
  301. // until the backend is connected.
  302. //
  303. //
  304. // Allocate a watchdog timer for our Xen interface.
  305. //
  306. WDF_TIMER_CONFIG timerConfig;
  307. WDF_OBJECT_ATTRIBUTES timerAttributes;
  308. WDF_TIMER_CONFIG_INIT(
  309. &timerConfig,
  310. FdoEvtTimerFunc);
  311. WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
  312. timerAttributes.ParentObject = device;
  313. timerAttributes.ExecutionLevel = WdfExecutionLevelPassive;
  314. status = WdfTimerCreate(
  315. &timerConfig,
  316. &timerAttributes,
  317. &fdoContext->WatchdogTimer);
  318. if (!NT_SUCCESS(status))
  319. {
  320. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  321. __FUNCTION__": WdfTimerCreate error %x\n",
  322. status);
  323. return status;
  324. }
  325. //
  326. // Create a collection of work items.
  327. //
  328. WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
  329. attributes.ParentObject = device;
  330. status = WdfCollectionCreate(&attributes,
  331. &fdoContext->FreeWorkItems);
  332. if (!NT_SUCCESS(status))
  333. {
  334. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  335. __FUNCTION__": WdfCollectionCreate error %x\n",
  336. status);
  337. return status;
  338. }
  339. for (ULONG index = 0; index < INIT_WORK_ITEM_COUNT; index++)
  340. {
  341. WDFWORKITEM workitem = NewWorkItem(fdoContext,
  342. NULL,
  343. 0,0,0,0);
  344. if (workitem)
  345. {
  346. status = WdfCollectionAdd(fdoContext->FreeWorkItems, workitem);
  347. if (!NT_SUCCESS(status))
  348. {
  349. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  350. __FUNCTION__": WdfCollectionAdd for workitem index %d error %x\n",
  351. index,
  352. status);
  353. WdfObjectDelete(workitem);
  354. return status;
  355. }
  356. }
  357. }
  358. PNP_BUS_INFORMATION busInformation;
  359. busInformation.BusNumber = 0;
  360. busInformation.BusTypeGuid = GUID_BUS_TYPE_USB;
  361. busInformation.LegacyBusType = PNPBus;
  362. WdfDeviceSetBusInformationForChildren(
  363. device,
  364. &busInformation);
  365. if (NT_SUCCESS(status)) {
  366. status = LateSetup(device);
  367. }
  368. return status;
  369. }
  370. /**
  371. * @brief The device object is being deleted.
  372. * This callback frees any resources allocated in FdoEvtDeviceAdd().
  373. *
  374. * @param[in] Device handle to WDFOBJECT created by FdoEvtDeviceAdd().
  375. *
  376. */
  377. VOID
  378. FdoEvtDeviceContextCleanup (
  379. _In_ WDFOBJECT Device)
  380. {
  381. PUSB_FDO_CONTEXT fdoContext = DeviceGetFdoContext(Device);
  382. TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE,
  383. __FUNCTION__"\n");
  384. CleanupDisconnectedDevice(fdoContext);
  385. //
  386. // scratchpad cleanup.
  387. //
  388. DeleteScratchpad(fdoContext);
  389. //
  390. // xen interface cleanup
  391. //
  392. if (fdoContext->Xen)
  393. {
  394. DeallocateXenInterface(fdoContext->Xen);
  395. }
  396. if (fdoContext->CompatIds)
  397. {
  398. ExFreePool(fdoContext->CompatIds);
  399. fdoContext->CompatIds = NULL;
  400. }
  401. }
  402. /**
  403. * @brief A newly arrived device needs to be initialized and made operational.
  404. * Connects the new device object to its hardware resources.
  405. *
  406. * @param[in] Device handle to the WDFDEVICE created by FdoEvtDeviceAdd().
  407. * @oaram[in] ResourcesTranslated translated resource list for the device.
  408. *
  409. * @returns NSTATUS value indicating success or failure.
  410. *
  411. */
  412. NTSTATUS
  413. FdoEvtDevicePrepareHardware (
  414. _In_ WDFDEVICE,
  415. WDFCMRESLIST,
  416. _In_ WDFCMRESLIST)
  417. {
  418. // --XT-- PUSB_FDO_CONTEXT fdoContext = DeviceGetFdoContext(Device);
  419. TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE,
  420. __FUNCTION__"\n");
  421. //
  422. // map the hardware resources
  423. //
  424. // --XT-- Removed call to MapXenDeviceRegisters and args to this call.
  425. return STATUS_SUCCESS;
  426. }
  427. /**
  428. * @brief Cleanup *AFTER* a device is no longer accessible.
  429. *
  430. * @param[in] Device handle to the WDFDEVICE created by FdoEvtDeviceAdd().
  431. *
  432. * @returns NTSTATUS value indicating success or failure. Should never fail.
  433. *
  434. */
  435. NTSTATUS
  436. FdoEvtDeviceReleaseHardware(
  437. IN WDFDEVICE Device,
  438. IN WDFCMRESLIST )
  439. {
  440. PUSB_FDO_CONTEXT fdoContext = DeviceGetFdoContext(Device);
  441. TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE,
  442. __FUNCTION__"\n");
  443. FreeUsbConfigData(fdoContext);
  444. return STATUS_SUCCESS;
  445. }
  446. /**
  447. * @brief Transition to fully powered state.
  448. *
  449. * @param[in] Device handle to the WDFDEVICE created by FdoEvtDeviceAdd().
  450. * @param[in] PreviousState
  451. *
  452. * @returns NTSTATUS value indicating success or failure.
  453. *
  454. */
  455. NTSTATUS
  456. FdoEvtDeviceD0Entry(
  457. IN WDFDEVICE Device,
  458. IN WDF_POWER_DEVICE_STATE PreviousState)
  459. {
  460. UNREFERENCED_PARAMETER(Device);
  461. // PUSB_FDO_CONTEXT fdoContext = DeviceGetFdoContext(Device);
  462. NTSTATUS status = STATUS_SUCCESS;
  463. TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE,
  464. __FUNCTION__": PreviousState %s\n",
  465. DbgDevicePowerString(PreviousState));
  466. XXX_TODO("STUB");
  467. return status;
  468. }
  469. NTSTATUS LateSetup(IN WDFDEVICE Device)
  470. {
  471. PUSB_FDO_CONTEXT fdoContext = DeviceGetFdoContext(Device);
  472. NTSTATUS status = STATUS_SUCCESS;
  473. //
  474. // set up the XEN connection.
  475. //
  476. if (!fdoContext->NxprepBoot)
  477. {
  478. status = XenConfigure(fdoContext);
  479. if (!NT_SUCCESS(status))
  480. {
  481. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  482. __FUNCTION__": Device %p xen configuration error %x",
  483. fdoContext->WdfDevice,
  484. status);
  485. return status;
  486. }
  487. //
  488. // get the USB device config data.
  489. //
  490. status = GetUsbConfigData(fdoContext);
  491. if (!NT_SUCCESS(status))
  492. {
  493. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  494. __FUNCTION__": %s GetUsbConfigData error %x\n",
  495. fdoContext->FrontEndPath,
  496. status);
  497. }
  498. else if (fdoContext->BlacklistDevice)
  499. {
  500. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  501. __FUNCTION__": %s Device is blacklisted. No PDO.\n",
  502. fdoContext->FrontEndPath);
  503. }
  504. else
  505. {
  506. fdoContext->PortConnected = TRUE;
  507. }
  508. }
  509. if (fdoContext->PortConnected || fdoContext->NxprepBoot)
  510. {
  511. //
  512. // create a child device.
  513. //
  514. status = CreateRootHubPdo(fdoContext);
  515. if (NT_SUCCESS(status) && !fdoContext->NxprepBoot)
  516. {
  517. WdfTimerStart(fdoContext->WatchdogTimer, WDF_REL_TIMEOUT_IN_SEC(2));
  518. }
  519. }
  520. TraceEvents(NT_SUCCESS(status) ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_ERROR,
  521. TRACE_DEVICE,
  522. __FUNCTION__": %s Device %p status %x\n",
  523. fdoContext->FrontEndPath,
  524. fdoContext->WdfDevice,
  525. status);
  526. return status;
  527. }
  528. /**
  529. * @brief Transition to fully powered state with interrupts enabled.
  530. * This is the first chance we have to interact with a fully functional device,
  531. * so collect the device configuration so we can create a USB pdo that can be
  532. * enumerated by PnP.
  533. *
  534. * Allow failures to "succeed" (return STATUS_SUCCESS) so that the
  535. * dreaded Yellow Bang does not occur. Instead, this device will appear
  536. * normally in Windows Device Manager, however it will not instantiate
  537. * its child RootHub PDO.
  538. *
  539. * @param[in] Device handle to the WDFDEVICE created by FdoEvtDeviceAdd().
  540. *
  541. * @returns NTSTATUS value indicating success or failure.
  542. *
  543. */
  544. NTSTATUS
  545. FdoEvtDeviceD0EntryPostInterruptsEnabled(
  546. IN WDFDEVICE device,
  547. IN WDF_POWER_DEVICE_STATE)
  548. {
  549. NTSTATUS status = STATUS_SUCCESS;
  550. PUSB_FDO_CONTEXT fdoContext = DeviceGetFdoContext(device);
  551. BOOLEAN online = XenCheckOnline(fdoContext->Xen);
  552. if (!online)
  553. {
  554. /* Backend device disappeared behind our back (while guest was in S3, for example), clean it up.. */
  555. CleanupDisconnectedDevice(fdoContext);
  556. return STATUS_SUCCESS;
  557. }
  558. if (!fdoContext->XenConfigured)
  559. {
  560. status = XenConfigure(fdoContext);
  561. if (!NT_SUCCESS(status))
  562. {
  563. return status;
  564. }
  565. WdfTimerStart(fdoContext->WatchdogTimer, WDF_REL_TIMEOUT_IN_SEC(2));
  566. }
  567. return status;
  568. }
  569. NTSTATUS
  570. XenConfigure(
  571. IN PUSB_FDO_CONTEXT fdoContext)
  572. {
  573. NTSTATUS status;
  574. if (fdoContext->XenConfigured)
  575. {
  576. TraceEvents(TRACE_LEVEL_WARNING, TRACE_DEVICE,
  577. __FUNCTION__": xen connection already configured");
  578. return STATUS_SUCCESS;
  579. }
  580. if (fdoContext->Xen)
  581. {
  582. DeallocateXenInterface(fdoContext->Xen);
  583. }
  584. fdoContext->Xen = AllocateXenInterface(fdoContext);
  585. if (!fdoContext->Xen)
  586. {
  587. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  588. __FUNCTION__": AllocateXenInterface failed\n");
  589. return STATUS_UNSUCCESSFUL;
  590. }
  591. status = XenDeviceInitialize(fdoContext->Xen, FdoEvtDeviceDpcFunc);
  592. if (!NT_SUCCESS(status))
  593. {
  594. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  595. __FUNCTION__": failed to initialize xen device");
  596. return status;
  597. }
  598. status = XenDeviceConnectBackend(fdoContext->Xen);
  599. if (!NT_SUCCESS(status))
  600. {
  601. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  602. __FUNCTION__": failed to connect backend");
  603. return status;
  604. }
  605. fdoContext->XenConfigured = TRUE;
  606. return STATUS_SUCCESS;
  607. }
  608. NTSTATUS
  609. XenDeconfigure(IN PUSB_FDO_CONTEXT fdoContext)
  610. {
  611. if (!fdoContext->XenConfigured)
  612. {
  613. return STATUS_SUCCESS;
  614. }
  615. fdoContext->XenConfigured = FALSE;
  616. XenDisconnectDPC(fdoContext->Xen);
  617. XenDeviceDisconnectBackend(fdoContext->Xen);
  618. return STATUS_SUCCESS;
  619. }
  620. /**
  621. * @brief handles child device processing on device removal.
  622. *
  623. * @param[in] Device handle to the WDFDEVICE created by FdoEvtDeviceAdd().
  624. *
  625. */
  626. VOID RemoveAllChildDevices(
  627. IN WDFDEVICE Device)
  628. {
  629. WdfFdoLockStaticChildListForIteration(Device);
  630. WDFDEVICE hChild = NULL;
  631. while ((hChild = WdfFdoRetrieveNextStaticChild(
  632. Device,
  633. hChild,
  634. WdfRetrieveAddedChildren)) != NULL)
  635. {
  636. NTSTATUS Status = WdfPdoMarkMissing(hChild);
  637. if (!NT_SUCCESS(Status))
  638. {
  639. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  640. __FUNCTION__": Device %p WdfPdoMarkMissing %p error %x\n",
  641. Device,
  642. hChild,
  643. Status);
  644. XXX_TODO("What to do with a pdo we can't mark missing?\n");
  645. }
  646. else
  647. {
  648. TraceEvents(TRACE_LEVEL_WARNING, TRACE_DEVICE,
  649. __FUNCTION__": Device %p WdfPdoMarkMissing %p\n",
  650. Device,
  651. hChild);
  652. WDF_DEVICE_STATE deviceState;
  653. WDF_DEVICE_STATE_INIT (&deviceState);
  654. deviceState.Removed = WdfTrue;
  655. WdfDeviceSetDeviceState(hChild,
  656. &deviceState);
  657. }
  658. }
  659. WdfFdoUnlockStaticChildListFromIteration(Device);
  660. }
  661. /**
  662. * @brief common processing to "unplug" a device from the Windows perspective.
  663. * Must be called with lock held.
  664. *
  665. * @param[in] fdoContext. The context for the FDO device.
  666. *
  667. */
  668. _Requires_lock_held_(fdoContext->WdfDevice)
  669. VOID
  670. FdoUnplugDevice(
  671. IN PUSB_FDO_CONTEXT fdoContext)
  672. {
  673. if (!fdoContext->DeviceUnplugged)
  674. {
  675. TraceEvents(TRACE_LEVEL_WARNING, TRACE_DEVICE,
  676. __FUNCTION__": %s\n",
  677. fdoContext->FrontEndPath);
  678. fdoContext->DeviceUnplugged = TRUE;
  679. if (fdoContext->ConfigBusy)
  680. {
  681. KeSetEvent(&fdoContext->ScratchPad.CompletionEvent, IO_NO_INCREMENT, FALSE);
  682. }
  683. if (!fdoContext->CtlrDisconnected)
  684. {
  685. ReleaseFdoLock(fdoContext);
  686. // --XT-- WdfDpcCancel(fdoContext->WdfDpc, TRUE);
  687. //
  688. // --XT-- Should disable the DPC here
  689. //
  690. XenDisconnectDPC(fdoContext->Xen);
  691. RemoveAllChildDevices(fdoContext->WdfDevice);
  692. AcquireFdoLock(fdoContext);
  693. }
  694. }
  695. DrainRequestQueue(fdoContext, TRUE);
  696. }
  697. VOID
  698. CleanupDisconnectedDevice(
  699. PUSB_FDO_CONTEXT fdoContext)
  700. {
  701. AcquireFdoLock(fdoContext);
  702. if (fdoContext->CtlrDisconnected)
  703. {
  704. ReleaseFdoLock(fdoContext);
  705. return;
  706. }
  707. fdoContext->CtlrDisconnected = TRUE;
  708. ReleaseFdoLock(fdoContext);
  709. WdfTimerStop(fdoContext->WatchdogTimer, TRUE);
  710. // --XT-- WdfDpcCancel(fdoContext->WdfDpc, TRUE);
  711. //
  712. // --XT-- This also looks like a reasonable place to turn off the event channel.
  713. //
  714. XenDisconnectDPC(fdoContext->Xen);
  715. AcquireFdoLock(fdoContext);
  716. FdoUnplugDevice(fdoContext);
  717. BOOLEAN completeRequest = TRUE;
  718. if (fdoContext->IdleRequest)
  719. {
  720. if (RequestGetRequestContext(fdoContext->IdleRequest)->RequestCompleted)
  721. {
  722. // should never happen if fdoContext->IdleRequest is not NULL.
  723. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  724. __FUNCTION__": Device %p Request %p marked completed\n",
  725. fdoContext->WdfDevice,
  726. fdoContext->IdleRequest);
  727. completeRequest = FALSE;
  728. }
  729. else if (RequestGetRequestContext(fdoContext->IdleRequest)->CancelSet)
  730. {
  731. NTSTATUS Status = WdfRequestUnmarkCancelable(fdoContext->IdleRequest);
  732. if (!NT_SUCCESS(Status))
  733. {
  734. if (Status == STATUS_CANCELLED)
  735. {
  736. //
  737. // don't complete the request here it is owned by the
  738. // cancel routine.
  739. XXX_TODO("Trace level probably too high");
  740. TraceEvents(TRACE_LEVEL_WARNING, TRACE_DEVICE,
  741. __FUNCTION__": Device %p Request %p Cancelled\n",
  742. fdoContext->WdfDevice,
  743. fdoContext->IdleRequest);
  744. completeRequest = FALSE;
  745. }
  746. //
  747. // note but ignore.
  748. //
  749. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  750. __FUNCTION__": Device %p Request %p WdfRequestUnmarkCancelable error %x\n",
  751. fdoContext->WdfDevice,
  752. fdoContext->IdleRequest,
  753. Status);
  754. }
  755. }
  756. WDFREQUEST idleRequest = fdoContext->IdleRequest;
  757. fdoContext->IdleRequest = NULL;
  758. if (completeRequest)
  759. {
  760. RequestGetRequestContext(idleRequest)->RequestCompleted = 0;
  761. ReleaseFdoLock(fdoContext);
  762. WdfRequestComplete(idleRequest, STATUS_CANCELLED);
  763. AcquireFdoLock(fdoContext);
  764. }
  765. }
  766. ReleaseFdoLock(fdoContext);
  767. if (fdoContext->XenConfigured)
  768. {
  769. CompleteRequestsFromShadow(fdoContext);
  770. }
  771. }
  772. /**
  773. * @brief Transition out of fully powered state.
  774. * This callback is invoked on unplug after FdoEvtDeviceSurpriseRemoval() or
  775. * on resource rebalance or device disable or shutdown.
  776. *
  777. * Plus take advantage of this opportunity to dump a bunch of stats on this device.
  778. *
  779. * @param[in] Device handle to the WDFDEVICE created by FdoEvtDeviceAdd().
  780. *
  781. * @returns NTSTATUS value indicating success or failure.
  782. *
  783. */
  784. NTSTATUS
  785. FdoEvtDeviceD0Exit(
  786. IN WDFDEVICE device,
  787. IN WDF_POWER_DEVICE_STATE)
  788. {
  789. PUSB_FDO_CONTEXT fdoContext = DeviceGetFdoContext(device);
  790. TraceEvents(TRACE_LEVEL_WARNING, TRACE_DEVICE,
  791. __FUNCTION__": %s Device %p\n",
  792. fdoContext->FrontEndPath,
  793. fdoContext->WdfDevice);
  794. WdfTimerStop(fdoContext->WatchdogTimer, TRUE);
  795. XenDeconfigure(fdoContext);
  796. TraceEvents(TRACE_LEVEL_WARNING, TRACE_DEVICE,
  797. __FUNCTION__": %s\n"
  798. " Direct Transfers: %I64d errors: %I64d largest: %d\n"
  799. " Indirect Transfers: %I64d errors: %I64d largest: %d\n",
  800. fdoContext->FrontEndPath,
  801. fdoContext->totalDirectTransfers,
  802. fdoContext->totalDirectErrors,
  803. fdoContext->largestDirectTransfer,
  804. fdoContext->totalIndirectTransfers,
  805. fdoContext->totalIndirectErrors,
  806. fdoContext->largestIndirectTransfer);
  807. //
  808. // --XT-- Removed tracing of 2 interrupt related values.
  809. //
  810. TraceEvents(TRACE_LEVEL_WARNING, TRACE_DEVICE,
  811. __FUNCTION__": %s\n"
  812. " DPC overlap %I64d DPC requeue %I64d\n"
  813. " DPC max passes %d DPC max processed %d DPC drain queue requests %d\n",
  814. fdoContext->FrontEndPath,
  815. fdoContext->totalDpcOverLapCount,
  816. fdoContext->totalDpcReQueueCount,
  817. fdoContext->maxDpcPasses,
  818. fdoContext->maxRequestsProcessed,
  819. fdoContext->maxRequeuedRequestsProcessed);
  820. // @todo anything else that needs undoing?
  821. return STATUS_SUCCESS;
  822. }
  823. /**
  824. * @brief Notification that the device has been unplugged.
  825. * This callback is invoked before FdoEvtDeviceD0Exit and FdoEvtDeviceReleaseHardware.
  826. *
  827. * @param[in] Device handle to the WDFDEVICE created by FdoEvtDeviceAdd().
  828. *
  829. */
  830. VOID FdoEvtDeviceSurpriseRemoval(
  831. IN WDFDEVICE Device)
  832. {
  833. PUSB_FDO_CONTEXT fdoContext = DeviceGetFdoContext(Device);
  834. TraceEvents(TRACE_LEVEL_WARNING, TRACE_DEVICE,
  835. __FUNCTION__": %s Device %p\n",
  836. fdoContext->FrontEndPath,
  837. fdoContext->WdfDevice);
  838. CleanupDisconnectedDevice(fdoContext);
  839. }
  840. //
  841. // Dynamic Bus Support - currently not needed except that
  842. // static verifier is convinced we need it.
  843. // Theoretically this would be a root hub PDO.
  844. //
  845. NTSTATUS FdoEvtChildListCreateDevice(
  846. _In_ WDFCHILDLIST ChildList,
  847. _In_ PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription,
  848. _In_ PWDFDEVICE_INIT ChildInit)
  849. {
  850. PUSB_FDO_CONTEXT fdoContext = DeviceGetFdoContext(WdfChildListGetDevice(ChildList));
  851. TraceEvents(TRACE_LEVEL_WARNING, TRACE_DEVICE,
  852. __FUNCTION__": %s\n",
  853. fdoContext->FrontEndPath);
  854. return CreateRootHubPdoWithDeviceInit(
  855. fdoContext,
  856. IdentificationDescription,
  857. ChildInit);
  858. }
  859. //
  860. // Backend IO processing (isr and dpc). XXX move to own file.
  861. //
  862. //
  863. // --XT-- Removed FdoEvtDeviceInterruptEnable
  864. //
  865. //
  866. // --XT-- Removed FdoEvtDeviceInterruptDisable
  867. //
  868. /**
  869. * @brief DPC callback.
  870. * Calls the Xen interface api to process the ringbuffer until the ringbuffer is empty,
  871. * then completes all requests provided by the Xen api.
  872. *
  873. * @todo consider completion within the XenDpc() loop. However this would release the device
  874. * lock.
  875. *
  876. * @param[in] Dpc The WDFDPC handle.
  877. *
  878. */
  879. VOID
  880. FdoEvtDeviceDpcFunc(
  881. IN VOID *Context)
  882. {
  883. // --XT-- FDO context passed directly now.
  884. PUSB_FDO_CONTEXT fdoContext = (PUSB_FDO_CONTEXT)Context;
  885. //
  886. // this stuff needs to be done at DPC level in order to complete irps.
  887. //
  888. ULONG passes = 0;
  889. AcquireFdoLock(fdoContext);
  890. if (fdoContext->InDpc)
  891. {
  892. fdoContext->DpcOverLapCount++;
  893. ReleaseFdoLock(fdoContext);
  894. return;
  895. }
  896. fdoContext->InDpc = TRUE;
  897. BOOLEAN moreWork;
  898. do
  899. {
  900. moreWork = XenDpc(fdoContext, fdoContext->RequestCollection);
  901. passes++;
  902. if (fdoContext->DpcOverLapCount)
  903. {
  904. fdoContext->totalDpcOverLapCount += fdoContext->DpcOverLapCount;
  905. fdoContext->DpcOverLapCount = 0;
  906. moreWork = TRUE;
  907. }
  908. if (moreWork & (passes >= KeQueryActiveProcessorCount(NULL)))
  909. {
  910. //
  911. // reschedule the dpc to prevent starvation.
  912. //
  913. // --XT-- WdfDpcEnqueue(fdoContext->WdfDpc);
  914. //
  915. // --XT-- Schedule through the Xen interface now.
  916. //
  917. XenScheduleDPC(fdoContext->Xen);
  918. TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DPC,
  919. __FUNCTION__": enqueue dpc at %d passes\n",
  920. passes);
  921. fdoContext->totalDpcReQueueCount++;
  922. break;
  923. }
  924. } while (moreWork);
  925. fdoContext->InDpc = FALSE; // allow another dpc instance to run and add to the collection.
  926. if (passes > fdoContext->maxDpcPasses)
  927. {
  928. fdoContext->maxDpcPasses = passes;
  929. }
  930. //
  931. // complete all queued Irps. Note that this section is re-entrant.
  932. // Note also that all of these requests have been "uncanceled" and ahve
  933. // been marked as completed in their request contexts and that the
  934. // additional reference on the request object has been removed.
  935. // The collection can be safely completed with no race conditions.
  936. //
  937. ULONG responseCount = 0;
  938. WDFREQUEST Request;
  939. while ((Request = (WDFREQUEST) WdfCollectionGetFirstItem(fdoContext->RequestCollection)) != NULL)
  940. {
  941. WdfCollectionRemoveItem(fdoContext->RequestCollection, 0);
  942. TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DPC,
  943. __FUNCTION__": complete Request %p Status %x\n",
  944. Request,
  945. WdfRequestWdmGetIrp(Request)->IoStatus.Status);
  946. ReleaseFdoLock(fdoContext);
  947. WdfRequestCompleteWithPriorityBoost(Request,
  948. WdfRequestWdmGetIrp(Request)->IoStatus.Status,
  949. IO_SOUND_INCREMENT);
  950. AcquireFdoLock(fdoContext);
  951. responseCount++;
  952. }
  953. if (responseCount > fdoContext->maxRequestsProcessed)
  954. {
  955. fdoContext->maxRequestsProcessed = responseCount;
  956. }
  957. //
  958. // fire up any queued requests
  959. //
  960. DrainRequestQueue(fdoContext, FALSE);
  961. ReleaseFdoLock(fdoContext);
  962. ULONG level = responseCount ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_VERBOSE;
  963. TraceEvents(level, TRACE_DPC,
  964. __FUNCTION__": exit responses processed %d passes %d\n",
  965. responseCount,
  966. passes);
  967. }
  968. //
  969. // --XT-- Removed FdoEvtDeviceIsrFunc
  970. //
  971. /**
  972. * @brief Watchdog timer
  973. * checks the operational state of the xen interface and initiates surprise removal if
  974. * the interface is not operational and the device is not unplugged.
  975. *
  976. * @param[in] Timer handle to timer allocated by FdoDeviceAdd()
  977. *
  978. */
  979. VOID
  980. FdoEvtTimerFunc(
  981. IN WDFTIMER Timer)
  982. {
  983. PUSB_FDO_CONTEXT fdoContext = DeviceGetFdoContext(WdfTimerGetParentObject(Timer));
  984. // XenCheckOperationalState waits on an event. Must be called at < DISPATCH_LEVEL.
  985. BOOLEAN operational = XenCheckOperationalState(fdoContext->Xen);
  986. AcquireFdoLock(fdoContext);
  987. if (!fdoContext->DeviceUnplugged)
  988. {
  989. if (operational)
  990. {
  991. // restart the timer.
  992. WdfTimerStart(Timer, WDF_REL_TIMEOUT_IN_SEC(1));
  993. }
  994. else
  995. {
  996. TraceEvents(TRACE_LEVEL_WARNING, TRACE_DEVICE,
  997. __FUNCTION__": %s Device %p unplug detected by watchdog\n",
  998. fdoContext->FrontEndPath,
  999. fdoContext->WdfDevice);
  1000. FdoUnplugDevice(fdoContext);
  1001. ReleaseFdoLock(fdoContext);
  1002. return;
  1003. }
  1004. }
  1005. ReleaseFdoLock(fdoContext);
  1006. //
  1007. // @todo run the dpc - if this fixes anything fix the bug!
  1008. //
  1009. if (!fdoContext->DeviceUnplugged)
  1010. {
  1011. //
  1012. // --XT-- Now passing the FDO context directly.
  1013. //
  1014. FdoEvtDeviceDpcFunc(fdoContext);
  1015. }
  1016. }
  1017. /**
  1018. * @brief handles CreateFile operations for the usb controller.
  1019. * This function basically exists only to log that a create occurred.
  1020. *
  1021. * @param[in] Device A handle to a framework device object.
  1022. * @param[in] Request A handle to a framework request object that represents a file creation request
  1023. * @param[in] FileObject A handle to a framework file object that describes a file that is being opened for the specified request.
  1024. * This parameter is NULL if the driver has specified WdfFileObjectNotRequired for the FileObjectClass member of the WDF_FILEOBJECT_CONFIG structure.
  1025. */
  1026. VOID
  1027. FdoEvtDeviceFileCreate (
  1028. IN WDFDEVICE Device,
  1029. IN WDFREQUEST Request,
  1030. IN WDFFILEOBJECT FileObject)
  1031. {
  1032. PUSB_FDO_CONTEXT fdoContext = DeviceGetFdoContext(Device);
  1033. UNREFERENCED_PARAMETER(FileObject);
  1034. TraceEvents(TRACE_LEVEL_WARNING, TRACE_DEVICE,
  1035. __FUNCTION__": %s Device %p\n",
  1036. fdoContext->FrontEndPath,
  1037. fdoContext->WdfDevice);
  1038. WdfRequestComplete(Request, STATUS_SUCCESS);
  1039. }
  1040. /**
  1041. * @brief handles operations that must be performed when all of an application's
  1042. * accesses to a device have been closed.
  1043. *
  1044. * @param[in] FileObject The handle to the FileObject.
  1045. */
  1046. VOID
  1047. FdoEvtFileClose (
  1048. IN WDFFILEOBJECT FileObject)
  1049. {
  1050. PUSB_FDO_CONTEXT fdoContext = DeviceGetFdoContext(WdfFileObjectGetDevice(FileObject));
  1051. TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE,
  1052. __FUNCTION__": %s Device %p\n",
  1053. fdoContext->FrontEndPath,
  1054. fdoContext->WdfDevice);
  1055. }
  1056. //
  1057. // Workitem support.
  1058. // Maintain a collection of workitems, 2 to start with, and
  1059. // hand them out on demand. If the collection is empty, allocate
  1060. // a new work item.
  1061. //
  1062. /**
  1063. * @brief generic workitem callback function
  1064. * calls the specific callback function in the work item context.
  1065. *
  1066. * @param[in] WorkItem handle to the workitem object.
  1067. *
  1068. */
  1069. VOID
  1070. EvtFdoDeviceGenericWorkItem (
  1071. IN WDFWORKITEM WorkItem)
  1072. {
  1073. PUSB_FDO_WORK_ITEM_CONTEXT context = WorkItemGetContext(WorkItem);
  1074. //
  1075. // NULL indicates somebody screwed up, log it and forget it.
  1076. //
  1077. if (context->CallBack)
  1078. {
  1079. context->CallBack(WorkItem);
  1080. }
  1081. else
  1082. {
  1083. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  1084. __FUNCTION__": %s Device %p WorkItem %p NULL callback\n",
  1085. context->FdoContext->FrontEndPath,
  1086. context->FdoContext->WdfDevice,
  1087. WorkItem);
  1088. }
  1089. context->CallBack = NULL;
  1090. //
  1091. // put this workitem into our collection.
  1092. //
  1093. AcquireFdoLock(context->FdoContext);
  1094. NTSTATUS Status = WdfCollectionAdd(context->FdoContext->FreeWorkItems, WorkItem);
  1095. ReleaseFdoLock(context->FdoContext);
  1096. if (!NT_SUCCESS(Status))
  1097. {
  1098. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  1099. __FUNCTION__": %s Device %p WdfCollectionAdd error %x deleting workitem %p\n",
  1100. context->FdoContext->FrontEndPath,
  1101. context->FdoContext->WdfDevice,
  1102. Status,
  1103. WorkItem);
  1104. // oh well delete it
  1105. WdfObjectDelete(WorkItem);
  1106. }
  1107. }
  1108. /**
  1109. * @brief Allocate a workitem from or for our collection of workitems.
  1110. * Must be called with the device lock held.
  1111. *
  1112. * @param[in] Device handle to the WDFDEVICE created by FdoEvtDeviceAdd.
  1113. * @param[in] callback only optional if this is add device doing an allocation!
  1114. * @param[in] Param0 arbitrary context data
  1115. * @param[in] Param1 arbitrary context data
  1116. * @param[in] Param2 arbitrary context data
  1117. * @param[in] Param3 arbitrary context data
  1118. *
  1119. * @returns a WDFWORKITEM handle or NULL on failure.
  1120. *
  1121. */
  1122. WDFWORKITEM
  1123. NewWorkItem(
  1124. IN PUSB_FDO_CONTEXT fdoContext,
  1125. IN PFN_WDF_WORKITEM callback,
  1126. IN ULONG_PTR Param0,
  1127. IN ULONG_PTR Param1,
  1128. IN ULONG_PTR Param2,
  1129. IN ULONG_PTR Param3)
  1130. {
  1131. //
  1132. // First try to get a workitem from our collection of them.
  1133. //
  1134. WDFWORKITEM WorkItem = (WDFWORKITEM) WdfCollectionGetFirstItem(fdoContext->FreeWorkItems);
  1135. if (WorkItem == NULL)
  1136. {
  1137. //
  1138. // ok - allocate a new one.
  1139. //
  1140. TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE,
  1141. __FUNCTION__": Device %p FreeWorkItems is empty, init count %d\n",
  1142. fdoContext->WdfDevice,
  1143. INIT_WORK_ITEM_COUNT);
  1144. NTSTATUS status = STATUS_SUCCESS;
  1145. WDF_OBJECT_ATTRIBUTES attributes;
  1146. WDF_WORKITEM_CONFIG workitemConfig;
  1147. WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
  1148. WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(
  1149. &attributes,
  1150. USB_FDO_WORK_ITEM_CONTEXT);
  1151. attributes.ParentObject = fdoContext->WdfDevice;
  1152. WDF_WORKITEM_CONFIG_INIT(
  1153. &workitemConfig,
  1154. EvtFdoDeviceGenericWorkItem);
  1155. status = WdfWorkItemCreate(
  1156. &workitemConfig,
  1157. &attributes,
  1158. &WorkItem);
  1159. if (!NT_SUCCESS(status))
  1160. {
  1161. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  1162. __FUNCTION__": WdfWorkItemCreate error %x\n",
  1163. status);
  1164. return NULL;
  1165. }
  1166. }
  1167. else
  1168. {
  1169. // note that WdfCollectionGetFirstItem doesn't remove it from the collection.
  1170. WdfCollectionRemove(fdoContext->FreeWorkItems, WorkItem);
  1171. }
  1172. if (WorkItem)
  1173. {
  1174. // initialize it.
  1175. PUSB_FDO_WORK_ITEM_CONTEXT context = WorkItemGetContext(WorkItem);
  1176. context->FdoContext = fdoContext;
  1177. context->CallBack = callback;
  1178. context->Params[0] = Param0;
  1179. context->Params[1] = Param1;
  1180. context->Params[2] = Param2;
  1181. context->Params[3] = Param3;
  1182. }
  1183. return WorkItem;
  1184. }
  1185. /**
  1186. * @brief deallocate callback item.
  1187. * Must be called with lock held.
  1188. * *Note* this is called only to deal with error cases. The normal
  1189. * callback function re-adds the work item to the collection.
  1190. *
  1191. * @param[in] WorkItem allocated WorkItem to be freed.
  1192. *
  1193. */
  1194. VOID
  1195. FreeWorkItem(
  1196. IN WDFWORKITEM WorkItem)
  1197. {
  1198. PUSB_FDO_WORK_ITEM_CONTEXT context = WorkItemGetContext(WorkItem);
  1199. NTSTATUS Status = WdfCollectionAdd(context->FdoContext->FreeWorkItems,
  1200. WorkItem);
  1201. if (!NT_SUCCESS(Status))
  1202. {
  1203. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  1204. __FUNCTION__": Device %p WdfCollectionAdd error %x, deleting instead.\n",
  1205. context->FdoContext->WdfDevice,
  1206. Status);
  1207. WdfObjectDelete(WorkItem);
  1208. }
  1209. }
  1210. //
  1211. // file local helper functions.
  1212. //
  1213. NTSTATUS
  1214. InitScratchpad(
  1215. IN PUSB_FDO_CONTEXT fdoContext)
  1216. {
  1217. NTSTATUS status;
  1218. KeInitializeEvent(&fdoContext->ScratchPad.CompletionEvent, NotificationEvent, FALSE);
  1219. fdoContext->ScratchPad.Buffer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, XVU1);
  1220. if (!fdoContext->ScratchPad.Buffer)
  1221. {
  1222. status = STATUS_NO_MEMORY;
  1223. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  1224. __FUNCTION__": Device %p ExAllocatePoolWithTag failed\n",
  1225. fdoContext->WdfDevice);
  1226. return status;
  1227. }
  1228. RtlZeroMemory(fdoContext->ScratchPad.Buffer, PAGE_SIZE);
  1229. fdoContext->ScratchPad.Mdl = IoAllocateMdl(fdoContext->ScratchPad.Buffer,
  1230. PAGE_SIZE,
  1231. FALSE,
  1232. FALSE,
  1233. NULL);
  1234. if (!fdoContext->ScratchPad.Mdl)
  1235. {
  1236. status = STATUS_NO_MEMORY;
  1237. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  1238. __FUNCTION__": device %p IoAllocateMdl failed\n",
  1239. fdoContext->WdfDevice);
  1240. return status;
  1241. }
  1242. MmBuildMdlForNonPagedPool(fdoContext->ScratchPad.Mdl);
  1243. return STATUS_SUCCESS;
  1244. }
  1245. VOID
  1246. DeleteScratchpad(
  1247. IN PUSB_FDO_CONTEXT fdoContext)
  1248. {
  1249. if (fdoContext->ScratchPad.Buffer)
  1250. {
  1251. ExFreePool(fdoContext->ScratchPad.Buffer);
  1252. fdoContext->ScratchPad.Buffer = NULL;
  1253. }
  1254. if (fdoContext->ScratchPad.Mdl)
  1255. {
  1256. IoFreeMdl(fdoContext->ScratchPad.Mdl);
  1257. fdoContext->ScratchPad.Mdl = NULL;
  1258. }
  1259. }
  1260. PCHAR
  1261. DbgDevicePowerString(
  1262. IN WDF_POWER_DEVICE_STATE Type)
  1263. {
  1264. switch (Type)
  1265. {
  1266. case WdfPowerDeviceInvalid:
  1267. return "WdfPowerDeviceInvalid";
  1268. case WdfPowerDeviceD0:
  1269. return "WdfPowerDeviceD0";
  1270. case PowerDeviceD1:
  1271. return "WdfPowerDeviceD1";
  1272. case WdfPowerDeviceD2:
  1273. return "WdfPowerDeviceD2";
  1274. case WdfPowerDeviceD3:
  1275. return "WdfPowerDeviceD3";
  1276. case WdfPowerDeviceD3Final:
  1277. return "WdfPowerDeviceD3Final";
  1278. case WdfPowerDevicePrepareForHibernation:
  1279. return "WdfPowerDevicePrepareForHibernation";
  1280. case WdfPowerDeviceMaximum:
  1281. return "PowerDeviceMaximum";
  1282. default:
  1283. return "UnKnown Device Power State";
  1284. }
  1285. }