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.

699 lines
22 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. usb.c
  5. Abstract: ESC/POS (serial) interface for USB Point-of-Sale devices
  6. Author:
  7. ervinp
  8. Environment:
  9. Kernel mode
  10. Revision History:
  11. --*/
  12. #include <WDM.H>
  13. #include <usbdi.h>
  14. #include <usbdlib.h>
  15. #include <usbioctl.h>
  16. #include "escpos.h"
  17. #include "debug.h"
  18. NTSTATUS InitUSB(PARENTFDOEXT *parentFdoExt)
  19. /*++
  20. Routine Description:
  21. Intialize USB-related data
  22. Arguments:
  23. parentFdoExt - device extension for targetted device object
  24. Return Value:
  25. NT status code
  26. --*/
  27. {
  28. NTSTATUS status;
  29. status = GetDeviceDescriptor(parentFdoExt);
  30. if (NT_SUCCESS(status)){
  31. status = GetConfigDescriptor(parentFdoExt);
  32. if (NT_SUCCESS(status)){
  33. status = SelectConfiguration(parentFdoExt);
  34. }
  35. }
  36. return status;
  37. }
  38. NTSTATUS GetConfigDescriptor(PARENTFDOEXT *parentFdoExt)
  39. /*++
  40. Routine Description:
  41. Function retrieves the configuration descriptor from the device
  42. Arguments:
  43. parentFdoExt - device extension for targetted device object
  44. Return Value:
  45. NT status code
  46. --*/
  47. {
  48. URB urb = { 0 };
  49. USB_CONFIGURATION_DESCRIPTOR configDescBase = { 0 };
  50. NTSTATUS status;
  51. PAGED_CODE();
  52. /*
  53. * Get the first part of the configuration descriptor.
  54. * It will tell us the size of the full configuration descriptor,
  55. * including all the following interface descriptors, etc.
  56. */
  57. UsbBuildGetDescriptorRequest(&urb,
  58. (USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
  59. USB_CONFIGURATION_DESCRIPTOR_TYPE,
  60. 0,
  61. 0,
  62. (PVOID)&configDescBase,
  63. NULL,
  64. sizeof(USB_CONFIGURATION_DESCRIPTOR),
  65. NULL);
  66. status = SubmitUrb(parentFdoExt->topDevObj, &urb, TRUE, NULL, NULL);
  67. if (NT_SUCCESS(status)) {
  68. ULONG configDescLen = configDescBase.wTotalLength;
  69. /*
  70. * Now allocate the right-sized buffer for the full configuration descriptor.
  71. */
  72. ASSERT(configDescLen < 0x1000);
  73. parentFdoExt->configDesc = ALLOCPOOL(NonPagedPool, configDescLen);
  74. if (parentFdoExt->configDesc) {
  75. RtlZeroMemory(parentFdoExt->configDesc, configDescLen);
  76. UsbBuildGetDescriptorRequest(&urb,
  77. (USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
  78. USB_CONFIGURATION_DESCRIPTOR_TYPE,
  79. 0,
  80. 0,
  81. parentFdoExt->configDesc,
  82. NULL,
  83. configDescLen,
  84. NULL);
  85. status = SubmitUrb(parentFdoExt->topDevObj, &urb, TRUE, NULL, NULL);
  86. if (NT_SUCCESS(status)) {
  87. DBGVERBOSE(("Got config desc @ %ph, len=%xh.", parentFdoExt->configDesc, urb.UrbControlDescriptorRequest.TransferBufferLength));
  88. ASSERT(urb.UrbControlDescriptorRequest.TransferBufferLength == configDescLen);
  89. }
  90. }
  91. else {
  92. status = STATUS_INSUFFICIENT_RESOURCES;
  93. }
  94. }
  95. ASSERT(NT_SUCCESS(status));
  96. return status;
  97. }
  98. NTSTATUS GetDeviceDescriptor(PARENTFDOEXT *parentFdoExt)
  99. /*++
  100. Routine Description:
  101. Function retrieves the device descriptor from the device
  102. Arguments:
  103. parentFdoExt - device extension for targetted device object
  104. Return Value:
  105. NT status code
  106. --*/
  107. {
  108. URB urb;
  109. NTSTATUS status;
  110. PAGED_CODE();
  111. RtlZeroMemory(&parentFdoExt->deviceDesc, sizeof(parentFdoExt->deviceDesc));
  112. UsbBuildGetDescriptorRequest(&urb,
  113. (USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
  114. USB_DEVICE_DESCRIPTOR_TYPE,
  115. 0,
  116. 0,
  117. (PVOID)&parentFdoExt->deviceDesc,
  118. NULL,
  119. sizeof(parentFdoExt->deviceDesc),
  120. NULL);
  121. status = SubmitUrb(parentFdoExt->topDevObj, &urb, TRUE, NULL, NULL);
  122. if (NT_SUCCESS(status)){
  123. DBGVERBOSE(("Got device desc @ %ph, len=%xh (should be %xh).", (PVOID)&parentFdoExt->deviceDesc, urb.UrbControlDescriptorRequest.TransferBufferLength, sizeof(parentFdoExt->deviceDesc)));
  124. }
  125. ASSERT(NT_SUCCESS(status));
  126. return status;
  127. }
  128. NTSTATUS ReadPipe( PARENTFDOEXT *parentFdoExt,
  129. USBD_PIPE_HANDLE pipeHandle,
  130. READPACKET *readPacket,
  131. BOOLEAN synchronous
  132. )
  133. {
  134. PURB urb;
  135. NTSTATUS status;
  136. ASSERT(pipeHandle);
  137. DBGVERBOSE(("ReadPipe: dataLen=%xh, sync=%xh", readPacket->length, (ULONG)synchronous));
  138. urb = ALLOCPOOL(NonPagedPool, sizeof(URB));
  139. if (urb){
  140. RtlZeroMemory(urb, sizeof(URB));
  141. urb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
  142. urb->UrbBulkOrInterruptTransfer.Hdr.Length = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
  143. urb->UrbBulkOrInterruptTransfer.PipeHandle = pipeHandle;
  144. urb->UrbBulkOrInterruptTransfer.TransferBufferLength = readPacket->length;
  145. urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;
  146. urb->UrbBulkOrInterruptTransfer.TransferBuffer = readPacket->data;
  147. urb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_SHORT_TRANSFER_OK | USBD_TRANSFER_DIRECTION_IN;
  148. urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
  149. if (synchronous){
  150. /*
  151. * Synchronous read.
  152. */
  153. status = SubmitUrb(parentFdoExt->topDevObj, urb, TRUE, NULL, 0);
  154. readPacket->length = urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
  155. FREEPOOL(urb);
  156. }
  157. else {
  158. /*
  159. * Asynchronous read.
  160. * Completion routine will free URB.
  161. */
  162. IncrementPendingActionCount(parentFdoExt);
  163. readPacket->urb = urb;
  164. status = SubmitUrb( parentFdoExt->topDevObj,
  165. urb,
  166. FALSE, // asynchronous
  167. ReadPipeCompletion, // completion routine
  168. readPacket // completion context
  169. );
  170. }
  171. }
  172. else {
  173. status = STATUS_INSUFFICIENT_RESOURCES;
  174. }
  175. return status;
  176. }
  177. NTSTATUS ReadPipeCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context)
  178. {
  179. READPACKET *readPacket = (READPACKET *)context;
  180. NTSTATUS status = irp->IoStatus.Status;
  181. POSPDOEXT *pdoExt;
  182. KIRQL oldIrql;
  183. ASSERT(readPacket->signature == READPACKET_SIG);
  184. pdoExt = readPacket->context;
  185. /*
  186. * Set the readPacket's length to the actual length returned by the device.
  187. */
  188. ASSERT(readPacket->urb->UrbBulkOrInterruptTransfer.TransferBufferLength <= readPacket->length);
  189. readPacket->length = readPacket->urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
  190. DBGVERBOSE(("ReadPipeCompletion: irp=%ph, status=%xh, data=%ph, len=%xh, context=%ph.", irp, status, readPacket->data, readPacket->length, readPacket->context));
  191. FREEPOOL(readPacket->urb);
  192. readPacket->urb = BAD_POINTER;
  193. if (NT_SUCCESS(status)){
  194. DBGSHOWBYTES("READ PIPE result", readPacket->data, readPacket->length);
  195. if (pdoExt){
  196. KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
  197. ASSERT(pdoExt->inputEndpointInfo.endpointIsBusy);
  198. pdoExt->inputEndpointInfo.endpointIsBusy = FALSE;
  199. /*
  200. * Queue this completed readPacket
  201. */
  202. ASSERT(readPacket->offset == 0);
  203. /*
  204. * Do NOT queue empty readPackets.
  205. */
  206. if(readPacket->length == 0)
  207. FreeReadPacket(readPacket);
  208. else {
  209. InsertTailList(&pdoExt->completedReadPacketsList, &readPacket->listEntry);
  210. pdoExt->totalQueuedReadDataLength += readPacket->length;
  211. }
  212. KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
  213. /*
  214. * Now try to satisfy pending read IRPs with the completed readPacket data.
  215. */
  216. SatisfyPendingReads(pdoExt);
  217. }
  218. else {
  219. DBGVERBOSE(("Just debug testing -- not processing read result"));
  220. FreeReadPacket(readPacket);
  221. }
  222. }
  223. else {
  224. FreeReadPacket(readPacket);
  225. }
  226. /*
  227. * If there are more read IRPs pending, issue another read.
  228. */
  229. if (pdoExt){
  230. BOOLEAN scheduleAnotherRead;
  231. KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
  232. scheduleAnotherRead = !IsListEmpty(&pdoExt->pendingReadIrpsList);
  233. KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
  234. if (scheduleAnotherRead){
  235. DBGVERBOSE(("ReadPipeCompletion: scheduling read workItem"));
  236. ExQueueWorkItem(&pdoExt->readWorkItem, DelayedWorkQueue);
  237. }
  238. }
  239. /*
  240. * This IRP was allocated by SubmitUrb(). Free it here.
  241. * Return STATUS_MORE_PROCESSING_REQUIRED so the kernel does not
  242. * continue processing this IRP.
  243. */
  244. IoFreeIrp(irp);
  245. DecrementPendingActionCount(pdoExt->parentFdoExt);
  246. return STATUS_MORE_PROCESSING_REQUIRED;
  247. }
  248. NTSTATUS WritePipe(PARENTFDOEXT *parentFdoExt, USBD_PIPE_HANDLE pipeHandle, PUCHAR data, ULONG dataLen)
  249. {
  250. URB urb;
  251. NTSTATUS status;
  252. ASSERT(pipeHandle);
  253. DBGSHOWBYTES("WritePipe bytes:", data, dataLen);
  254. urb.UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
  255. urb.UrbBulkOrInterruptTransfer.Hdr.Length = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);
  256. urb.UrbBulkOrInterruptTransfer.PipeHandle = pipeHandle;
  257. urb.UrbBulkOrInterruptTransfer.TransferBufferLength = dataLen;
  258. urb.UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;
  259. urb.UrbBulkOrInterruptTransfer.TransferBuffer = data;
  260. urb.UrbBulkOrInterruptTransfer.TransferFlags = USBD_SHORT_TRANSFER_OK | USBD_TRANSFER_DIRECTION_OUT;
  261. urb.UrbBulkOrInterruptTransfer.UrbLink = NULL;
  262. status = SubmitUrb(parentFdoExt->topDevObj, &urb, TRUE, NULL, NULL);
  263. return status;
  264. }
  265. NTSTATUS SubmitUrb( PDEVICE_OBJECT pdo,
  266. PURB urb,
  267. BOOLEAN synchronous,
  268. PVOID completionRoutine,
  269. PVOID completionContext)
  270. /*++
  271. Routine Description:
  272. Send the URB to the USB device.
  273. If synchronous is TRUE, ignore the completion info and synchonize the IRP;
  274. otherwise, don't synchronize and set the provided completion routine for the IRP.
  275. Arguments:
  276. Return Value:
  277. NT status code
  278. --*/
  279. {
  280. PIRP irp;
  281. NTSTATUS status;
  282. /*
  283. * Allocate the IRP to send the buffer down the USB stack.
  284. *
  285. * Don't use IoBuildDeviceIoControlRequest (because it queues
  286. * the IRP on the current thread's irp list and may
  287. * cause the calling process to hang if the IopCompleteRequest APC
  288. * does not fire and dequeue the IRP).
  289. */
  290. irp = IoAllocateIrp(pdo->StackSize, FALSE);
  291. if (irp){
  292. PIO_STACK_LOCATION nextSp;
  293. #if STATUS_ENDPOINT
  294. DBGVERBOSE(("SubmitUrb: submitting URB %ph on IRP %ph (sync=%d)", urb, irp, synchronous));
  295. #endif
  296. nextSp = IoGetNextIrpStackLocation(irp);
  297. nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  298. nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
  299. /*
  300. * Attach the URB to this IRP.
  301. */
  302. nextSp->Parameters.Others.Argument1 = urb;
  303. if (synchronous){
  304. status = CallDriverSync(pdo, irp);
  305. IoFreeIrp(irp);
  306. }
  307. else {
  308. /*
  309. * Caller's completion routine will free the irp
  310. * when it completes.
  311. */
  312. ASSERT(completionRoutine);
  313. ASSERT(completionContext);
  314. IoSetCompletionRoutine( irp,
  315. completionRoutine,
  316. completionContext,
  317. TRUE, TRUE, TRUE);
  318. status = IoCallDriver(pdo, irp);
  319. }
  320. }
  321. else {
  322. status = STATUS_INSUFFICIENT_RESOURCES;
  323. }
  324. return status;
  325. }
  326. NTSTATUS SelectConfiguration(PARENTFDOEXT *parentFdoExt)
  327. {
  328. USBD_INTERFACE_LIST_ENTRY interfaceList[2];
  329. NTSTATUS status;
  330. /*
  331. * We only look at vendor-class interfaces
  332. */
  333. // BUGBUG - limited to one interface
  334. interfaceList[0].InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
  335. parentFdoExt->configDesc,
  336. parentFdoExt->configDesc,
  337. -1,
  338. -1,
  339. USB_INTERFACE_CLASS_VENDOR,
  340. -1,
  341. -1);
  342. if (interfaceList[0].InterfaceDescriptor){
  343. PURB urb;
  344. interfaceList[1].InterfaceDescriptor = NULL;
  345. urb = USBD_CreateConfigurationRequestEx(parentFdoExt->configDesc, &interfaceList[0]);
  346. if (urb){
  347. status = SubmitUrb(parentFdoExt->topDevObj, urb, TRUE, NULL, NULL);
  348. if (NT_SUCCESS(status)){
  349. PUSBD_INTERFACE_INFORMATION interfaceInfo;
  350. parentFdoExt->configHandle = urb->UrbSelectConfiguration.ConfigurationHandle;
  351. interfaceInfo = &urb->UrbSelectConfiguration.Interface;
  352. parentFdoExt->interfaceInfo = MemDup(interfaceInfo, interfaceInfo->Length);
  353. if (parentFdoExt->interfaceInfo){
  354. DBGVERBOSE(("SelectConfiguration: got interfaceInfo @ %ph.", parentFdoExt->interfaceInfo));
  355. }
  356. else {
  357. status = STATUS_INSUFFICIENT_RESOURCES;
  358. }
  359. }
  360. else {
  361. DBGERR(("SelectConfiguration: URB failed w/ %xh.", status));
  362. }
  363. FREEPOOL(urb);
  364. }
  365. else {
  366. status = STATUS_INSUFFICIENT_RESOURCES;
  367. }
  368. }
  369. else {
  370. status = STATUS_DEVICE_DATA_ERROR;
  371. }
  372. return status;
  373. }
  374. NTSTATUS CreatePdoForEachEndpointPair(PARENTFDOEXT *parentFdoExt)
  375. /*++
  376. Routine Description:
  377. Walk the USB endpoints.
  378. For each input/output endpoint pair, create one PDO
  379. which will be exposed as a COM (serial) port interface.
  380. BUGBUG - right now, this only looks at the first interface
  381. (on the default confuguration)
  382. Arguments:
  383. parentFdoExt - device extension for the targetted device object
  384. Return Value:
  385. NT status code
  386. --*/
  387. {
  388. NTSTATUS status;
  389. ULONG maxPossiblePDOs, deviceRelationsSize;
  390. maxPossiblePDOs = (parentFdoExt->interfaceInfo->NumberOfPipes/2);
  391. deviceRelationsSize = sizeof(DEVICE_RELATIONS) + maxPossiblePDOs*sizeof(PDEVICE_OBJECT);
  392. parentFdoExt->deviceRelations = ALLOCPOOL(NonPagedPool, deviceRelationsSize);
  393. if (parentFdoExt->deviceRelations){
  394. ULONG inputPipeIndex = 0, outputPipeIndex = 0, statusPipeIndex = 0, comInterfaceIndex = 0;
  395. RtlZeroMemory(parentFdoExt->deviceRelations, deviceRelationsSize);
  396. status = STATUS_NO_MATCH;
  397. while (TRUE){
  398. UCHAR endPtAddr;
  399. USBD_PIPE_TYPE pipeType;
  400. #if STATUS_ENDPOINT
  401. /*
  402. * In the case of Serial Emulation, the protocol is that all DATA endpoints
  403. * will be of type BULK and all STATUS endpoints will be of type INTERRUPT.
  404. */
  405. if(parentFdoExt->posFlag & SERIAL_EMULATION) {
  406. while(inputPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes) {
  407. endPtAddr = parentFdoExt->interfaceInfo->Pipes[inputPipeIndex].EndpointAddress;
  408. pipeType = parentFdoExt->interfaceInfo->Pipes[inputPipeIndex].PipeType;
  409. if((pipeType == UsbdPipeTypeBulk) && (endPtAddr & USB_ENDPOINT_DIRECTION_MASK))
  410. break;
  411. inputPipeIndex++;
  412. }
  413. while(outputPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes) {
  414. endPtAddr = parentFdoExt->interfaceInfo->Pipes[outputPipeIndex].EndpointAddress;
  415. pipeType = parentFdoExt->interfaceInfo->Pipes[outputPipeIndex].PipeType;
  416. if((pipeType == UsbdPipeTypeBulk) && !(endPtAddr & USB_ENDPOINT_DIRECTION_MASK))
  417. break;
  418. outputPipeIndex++;
  419. }
  420. while(statusPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes) {
  421. endPtAddr = parentFdoExt->interfaceInfo->Pipes[statusPipeIndex].EndpointAddress;
  422. pipeType = parentFdoExt->interfaceInfo->Pipes[statusPipeIndex].PipeType;
  423. if((pipeType == UsbdPipeTypeInterrupt) && (endPtAddr & USB_ENDPOINT_DIRECTION_MASK))
  424. break;
  425. statusPipeIndex++;
  426. }
  427. if(!(statusPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes))
  428. break;
  429. }
  430. else {
  431. #endif
  432. /*
  433. * No Serial Emulation required. Find only DATA endpoints
  434. * which can be of either type in this case.
  435. */
  436. while(inputPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes) {
  437. endPtAddr = parentFdoExt->interfaceInfo->Pipes[inputPipeIndex].EndpointAddress;
  438. pipeType = parentFdoExt->interfaceInfo->Pipes[inputPipeIndex].PipeType;
  439. if((pipeType == UsbdPipeTypeInterrupt) || (pipeType == UsbdPipeTypeBulk)) {
  440. if(endPtAddr & USB_ENDPOINT_DIRECTION_MASK) {
  441. break;
  442. }
  443. }
  444. inputPipeIndex++;
  445. }
  446. while(outputPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes) {
  447. endPtAddr = parentFdoExt->interfaceInfo->Pipes[outputPipeIndex].EndpointAddress;
  448. pipeType = parentFdoExt->interfaceInfo->Pipes[outputPipeIndex].PipeType;
  449. if((pipeType == UsbdPipeTypeInterrupt) || (pipeType == UsbdPipeTypeBulk)) {
  450. if(!(endPtAddr & USB_ENDPOINT_DIRECTION_MASK)) {
  451. break;
  452. }
  453. }
  454. outputPipeIndex++;
  455. }
  456. #if STATUS_ENDPOINT
  457. }
  458. #endif
  459. if ((inputPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes) &&
  460. (outputPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes)){
  461. /*
  462. * We've found a pair (in/out) of endpoints.
  463. * Create a PDO to represent a COM (serial) port interface on these endpoints.
  464. */
  465. PUSBD_PIPE_INFORMATION inputPipeInfo = &parentFdoExt->interfaceInfo->Pipes[inputPipeIndex];
  466. PUSBD_PIPE_INFORMATION outputPipeInfo = &parentFdoExt->interfaceInfo->Pipes[outputPipeIndex];
  467. ENDPOINTINFO inputEndpointInfo, outputEndpointInfo, statusEndpointInfo;
  468. #if EPSON_PRINTER
  469. /*
  470. * On the EPSON printer, we want to talk to the endpoints size 0x40,
  471. * not the other pair with length 8.
  472. */
  473. if ((inputPipeInfo->MaximumPacketSize == 8) && (outputPipeInfo->MaximumPacketSize == 8)){
  474. inputPipeIndex++, outputPipeIndex++;
  475. continue;
  476. }
  477. #endif
  478. inputEndpointInfo.pipeHandle = inputPipeInfo->PipeHandle;
  479. inputEndpointInfo.pipeLen = inputPipeInfo->MaximumPacketSize;
  480. inputEndpointInfo.endpointIsBusy = FALSE;
  481. outputEndpointInfo.pipeHandle = outputPipeInfo->PipeHandle;
  482. outputEndpointInfo.pipeLen = outputPipeInfo->MaximumPacketSize;
  483. outputEndpointInfo.endpointIsBusy = FALSE;
  484. #if STATUS_ENDPOINT
  485. if(parentFdoExt->posFlag & SERIAL_EMULATION) {
  486. PUSBD_PIPE_INFORMATION statusPipeInfo = &parentFdoExt->interfaceInfo->Pipes[statusPipeIndex];
  487. statusEndpointInfo.pipeHandle = statusPipeInfo->PipeHandle;
  488. statusEndpointInfo.pipeLen = statusPipeInfo->MaximumPacketSize;
  489. statusEndpointInfo.endpointIsBusy = FALSE;
  490. }
  491. #endif
  492. status = CreateCOMPdo(parentFdoExt, comInterfaceIndex++, &inputEndpointInfo, &outputEndpointInfo, &statusEndpointInfo);
  493. if (NT_SUCCESS(status)){
  494. DBGVERBOSE(("CreatePdoForEachEndpointPair: created COMPdo with in/out len %xh/%xh.", inputEndpointInfo.pipeLen, outputEndpointInfo.pipeLen));
  495. inputPipeIndex++, outputPipeIndex++, statusPipeIndex++;
  496. }
  497. else {
  498. DBGERR(("CreatePdoForEachEndpointPair: CreateCOMPdo failed with %xh.", status));
  499. break;
  500. }
  501. }
  502. else {
  503. if((parentFdoExt->posFlag & ODD_ENDPOINT) &&
  504. ((inputPipeIndex + outputPipeIndex) < (2 * parentFdoExt->interfaceInfo->NumberOfPipes))) {
  505. /*
  506. * We've found an odd endpoint.
  507. * Create a PDO to represent a COM (serial) port interface on this endpoint.
  508. */
  509. PUSBD_PIPE_INFORMATION oddPipeInfo = &parentFdoExt->interfaceInfo->Pipes[MIN(inputPipeIndex, outputPipeIndex)];
  510. ENDPOINTINFO oddEndpointInfo;
  511. oddEndpointInfo.pipeHandle = oddPipeInfo->PipeHandle;
  512. oddEndpointInfo.pipeLen = oddPipeInfo->MaximumPacketSize;
  513. oddEndpointInfo.endpointIsBusy = FALSE;
  514. if(inputPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes)
  515. status = CreateCOMPdo(parentFdoExt, comInterfaceIndex++, &oddEndpointInfo, NULL, NULL);
  516. else
  517. status = CreateCOMPdo(parentFdoExt, comInterfaceIndex++, NULL, &oddEndpointInfo, NULL);
  518. if (NT_SUCCESS(status)){
  519. DBGVERBOSE(("CreatePdoForEachEndpointPair: created <odd> COMPdo with len %xh.", oddEndpointInfo.pipeLen));
  520. }
  521. else {
  522. DBGERR(("CreatePdoForEachEndpointPair: CreateCOMPdo failed with %xh.", status));
  523. break;
  524. }
  525. }
  526. break;
  527. }
  528. }
  529. }
  530. else {
  531. status = STATUS_INSUFFICIENT_RESOURCES;
  532. }
  533. ASSERT(NT_SUCCESS(status));
  534. return status;
  535. }