Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

484 lines
16 KiB

  1. /*
  2. *************************************************************************
  3. * File: PNP.C
  4. *
  5. * Module: USBCCGP.SYS
  6. * USB Common Class Generic Parent driver.
  7. *
  8. * Copyright (c) 1998 Microsoft Corporation
  9. *
  10. *
  11. * Author: ervinp
  12. *
  13. *************************************************************************
  14. */
  15. #include <wdm.h>
  16. #include <stdio.h>
  17. #include <usbdi.h>
  18. #include <usbdlib.h>
  19. #include <usbioctl.h>
  20. #include <wdmguid.h>
  21. #include "usbccgp.h"
  22. #include "debug.h"
  23. #ifdef ALLOC_PRAGMA
  24. #pragma alloc_text(PAGE, USBC_PnP)
  25. #pragma alloc_text(PAGE, GetDeviceText)
  26. #endif
  27. NTSTATUS USBC_PnP(PDEVEXT devExt, PIRP irp)
  28. {
  29. PIO_STACK_LOCATION irpSp;
  30. NTSTATUS status;
  31. UCHAR minorFunction;
  32. BOOLEAN isParentFdo;
  33. enum deviceState oldState;
  34. PAGED_CODE();
  35. irpSp = IoGetCurrentIrpStackLocation(irp);
  36. /*
  37. * Keep these privately so we still have it after the IRP completes
  38. * or after the device extension is freed on a REMOVE_DEVICE
  39. */
  40. minorFunction = irpSp->MinorFunction;
  41. isParentFdo = devExt->isParentFdo;
  42. DBG_LOG_PNP_IRP(irp, minorFunction, isParentFdo, FALSE, 0);
  43. if (isParentFdo){
  44. PPARENT_FDO_EXT parentFdoExt = &devExt->parentFdoExt;
  45. BOOLEAN irpAlreadyCompleted = FALSE;
  46. status = NO_STATUS;
  47. if (parentFdoExt->state == STATE_SUSPENDED ||
  48. parentFdoExt->pendingIdleIrp) {
  49. ParentSetD0(parentFdoExt);
  50. }
  51. switch (minorFunction){
  52. case IRP_MN_START_DEVICE:
  53. status = StartParentFdo(parentFdoExt, irp);
  54. break;
  55. case IRP_MN_QUERY_STOP_DEVICE:
  56. if (parentFdoExt->state == STATE_SUSPENDED){
  57. status = STATUS_DEVICE_POWER_FAILURE;
  58. }
  59. else {
  60. /*
  61. * We will pass this IRP down the driver stack.
  62. * However, we need to change the default status
  63. * from STATUS_NOT_SUPPORTED to STATUS_SUCCESS.
  64. */
  65. irp->IoStatus.Status = STATUS_SUCCESS;
  66. }
  67. break;
  68. case IRP_MN_STOP_DEVICE:
  69. if (parentFdoExt->state == STATE_SUSPENDED){
  70. DBGERR(("Got STOP while device suspended"));
  71. status = STATUS_DEVICE_POWER_FAILURE;
  72. }
  73. else {
  74. /*
  75. * Only set state to STOPPED if the device was
  76. * previously started successfully.
  77. */
  78. if (parentFdoExt->state == STATE_STARTED){
  79. parentFdoExt->state = STATE_STOPPING;
  80. ParentCloseConfiguration(parentFdoExt);
  81. }
  82. else {
  83. DBGWARN(("Got STOP while in state is %xh.", parentFdoExt->state));
  84. }
  85. }
  86. break;
  87. case IRP_MN_QUERY_REMOVE_DEVICE:
  88. /*
  89. * We will pass this IRP down the driver stack.
  90. * However, we need to change the default status
  91. * from STATUS_NOT_SUPPORTED to STATUS_SUCCESS.
  92. */
  93. irp->IoStatus.Status = STATUS_SUCCESS;
  94. break;
  95. case IRP_MN_REMOVE_DEVICE:
  96. /*
  97. * Cancel downward IO
  98. * Set default status to SUCCESS
  99. * Send the IRP down
  100. * Detach
  101. * Cleanup
  102. */
  103. PrepareParentFDOForRemove(parentFdoExt);
  104. irp->IoStatus.Status = STATUS_SUCCESS;
  105. IoSkipCurrentIrpStackLocation(irp);
  106. status = IoCallDriver(parentFdoExt->topDevObj, irp);
  107. irpAlreadyCompleted = TRUE;
  108. IoDetachDevice(parentFdoExt->topDevObj);
  109. FreeParentFDOResources(parentFdoExt);
  110. break;
  111. case IRP_MN_QUERY_DEVICE_RELATIONS:
  112. status = QueryParentDeviceRelations(parentFdoExt, irp);
  113. break;
  114. case IRP_MN_QUERY_CAPABILITIES:
  115. /*
  116. * Return the USB PDO's capabilities, but add the SurpriseRemovalOK bit.
  117. */
  118. ASSERT(irpSp->Parameters.DeviceCapabilities.Capabilities);
  119. IoCopyCurrentIrpStackLocationToNext(irp);
  120. status = CallDriverSync(parentFdoExt->topDevObj, irp);
  121. if (NT_SUCCESS(status)){
  122. irpSp->Parameters.DeviceCapabilities.Capabilities->SurpriseRemovalOK = TRUE;
  123. }
  124. break;
  125. }
  126. if (status == NO_STATUS){
  127. IoCopyCurrentIrpStackLocationToNext(irp);
  128. IoSetCompletionRoutine(irp, USBC_PnpComplete, (PVOID)devExt, TRUE, TRUE, TRUE);
  129. status = IoCallDriver(parentFdoExt->topDevObj, irp);
  130. }
  131. else if (irpAlreadyCompleted){
  132. /*
  133. * Don't touch the irp.
  134. */
  135. }
  136. else if (status != STATUS_PENDING){
  137. /*
  138. * Complete the IRP here
  139. */
  140. irp->IoStatus.Status = status;
  141. IoCompleteRequest(irp, IO_NO_INCREMENT);
  142. }
  143. }
  144. else {
  145. /*
  146. * This is a child function PDO.
  147. */
  148. PFUNCTION_PDO_EXT functionPdoExt = &devExt->functionPdoExt;
  149. switch (minorFunction){
  150. case IRP_MN_START_DEVICE:
  151. functionPdoExt->state = STATE_STARTED;
  152. status = STATUS_SUCCESS;
  153. break;
  154. case IRP_MN_QUERY_STOP_DEVICE:
  155. case IRP_MN_CANCEL_STOP_DEVICE:
  156. status = STATUS_SUCCESS;
  157. break;
  158. case IRP_MN_STOP_DEVICE:
  159. /*
  160. * Only set state to STOPPED if the device was
  161. * previously started successfully.
  162. */
  163. if (functionPdoExt->state == STATE_STARTED){
  164. functionPdoExt->state = STATE_STOPPED;
  165. }
  166. status = STATUS_SUCCESS;
  167. break;
  168. case IRP_MN_QUERY_REMOVE_DEVICE:
  169. case IRP_MN_CANCEL_REMOVE_DEVICE:
  170. status = STATUS_SUCCESS;
  171. break;
  172. case IRP_MN_REMOVE_DEVICE:
  173. /*
  174. * Since we are the bus driver for the function-PDOs, we cannot
  175. * delete the function pdo on a remove-device. We must wait
  176. * for the parent to get the remove before deleting the function pdo.
  177. */
  178. oldState = functionPdoExt->state;
  179. functionPdoExt->state = STATE_REMOVED;
  180. status = STATUS_SUCCESS;
  181. break;
  182. case IRP_MN_QUERY_ID:
  183. status = QueryFunctionPdoID(functionPdoExt, irp);
  184. break;
  185. case IRP_MN_QUERY_DEVICE_RELATIONS:
  186. status = QueryFunctionDeviceRelations(functionPdoExt, irp);
  187. break;
  188. case IRP_MN_QUERY_CAPABILITIES:
  189. status = QueryFunctionCapabilities(functionPdoExt, irp);
  190. break;
  191. case IRP_MN_QUERY_PNP_DEVICE_STATE:
  192. irp->IoStatus.Information = 0;
  193. switch (functionPdoExt->state){
  194. case STATE_START_FAILED:
  195. irp->IoStatus.Information |= PNP_DEVICE_FAILED;
  196. break;
  197. case STATE_STOPPED:
  198. irp->IoStatus.Information |= PNP_DEVICE_DISABLED;
  199. break;
  200. case STATE_REMOVING:
  201. case STATE_REMOVED:
  202. irp->IoStatus.Information |= PNP_DEVICE_REMOVED;
  203. break;
  204. }
  205. status = STATUS_SUCCESS;
  206. break;
  207. case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
  208. // Adrian says that once PnP sends this IRP, the PDO is valid for
  209. // PnP functions like IoGetDeviceProperty, etc.
  210. //
  211. // And since we know that the PDO is valid and the DevNode now exists,
  212. // this would also be a good time to handle the MS ExtPropDesc.
  213. //
  214. InstallExtPropDesc(functionPdoExt);
  215. status = STATUS_SUCCESS;
  216. break;
  217. case IRP_MN_QUERY_BUS_INFORMATION:
  218. {
  219. PPNP_BUS_INFORMATION busInfo = ALLOCPOOL(PagedPool, sizeof(PNP_BUS_INFORMATION));
  220. if (busInfo) {
  221. busInfo->BusTypeGuid = GUID_BUS_TYPE_USB;
  222. busInfo->LegacyBusType = PNPBus;
  223. busInfo->BusNumber = 0;
  224. irp->IoStatus.Information = (ULONG_PTR)busInfo;
  225. status = STATUS_SUCCESS;
  226. }
  227. else {
  228. status = STATUS_INSUFFICIENT_RESOURCES;
  229. }
  230. }
  231. break;
  232. case IRP_MN_QUERY_DEVICE_TEXT:
  233. status = GetDeviceText(functionPdoExt, irp);
  234. break;
  235. case IRP_MN_QUERY_INTERFACE:
  236. /*
  237. * Send this down to the parent.
  238. */
  239. IoCopyCurrentIrpStackLocationToNext(irp);
  240. status = CallDriverSync(functionPdoExt->parentFdoExt->fdo, irp);
  241. break;
  242. default:
  243. /*
  244. * Complete the IRP with the default status.
  245. */
  246. status = irp->IoStatus.Status;
  247. break;
  248. }
  249. /*
  250. * Complete the IRP here
  251. */
  252. irp->IoStatus.Status = status;
  253. IoCompleteRequest(irp, IO_NO_INCREMENT);
  254. }
  255. DBG_LOG_PNP_IRP(irp, minorFunction, isParentFdo, TRUE, status);
  256. return status;
  257. }
  258. NTSTATUS USBC_PnpComplete(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context)
  259. {
  260. PDEVEXT devExt = (PDEVEXT)context;
  261. PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp);
  262. NTSTATUS status = irp->IoStatus.Status;
  263. ASSERT(devExt->signature == USBCCGP_TAG);
  264. if (devExt->isParentFdo){
  265. PPARENT_FDO_EXT parentFdoExt = &devExt->parentFdoExt;
  266. switch (irpSp->MinorFunction){
  267. case IRP_MN_START_DEVICE:
  268. ASSERT(0);
  269. break;
  270. case IRP_MN_STOP_DEVICE:
  271. /*
  272. * Only set the state to STOPPED if the device
  273. * was previously successfully started;
  274. * otherwise, leave the state alone.
  275. */
  276. if (parentFdoExt->state == STATE_STOPPING){
  277. /*
  278. * Free the interface list's .Interface pointers,
  279. * which we have to re-allocate on a start-after-stop.
  280. */
  281. FreeInterfaceList(parentFdoExt, FALSE);
  282. parentFdoExt->state = STATE_STOPPED;
  283. }
  284. break;
  285. }
  286. }
  287. /*
  288. * Must propagate the pending bit if a lower driver returned pending.
  289. */
  290. if (irp->PendingReturned){
  291. IoMarkIrpPending(irp);
  292. }
  293. return STATUS_SUCCESS;
  294. }
  295. /*
  296. ********************************************************************************
  297. * GetDeviceText
  298. ********************************************************************************
  299. *
  300. * If the interface descriptor for this function has a non-zero iInterface
  301. * string, return that string. Otherwise, pass this request off to the
  302. * parent, which will return the iProduct string from the device descriptor.
  303. *
  304. */
  305. NTSTATUS GetDeviceText(PFUNCTION_PDO_EXT functionPdoExt, PIRP irp)
  306. {
  307. NTSTATUS status;
  308. UCHAR ifaceStringIndex;
  309. ULONG ulBytes = 0;
  310. PAGED_CODE();
  311. ifaceStringIndex = functionPdoExt->functionInterfaceList[0].InterfaceDescriptor->iInterface;
  312. if (ifaceStringIndex){
  313. PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp);
  314. PUSB_STRING_DESCRIPTOR ifaceStringDesc;
  315. PWCHAR deviceText;
  316. switch (irpSp->Parameters.QueryDeviceText.DeviceTextType){
  317. case DeviceTextDescription:
  318. ifaceStringDesc = ALLOCPOOL(NonPagedPool, MAXIMUM_USB_STRING_LENGTH);
  319. if (ifaceStringDesc){
  320. LANGID langId = (LANGID)(irpSp->Parameters.QueryDeviceText.LocaleId >> 16);
  321. if (langId == 0){
  322. /*
  323. * Force to English.
  324. */
  325. langId = 0x0409;
  326. }
  327. QDT_Retry:
  328. status = GetStringDescriptor( functionPdoExt->parentFdoExt,
  329. ifaceStringIndex,
  330. langId,
  331. ifaceStringDesc,
  332. MAXIMUM_USB_STRING_LENGTH);
  333. if (NT_SUCCESS(status) &&
  334. ifaceStringDesc->bLength == 0) {
  335. status = STATUS_UNSUCCESSFUL;
  336. }
  337. if (NT_SUCCESS(status)){
  338. ULONG numWchars = (ifaceStringDesc->bLength - 2*sizeof(UCHAR))/sizeof(WCHAR);
  339. deviceText = ALLOCPOOL(PagedPool, (numWchars+1)*sizeof(WCHAR));
  340. if (deviceText){
  341. RtlZeroMemory(deviceText, (numWchars+1)*sizeof(WCHAR));
  342. RtlCopyMemory(deviceText, ifaceStringDesc->bString, numWchars*sizeof(WCHAR));
  343. irp->IoStatus.Information = (ULONG_PTR)deviceText;
  344. }
  345. else {
  346. status = STATUS_INSUFFICIENT_RESOURCES;
  347. }
  348. } else if (langId != 0x409) {
  349. // We are running a non-English flavor of the OS, but the
  350. // attached USB device does not contain device text in
  351. // the requested language. Let's try again for English.
  352. langId = 0x0409;
  353. goto QDT_Retry;
  354. }
  355. FREEPOOL(ifaceStringDesc);
  356. }
  357. else {
  358. status = STATUS_INSUFFICIENT_RESOURCES;
  359. }
  360. if (!NT_SUCCESS(status) && GenericCompositeUSBDeviceString) {
  361. // Return generic English string if one could not be
  362. // obtained from the device.
  363. STRLEN(ulBytes, GenericCompositeUSBDeviceString);
  364. ulBytes += sizeof(UNICODE_NULL);
  365. deviceText = ALLOCPOOL(PagedPool, ulBytes);
  366. if (deviceText) {
  367. RtlZeroMemory(deviceText, ulBytes);
  368. RtlCopyMemory(deviceText,
  369. GenericCompositeUSBDeviceString,
  370. ulBytes);
  371. irp->IoStatus.Information = (ULONG_PTR) deviceText;
  372. status = STATUS_SUCCESS;
  373. } else {
  374. status = STATUS_INSUFFICIENT_RESOURCES;
  375. }
  376. }
  377. break;
  378. case DeviceTextLocationInformation:
  379. /*
  380. * BUGBUG
  381. * Supporting this call to return phys port # is optional.
  382. *
  383. * We may be able to service it with
  384. * a call to IOCTL_INTERNAL_USB_GET_PARENT_HUB_INFO
  385. * (Pass a PULONG in Argument2, this will be filled in with the port #.).
  386. */
  387. status = irp->IoStatus.Status;
  388. break;
  389. default:
  390. DBGWARN(("GetDeviceText: unhandled DeviceTextType %xh.", (ULONG)irpSp->Parameters.QueryDeviceText.DeviceTextType));
  391. status = irp->IoStatus.Status;
  392. break;
  393. }
  394. }
  395. else {
  396. IoCopyCurrentIrpStackLocationToNext(irp);
  397. status = CallNextDriverSync(functionPdoExt->parentFdoExt, irp);
  398. }
  399. return status;
  400. }