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.

3494 lines
95 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1998 - 1999
  6. //
  7. // File: util.c
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "pch.h"
  11. #define IDMFG 0
  12. #define IDMDL 1
  13. #define NUMOFBROTHERPRODUCT 14
  14. CHAR *XflagOnEvent24Devices[NUMOFBROTHERPRODUCT][2] =
  15. {
  16. // Brother
  17. {"Brother", "MFC" }, // 0
  18. {"Brother", "FAX" }, // 1
  19. {"Brother", "HL-P" }, // 2
  20. {"Brother", "DCP" }, // 3
  21. // PB
  22. {"PitneyBowes", "1630" }, // 4
  23. {"PitneyBowes", "1640" }, // 5
  24. // LEGEND
  25. {"LEGEND", "LJ6112MFC" }, // 6
  26. {"LEGEND", "LJ6212MFC" }, // 7
  27. //
  28. {"HBP", "MFC 6550" }, // 8
  29. {"HBP", "OMNI L621" }, // 9
  30. {"HBP", "LJ 6106MFC" }, // 10
  31. {"HBP", "LJ 6206MFC" }, // 11
  32. // P2500
  33. {"Legend", "LJ6012MFP" }, // 12
  34. //Terminater
  35. {NULL, NULL } // 13
  36. };
  37. NTSTATUS
  38. PptAcquireRemoveLockOrFailIrp(PDEVICE_OBJECT DevObj, PIRP Irp)
  39. {
  40. PFDO_EXTENSION fdx = DevObj->DeviceExtension;
  41. NTSTATUS status = PptAcquireRemoveLock( &fdx->RemoveLock, Irp );
  42. if( status != STATUS_SUCCESS ) {
  43. P4CompleteRequest( Irp, status, Irp->IoStatus.Information );
  44. }
  45. return status;
  46. }
  47. NTSTATUS
  48. PptDispatchPreProcessIrp(
  49. IN PDEVICE_OBJECT DevObj,
  50. IN PIRP Irp
  51. )
  52. /*++
  53. - Acquire removelock
  54. - If(!Special Handling IRP) {
  55. check if we are running, stalled
  56. --*/
  57. {
  58. PFDO_EXTENSION Fdx = DevObj->DeviceExtension;
  59. NTSTATUS status = PptAcquireRemoveLock(&Fdx->RemoveLock, Irp);
  60. UNREFERENCED_PARAMETER(DevObj);
  61. UNREFERENCED_PARAMETER(Irp);
  62. if ( !NT_SUCCESS( status ) ) {
  63. //
  64. // Someone gave us a pnp irp after a remove. Unthinkable!
  65. //
  66. PptAssertMsg("Someone gave us a PnP IRP after a Remove",FALSE);
  67. P4CompleteRequest( Irp, status, 0 );
  68. }
  69. return status;
  70. }
  71. NTSTATUS
  72. PptSynchCompletionRoutine(
  73. IN PDEVICE_OBJECT DevObj,
  74. IN PIRP Irp,
  75. IN PKEVENT Event
  76. )
  77. /*++
  78. Routine Description:
  79. This routine is for use with synchronous IRP processing.
  80. All it does is signal an event, so the driver knows it
  81. can continue.
  82. Arguments:
  83. DriverObject - Pointer to driver object created by system.
  84. Irp - Irp that just completed
  85. Event - Event we'll signal to say Irp is done
  86. Return Value:
  87. None.
  88. --*/
  89. {
  90. UNREFERENCED_PARAMETER( Irp );
  91. UNREFERENCED_PARAMETER( DevObj );
  92. KeSetEvent((PKEVENT) Event, 0, FALSE);
  93. return (STATUS_MORE_PROCESSING_REQUIRED);
  94. }
  95. PWSTR
  96. PptGetPortNameFromPhysicalDeviceObject(
  97. PDEVICE_OBJECT PhysicalDeviceObject
  98. )
  99. /*++
  100. Routine Description:
  101. Retrieve the PortName for the ParPort from the registry. This PortName
  102. will be used as the symbolic link name for the end of chain device
  103. object created by ParClass for this ParPort.
  104. *** This function allocates pool. ExFreePool must be called when
  105. result is no longer needed.
  106. Arguments:
  107. PortDeviceObject - The ParPort Device Object
  108. Return Value:
  109. PortName - if successful
  110. NULL - otherwise
  111. --*/
  112. {
  113. NTSTATUS status;
  114. HANDLE hKey;
  115. PKEY_VALUE_FULL_INFORMATION buffer;
  116. ULONG bufferLength;
  117. ULONG resultLength;
  118. PWSTR valueNameWstr;
  119. UNICODE_STRING valueName;
  120. PWSTR portName = NULL;
  121. PAGED_CODE ();
  122. //
  123. // try to open the registry key
  124. //
  125. status = IoOpenDeviceRegistryKey(PhysicalDeviceObject,
  126. PLUGPLAY_REGKEY_DEVICE,
  127. STANDARD_RIGHTS_ALL,
  128. &hKey);
  129. if( !NT_SUCCESS(status) ) {
  130. // unable to open key, bail out
  131. DD(NULL,DDT,"PptGetPortNameFromPhysicalDeviceObject(): FAIL w/status = %x\n", status);
  132. return NULL;
  133. }
  134. //
  135. // we have a handle to the registry key
  136. //
  137. // loop trying to read registry value until either we succeed or
  138. // we get a hard failure, grow the result buffer as needed
  139. //
  140. bufferLength = 0; // we will ask how large a buffer we need
  141. buffer = NULL;
  142. valueNameWstr = (PWSTR)L"PortName";
  143. RtlInitUnicodeString(&valueName, valueNameWstr);
  144. status = STATUS_BUFFER_TOO_SMALL;
  145. while(status == STATUS_BUFFER_TOO_SMALL) {
  146. status = ZwQueryValueKey(hKey,
  147. &valueName,
  148. KeyValueFullInformation,
  149. buffer,
  150. bufferLength,
  151. &resultLength);
  152. if(status == STATUS_BUFFER_TOO_SMALL) {
  153. //
  154. // buffer too small, free it and allocate a larger buffer
  155. //
  156. if(buffer) ExFreePool(buffer);
  157. buffer = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, resultLength);
  158. bufferLength = resultLength;
  159. if(!buffer) {
  160. // unable to allocate pool, clean up and exit
  161. ZwClose(hKey);
  162. return NULL;
  163. }
  164. }
  165. } // end while BUFFER_TOO_SMALL
  166. //
  167. // query is complete
  168. //
  169. // no longer need the handle so close it
  170. ZwClose(hKey);
  171. // check the status of our query
  172. if( !NT_SUCCESS(status) ) {
  173. if(buffer) ExFreePool(buffer);
  174. return NULL;
  175. }
  176. // make sure we have a buffer
  177. if( buffer ) {
  178. // sanity check our result
  179. if( (buffer->Type != REG_SZ) || (!buffer->DataLength) ) {
  180. ExFreePool(buffer); // query succeeded, so we know we have a buffer
  181. return NULL;
  182. }
  183. //
  184. // result looks ok, copy PortName to its own allocation of the proper size
  185. // and return a pointer to it
  186. //
  187. portName = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, buffer->DataLength);
  188. if(!portName) {
  189. // unable to allocate pool, clean up and exit
  190. ExFreePool(buffer);
  191. return NULL;
  192. }
  193. RtlCopyMemory(portName, (PUCHAR)buffer + buffer->DataOffset, buffer->DataLength);
  194. ExFreePool( buffer );
  195. }
  196. return portName;
  197. }
  198. NTSTATUS
  199. PptConnectInterrupt(
  200. IN PFDO_EXTENSION Fdx
  201. )
  202. /*++
  203. Routine Description:
  204. This routine connects the port interrupt service routine
  205. to the interrupt.
  206. Arguments:
  207. Fdx - Supplies the device extension.
  208. Return Value:
  209. NTSTATUS code.
  210. --*/
  211. {
  212. NTSTATUS Status = STATUS_SUCCESS;
  213. if (!Fdx->FoundInterrupt) {
  214. return STATUS_NOT_SUPPORTED;
  215. }
  216. //
  217. // Connect the interrupt.
  218. //
  219. Status = IoConnectInterrupt(&Fdx->InterruptObject,
  220. PptInterruptService,
  221. Fdx,
  222. NULL,
  223. Fdx->InterruptVector,
  224. Fdx->InterruptLevel,
  225. Fdx->InterruptLevel,
  226. Fdx->InterruptMode,
  227. TRUE,
  228. Fdx->InterruptAffinity,
  229. FALSE);
  230. if (!NT_SUCCESS(Status)) {
  231. PptLogError(Fdx->DeviceObject->DriverObject,
  232. Fdx->DeviceObject,
  233. Fdx->PortInfo.OriginalController,
  234. PhysicalZero, 0, 0, 0, 14,
  235. Status, PAR_INTERRUPT_CONFLICT);
  236. }
  237. return Status;
  238. }
  239. VOID
  240. PptDisconnectInterrupt(
  241. IN PFDO_EXTENSION Fdx
  242. )
  243. /*++
  244. Routine Description:
  245. This routine disconnects the port interrupt service routine
  246. from the interrupt.
  247. Arguments:
  248. Fdx - Supplies the device extension.
  249. Return Value:
  250. None.
  251. --*/
  252. {
  253. IoDisconnectInterrupt(Fdx->InterruptObject);
  254. }
  255. BOOLEAN
  256. PptSynchronizedIncrement(
  257. IN OUT PVOID SyncContext
  258. )
  259. /*++
  260. Routine Description:
  261. This routine increments the 'Count' variable in the context and returns
  262. its new value in the 'NewCount' variable.
  263. Arguments:
  264. SyncContext - Supplies the synchronize count context.
  265. Return Value:
  266. TRUE
  267. --*/
  268. {
  269. ((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->NewCount =
  270. ++(*(((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->Count));
  271. return(TRUE);
  272. }
  273. BOOLEAN
  274. PptSynchronizedDecrement(
  275. IN OUT PVOID SyncContext
  276. )
  277. /*++
  278. Routine Description:
  279. This routine decrements the 'Count' variable in the context and returns
  280. its new value in the 'NewCount' variable.
  281. Arguments:
  282. SyncContext - Supplies the synchronize count context.
  283. Return Value:
  284. TRUE
  285. --*/
  286. {
  287. ((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->NewCount =
  288. --(*(((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->Count));
  289. return(TRUE);
  290. }
  291. BOOLEAN
  292. PptSynchronizedRead(
  293. IN OUT PVOID SyncContext
  294. )
  295. /*++
  296. Routine Description:
  297. This routine reads the 'Count' variable in the context and returns
  298. its value in the 'NewCount' variable.
  299. Arguments:
  300. SyncContext - Supplies the synchronize count context.
  301. Return Value:
  302. None.
  303. --*/
  304. {
  305. ((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->NewCount =
  306. *(((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->Count);
  307. return(TRUE);
  308. }
  309. BOOLEAN
  310. PptSynchronizedQueue(
  311. IN PVOID Context
  312. )
  313. /*++
  314. Routine Description:
  315. This routine adds the given list entry to the given list.
  316. Arguments:
  317. Context - Supplies the synchronized list context.
  318. Return Value:
  319. TRUE
  320. --*/
  321. {
  322. PSYNCHRONIZED_LIST_CONTEXT ListContext;
  323. ListContext = Context;
  324. InsertTailList(ListContext->List, ListContext->NewEntry);
  325. return(TRUE);
  326. }
  327. BOOLEAN
  328. PptSynchronizedDisconnect(
  329. IN PVOID Context
  330. )
  331. /*++
  332. Routine Description:
  333. This routine removes the given list entry from the ISR
  334. list.
  335. Arguments:
  336. Context - Supplies the synchronized disconnect context.
  337. Return Value:
  338. FALSE - The given list entry was not removed from the list.
  339. TRUE - The given list entry was removed from the list.
  340. --*/
  341. {
  342. PSYNCHRONIZED_DISCONNECT_CONTEXT DisconnectContext;
  343. PKSERVICE_ROUTINE ServiceRoutine;
  344. PVOID ServiceContext;
  345. PLIST_ENTRY Current;
  346. PISR_LIST_ENTRY ListEntry;
  347. DisconnectContext = Context;
  348. ServiceRoutine = DisconnectContext->IsrInfo->InterruptServiceRoutine;
  349. ServiceContext = DisconnectContext->IsrInfo->InterruptServiceContext;
  350. for (Current = DisconnectContext->Extension->IsrList.Flink;
  351. Current != &(DisconnectContext->Extension->IsrList);
  352. Current = Current->Flink) {
  353. ListEntry = CONTAINING_RECORD(Current, ISR_LIST_ENTRY, ListEntry);
  354. if (ListEntry->ServiceRoutine == ServiceRoutine &&
  355. ListEntry->ServiceContext == ServiceContext) {
  356. RemoveEntryList(Current);
  357. return TRUE;
  358. }
  359. }
  360. return FALSE;
  361. }
  362. VOID
  363. PptCancelRoutine(
  364. IN OUT PDEVICE_OBJECT DeviceObject,
  365. IN OUT PIRP Irp
  366. )
  367. /*++
  368. Routine Description:
  369. This routine is called on when the given IRP is cancelled. It
  370. will dequeue this IRP off the work queue and complete the
  371. request as CANCELLED. If it can't get if off the queue then
  372. this routine will ignore the CANCEL request since the IRP
  373. is about to complete anyway.
  374. Arguments:
  375. DeviceObject - Supplies the device object.
  376. Irp - Supplies the IRP.
  377. Return Value:
  378. None.
  379. --*/
  380. {
  381. PFDO_EXTENSION Fdx = DeviceObject->DeviceExtension;
  382. SYNCHRONIZED_COUNT_CONTEXT SyncContext;
  383. DD((PCE)Fdx,DDT,"CANCEL IRP %x\n", Irp);
  384. SyncContext.Count = &Fdx->WorkQueueCount;
  385. if (Fdx->InterruptRefCount) {
  386. KeSynchronizeExecution( Fdx->InterruptObject, PptSynchronizedDecrement, &SyncContext );
  387. } else {
  388. PptSynchronizedDecrement( &SyncContext );
  389. }
  390. RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
  391. IoReleaseCancelSpinLock(Irp->CancelIrql);
  392. PptReleaseRemoveLock(&Fdx->RemoveLock, Irp);
  393. P4CompleteRequest( Irp, STATUS_CANCELLED, 0 );
  394. }
  395. VOID
  396. PptFreePortDpc(
  397. IN PKDPC Dpc,
  398. IN OUT PVOID Fdx,
  399. IN PVOID SystemArgument1,
  400. IN PVOID SystemArgument2
  401. )
  402. /*++
  403. Routine Description:
  404. This routine is a DPC that will free the port and if necessary
  405. complete an alloc request that is waiting.
  406. Arguments:
  407. Dpc - Not used.
  408. Fdx - Supplies the device extension.
  409. SystemArgument1 - Not used.
  410. SystemArgument2 - Not used.
  411. Return Value:
  412. None.
  413. --*/
  414. {
  415. UNREFERENCED_PARAMETER( Dpc );
  416. UNREFERENCED_PARAMETER( SystemArgument1 );
  417. UNREFERENCED_PARAMETER( SystemArgument2 );
  418. PptFreePort(Fdx);
  419. }
  420. BOOLEAN
  421. PptTryAllocatePortAtInterruptLevel(
  422. IN PVOID Context
  423. )
  424. /*++
  425. Routine Description:
  426. This routine is called at interrupt level to quickly allocate
  427. the parallel port if it is available. This call will fail
  428. if the port is not available.
  429. Arguments:
  430. Context - Supplies the device extension.
  431. Return Value:
  432. FALSE - The port was not allocated.
  433. TRUE - The port was successfully allocated.
  434. --*/
  435. {
  436. if (((PFDO_EXTENSION) Context)->WorkQueueCount == -1) {
  437. ((PFDO_EXTENSION) Context)->WorkQueueCount = 0;
  438. ( (PFDO_EXTENSION)Context )->WmiPortAllocFreeCounts.PortAllocates++;
  439. return(TRUE);
  440. } else {
  441. return(FALSE);
  442. }
  443. }
  444. VOID
  445. PptFreePortFromInterruptLevel(
  446. IN PVOID Context
  447. )
  448. /*++
  449. Routine Description:
  450. This routine frees the port that was allocated at interrupt level.
  451. Arguments:
  452. Context - Supplies the device extension.
  453. Return Value:
  454. None.
  455. --*/
  456. {
  457. // If no one is waiting for the port then this is simple operation,
  458. // otherwise queue a DPC to free the port later on.
  459. if (((PFDO_EXTENSION) Context)->WorkQueueCount == 0) {
  460. ((PFDO_EXTENSION) Context)->WorkQueueCount = -1;
  461. } else {
  462. KeInsertQueueDpc(&((PFDO_EXTENSION) Context)->FreePortDpc, NULL, NULL);
  463. }
  464. }
  465. BOOLEAN
  466. PptInterruptService(
  467. IN PKINTERRUPT Interrupt,
  468. IN PVOID Fdx
  469. )
  470. /*++
  471. Routine Description:
  472. This routine services the interrupt for the parallel port.
  473. This routine will call out to all of the interrupt routines
  474. that connected with this device via
  475. IOCTL_INTERNAL_PARALLEL_CONNECT_INTERRUPT in order until
  476. one of them returns TRUE.
  477. Arguments:
  478. Interrupt - Supplies the interrupt object.
  479. Fdx - Supplies the device extension.
  480. Return Value:
  481. FALSE - The interrupt was not handled.
  482. TRUE - The interrupt was handled.
  483. --*/
  484. {
  485. PLIST_ENTRY Current;
  486. PISR_LIST_ENTRY IsrListEntry;
  487. PFDO_EXTENSION fdx = Fdx;
  488. for( Current = fdx->IsrList.Flink; Current != &fdx->IsrList; Current = Current->Flink ) {
  489. IsrListEntry = CONTAINING_RECORD(Current, ISR_LIST_ENTRY, ListEntry);
  490. if (IsrListEntry->ServiceRoutine(Interrupt, IsrListEntry->ServiceContext)) {
  491. return TRUE;
  492. }
  493. }
  494. return FALSE;
  495. }
  496. BOOLEAN
  497. PptTryAllocatePort(
  498. IN PVOID Fdx
  499. )
  500. /*++
  501. Routine Description:
  502. This routine attempts to allocate the port. If the port is
  503. available then the call will succeed with the port allocated.
  504. If the port is not available the then call will fail
  505. immediately.
  506. Arguments:
  507. Fdx - Supplies the device extension.
  508. Return Value:
  509. FALSE - The port was not allocated.
  510. TRUE - The port was allocated.
  511. --*/
  512. {
  513. PFDO_EXTENSION fdx = Fdx;
  514. KIRQL CancelIrql;
  515. BOOLEAN b;
  516. if (fdx->InterruptRefCount) {
  517. b = KeSynchronizeExecution( fdx->InterruptObject, PptTryAllocatePortAtInterruptLevel, fdx );
  518. } else {
  519. IoAcquireCancelSpinLock(&CancelIrql);
  520. b = PptTryAllocatePortAtInterruptLevel(fdx);
  521. IoReleaseCancelSpinLock(CancelIrql);
  522. }
  523. DD((PCE)fdx,DDT,"PptTryAllocatePort on %x returned %x\n", fdx->PortInfo.Controller, b);
  524. return b;
  525. }
  526. BOOLEAN
  527. PptTraversePortCheckList(
  528. IN PVOID Fdx
  529. )
  530. /*++
  531. Routine Description:
  532. This routine traverses the deferred port check routines. This
  533. call must be synchronized at interrupt level so that real
  534. interrupts are blocked until these routines are completed.
  535. Arguments:
  536. Fdx - Supplies the device extension.
  537. Return Value:
  538. FALSE - The port is in use so no action taken by this routine.
  539. TRUE - All of the deferred interrupt routines were called.
  540. --*/
  541. {
  542. PFDO_EXTENSION fdx = Fdx;
  543. PLIST_ENTRY Current;
  544. PISR_LIST_ENTRY CheckEntry;
  545. //
  546. // First check to make sure that the port is still free.
  547. //
  548. if (fdx->WorkQueueCount >= 0) {
  549. return FALSE;
  550. }
  551. for (Current = fdx->IsrList.Flink;
  552. Current != &fdx->IsrList;
  553. Current = Current->Flink) {
  554. CheckEntry = CONTAINING_RECORD(Current,
  555. ISR_LIST_ENTRY,
  556. ListEntry);
  557. if (CheckEntry->DeferredPortCheckRoutine) {
  558. CheckEntry->DeferredPortCheckRoutine(CheckEntry->CheckContext);
  559. }
  560. }
  561. return TRUE;
  562. }
  563. VOID
  564. PptFreePort(
  565. IN PVOID Fdx
  566. )
  567. /*++
  568. Routine Description:
  569. This routine frees the port.
  570. Arguments:
  571. Fdx - Supplies the device extension.
  572. Return Value:
  573. None.
  574. --*/
  575. {
  576. PFDO_EXTENSION fdx = Fdx;
  577. SYNCHRONIZED_COUNT_CONTEXT SyncContext;
  578. KIRQL CancelIrql;
  579. PLIST_ENTRY Head;
  580. PIRP Irp;
  581. PIO_STACK_LOCATION IrpSp;
  582. ULONG InterruptRefCount;
  583. PPARALLEL_1284_COMMAND Command;
  584. BOOLEAN Allocated;
  585. DD((PCE)fdx,DDT,"PptFreePort\n");
  586. SyncContext.Count = &fdx->WorkQueueCount;
  587. IoAcquireCancelSpinLock( &CancelIrql );
  588. if (fdx->InterruptRefCount) {
  589. KeSynchronizeExecution( fdx->InterruptObject, PptSynchronizedDecrement, &SyncContext );
  590. } else {
  591. PptSynchronizedDecrement( &SyncContext );
  592. }
  593. IoReleaseCancelSpinLock( CancelIrql );
  594. //
  595. // Log the free for WMI
  596. //
  597. fdx->WmiPortAllocFreeCounts.PortFrees++;
  598. //
  599. // Port is free, check for queued ALLOCATE and/or SELECT requests
  600. //
  601. Allocated = FALSE;
  602. while( !Allocated && SyncContext.NewCount >= 0 ) {
  603. //
  604. // We have ALLOCATE and/or SELECT requests queued, satisfy the first request
  605. //
  606. IoAcquireCancelSpinLock(&CancelIrql);
  607. Head = RemoveHeadList(&fdx->WorkQueue);
  608. if( Head == &fdx->WorkQueue ) {
  609. // queue is empty - we're done - exit while loop
  610. IoReleaseCancelSpinLock(CancelIrql);
  611. break;
  612. }
  613. Irp = CONTAINING_RECORD(Head, IRP, Tail.Overlay.ListEntry);
  614. PptSetCancelRoutine(Irp, NULL);
  615. if ( Irp->Cancel ) {
  616. Irp->IoStatus.Status = STATUS_CANCELLED;
  617. // Irp was cancelled so have to get next in line
  618. SyncContext.Count = &fdx->WorkQueueCount;
  619. if (fdx->InterruptRefCount) {
  620. KeSynchronizeExecution(fdx->InterruptObject, PptSynchronizedDecrement, &SyncContext);
  621. } else {
  622. PptSynchronizedDecrement(&SyncContext);
  623. }
  624. IoReleaseCancelSpinLock(CancelIrql);
  625. } else {
  626. Allocated = TRUE;
  627. IoReleaseCancelSpinLock(CancelIrql);
  628. // Finding out what kind of IOCTL it was
  629. IrpSp = IoGetCurrentIrpStackLocation(Irp);
  630. // Check to see if we need to select a
  631. if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_SELECT_DEVICE ) {
  632. // request at head of queue was a SELECT
  633. // so call the select function with the device command saying we already have port
  634. Command = (PPARALLEL_1284_COMMAND)Irp->AssociatedIrp.SystemBuffer;
  635. Command->CommandFlags |= PAR_HAVE_PORT_KEEP_PORT;
  636. // Call Function to try to select device
  637. Irp->IoStatus.Status = PptTrySelectDevice( Fdx, Command );
  638. } else {
  639. // request at head of queue was an ALLOCATE
  640. Irp->IoStatus.Status = STATUS_SUCCESS;
  641. }
  642. // Note that another Allocate request has been granted (WMI counts allocs)
  643. fdx->WmiPortAllocFreeCounts.PortAllocates++;
  644. }
  645. // Remove remove lock on Irp and Complete request whether the Irp
  646. // was cancelled or we acquired the port
  647. PptReleaseRemoveLock(&fdx->RemoveLock, Irp);
  648. P4CompleteRequest( Irp, Irp->IoStatus.Status, Irp->IoStatus.Information );
  649. }
  650. if( !Allocated ) {
  651. //
  652. // ALLOCATE/SELECT request queue was empty
  653. //
  654. IoAcquireCancelSpinLock(&CancelIrql);
  655. InterruptRefCount = fdx->InterruptRefCount;
  656. IoReleaseCancelSpinLock(CancelIrql);
  657. if ( InterruptRefCount ) {
  658. KeSynchronizeExecution( fdx->InterruptObject, PptTraversePortCheckList, fdx );
  659. }
  660. }
  661. return;
  662. }
  663. ULONG
  664. PptQueryNumWaiters(
  665. IN PVOID Fdx
  666. )
  667. /*++
  668. Routine Description:
  669. This routine returns the number of irps queued waiting for
  670. the parallel port.
  671. Arguments:
  672. Fdx - Supplies the device extension.
  673. Return Value:
  674. The number of irps queued waiting for the port.
  675. --*/
  676. {
  677. PFDO_EXTENSION fdx = Fdx;
  678. KIRQL CancelIrql;
  679. SYNCHRONIZED_COUNT_CONTEXT SyncContext;
  680. LONG count;
  681. SyncContext.Count = &fdx->WorkQueueCount;
  682. if (fdx->InterruptRefCount) {
  683. KeSynchronizeExecution(fdx->InterruptObject,
  684. PptSynchronizedRead,
  685. &SyncContext);
  686. } else {
  687. IoAcquireCancelSpinLock(&CancelIrql);
  688. PptSynchronizedRead(&SyncContext);
  689. IoReleaseCancelSpinLock(CancelIrql);
  690. }
  691. count = (SyncContext.NewCount >= 0) ? ((ULONG)SyncContext.NewCount) : 0;
  692. if( fdx->FdoWaitingOnPort ) {
  693. ++count;
  694. }
  695. return count;
  696. }
  697. PVOID
  698. PptSetCancelRoutine(PIRP Irp, PDRIVER_CANCEL CancelRoutine)
  699. {
  700. // #pragma warning( push )
  701. // 4054: 'type cast' : from function pointer to data pointer
  702. // 4055: 'type cast' : from data pointer to function pointer
  703. // 4152: nonstandard extension, function/data pointer conversion in expression
  704. #pragma warning( disable : 4054 4055 4152 )
  705. return IoSetCancelRoutine(Irp, CancelRoutine);
  706. // #pragma warning( pop )
  707. }
  708. // this is the version from Win2k parclass
  709. BOOLEAN
  710. CheckPort(
  711. IN PUCHAR wPortAddr,
  712. IN UCHAR bMask,
  713. IN UCHAR bValue,
  714. IN USHORT msTimeDelay
  715. )
  716. /*++
  717. Routine Description:
  718. This routine will loop for a given time period (actual time is
  719. passed in as an arguement) and wait for the dsr to match
  720. predetermined value (dsr value is passed in).
  721. Arguments:
  722. wPortAddr - Supplies the base address of the parallel port + some offset.
  723. This will have us point directly to the dsr (controller + 1).
  724. bMask - Mask used to determine which bits we are looking at
  725. bValue - Value we are looking for.
  726. msTimeDelay - Max time to wait for peripheral response (in ms)
  727. Return Value:
  728. TRUE if a dsr match was found.
  729. FALSE if the time period expired before a match was found.
  730. --*/
  731. {
  732. UCHAR dsr;
  733. LARGE_INTEGER Wait;
  734. LARGE_INTEGER Start;
  735. LARGE_INTEGER End;
  736. // Do a quick check in case we have one stinkingly fast peripheral!
  737. dsr = P5ReadPortUchar(wPortAddr);
  738. if ((dsr & bMask) == bValue)
  739. return TRUE;
  740. Wait.QuadPart = (msTimeDelay * 10 * 1000) + KeQueryTimeIncrement();
  741. KeQueryTickCount(&Start);
  742. CheckPort_Start:
  743. KeQueryTickCount(&End);
  744. dsr = P5ReadPortUchar(wPortAddr);
  745. if ((dsr & bMask) == bValue)
  746. return TRUE;
  747. if ((End.QuadPart - Start.QuadPart) * KeQueryTimeIncrement() > Wait.QuadPart)
  748. {
  749. // We timed out!!!
  750. // do one last check
  751. dsr = P5ReadPortUchar(wPortAddr);
  752. if ((dsr & bMask) == bValue)
  753. return TRUE;
  754. #if DVRH_BUS_RESET_ON_ERROR
  755. BusReset(wPortAddr+1); // Pass in the dcr address
  756. #endif
  757. #if DBG
  758. DD(NULL,DDW,"CheckPort: Timeout\n");
  759. {
  760. int i;
  761. for (i = 3; i < 8; i++) {
  762. if ((bMask >> i) & 1) {
  763. if (((bValue >> i) & 1) != ((dsr >> i) & 1)) {
  764. DD(NULL,DDW,"Bit %d is %d and should be %d!!!\n", i, (dsr >> i) & 1, (bValue >> i) & 1);
  765. }
  766. }
  767. }
  768. }
  769. #endif
  770. goto CheckPort_TimeOut;
  771. }
  772. goto CheckPort_Start;
  773. CheckPort_TimeOut:
  774. return FALSE;
  775. }
  776. NTSTATUS
  777. PptBuildParallelPortDeviceName(
  778. IN ULONG Number,
  779. OUT PUNICODE_STRING DeviceName
  780. )
  781. /*++
  782. Routine Description:
  783. Build a Device Name of the form: \Device\ParallelPortN
  784. *** On success this function returns allocated memory that must be freed by the caller
  785. Arguments:
  786. DriverObject - ParPort driver object
  787. PhysicalDeviceObject - PDO whose stack the ParPort FDO will attach to
  788. DeviceObject - ParPort FDO
  789. UniNameString - the DeviceName (e.g., \Device\ParallelPortN)
  790. PortName - the "LPTx" PortName from the devnode
  791. PortNumber - the "N" in \Device\ParallelPortN
  792. Return Value:
  793. STATUS_SUCCESS on success
  794. error status otherwise
  795. --*/
  796. {
  797. UNICODE_STRING uniDeviceString;
  798. UNICODE_STRING uniBaseNameString;
  799. UNICODE_STRING uniPortNumberString;
  800. WCHAR wcPortNum[10];
  801. NTSTATUS status;
  802. //
  803. // Init strings
  804. //
  805. RtlInitUnicodeString( DeviceName, NULL );
  806. RtlInitUnicodeString( &uniDeviceString, (PWSTR)L"\\Device\\" );
  807. RtlInitUnicodeString( &uniBaseNameString, (PWSTR)DD_PARALLEL_PORT_BASE_NAME_U );
  808. //
  809. // Convert Port Number to UNICODE_STRING
  810. //
  811. uniPortNumberString.Length = 0;
  812. uniPortNumberString.MaximumLength = sizeof( wcPortNum );
  813. uniPortNumberString.Buffer = wcPortNum;
  814. status = RtlIntegerToUnicodeString( Number, 10, &uniPortNumberString);
  815. if( !NT_SUCCESS( status ) ) {
  816. return status;
  817. }
  818. //
  819. // Compute size required and alloc a buffer
  820. //
  821. DeviceName->MaximumLength = (USHORT)( uniDeviceString.Length +
  822. uniBaseNameString.Length +
  823. uniPortNumberString.Length +
  824. sizeof(UNICODE_NULL) );
  825. DeviceName->Buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, DeviceName->MaximumLength );
  826. if( NULL == DeviceName->Buffer ) {
  827. return STATUS_INSUFFICIENT_RESOURCES;
  828. }
  829. RtlZeroMemory( DeviceName->Buffer, DeviceName->MaximumLength );
  830. //
  831. // Catenate the parts to construct the DeviceName
  832. //
  833. RtlAppendUnicodeStringToString(DeviceName, &uniDeviceString);
  834. RtlAppendUnicodeStringToString(DeviceName, &uniBaseNameString);
  835. RtlAppendUnicodeStringToString(DeviceName, &uniPortNumberString);
  836. return STATUS_SUCCESS;
  837. }
  838. NTSTATUS
  839. PptInitializeDeviceExtension(
  840. IN PDRIVER_OBJECT DriverObject,
  841. IN PDEVICE_OBJECT PhysicalDeviceObject,
  842. IN PDEVICE_OBJECT DeviceObject,
  843. IN PUNICODE_STRING UniNameString,
  844. IN PWSTR PortName,
  845. IN ULONG PortNumber
  846. )
  847. /*++
  848. Routine Description:
  849. Initialize a ParPort FDO DeviceExtension
  850. Arguments:
  851. DriverObject - ParPort driver object
  852. PhysicalDeviceObject - PDO whose stack the ParPort FDO will attach to
  853. DeviceObject - ParPort FDO
  854. UniNameString - the DeviceName (e.g., \Device\ParallelPortN)
  855. PortName - the "LPTx" PortName from the devnode
  856. PortNumber - the "N" in \Device\ParallelPortN
  857. Return Value:
  858. STATUS_SUCCESS on success
  859. error status otherwise
  860. --*/
  861. {
  862. PFDO_EXTENSION Fdx = DeviceObject->DeviceExtension;
  863. RtlZeroMemory( Fdx, sizeof(FDO_EXTENSION) );
  864. //
  865. // Signature1 helps confirm that we are looking at a Parport DeviceExtension
  866. //
  867. Fdx->Signature1 = PARPORT_TAG;
  868. Fdx->Signature2 = PARPORT_TAG;
  869. //
  870. // Standard Info
  871. //
  872. Fdx->DriverObject = DriverObject;
  873. Fdx->PhysicalDeviceObject = PhysicalDeviceObject;
  874. Fdx->DeviceObject = DeviceObject;
  875. Fdx->PnpInfo.PortNumber = PortNumber; // this is the "N" in \Device\ParallelPortN
  876. //
  877. // We are an FDO
  878. //
  879. Fdx->DevType = DevTypeFdo;
  880. //
  881. // Mutual Exclusion initialization
  882. //
  883. IoInitializeRemoveLock(&Fdx->RemoveLock, PARPORT_TAG, 1, 10);
  884. ExInitializeFastMutex(&Fdx->OpenCloseMutex);
  885. ExInitializeFastMutex(&Fdx->ExtensionFastMutex);
  886. //
  887. // chipset detection initialization - redundant, but safer
  888. //
  889. Fdx->NationalChipFound = FALSE;
  890. Fdx->NationalChecked = FALSE;
  891. //
  892. // List Head for List of PDOs to delete during driver unload, if not before
  893. //
  894. InitializeListHead(&Fdx->DevDeletionListHead);
  895. //
  896. // Initialize 'WorkQueue' - a Queue for Allocate and Select requests
  897. //
  898. InitializeListHead(&Fdx->WorkQueue);
  899. Fdx->WorkQueueCount = -1;
  900. //
  901. // Initialize Exports - Exported via Internal IOCTLs
  902. //
  903. Fdx->PortInfo.FreePort = PptFreePort;
  904. Fdx->PortInfo.TryAllocatePort = PptTryAllocatePort;
  905. Fdx->PortInfo.QueryNumWaiters = PptQueryNumWaiters;
  906. Fdx->PortInfo.Context = Fdx;
  907. Fdx->PnpInfo.HardwareCapabilities = PPT_NO_HARDWARE_PRESENT;
  908. Fdx->PnpInfo.TrySetChipMode = PptSetChipMode;
  909. Fdx->PnpInfo.ClearChipMode = PptClearChipMode;
  910. Fdx->PnpInfo.TrySelectDevice = PptTrySelectDevice;
  911. Fdx->PnpInfo.DeselectDevice = PptDeselectDevice;
  912. Fdx->PnpInfo.Context = Fdx;
  913. Fdx->PnpInfo.PortName = PortName;
  914. //
  915. // Save location info in common extension
  916. //
  917. {
  918. ULONG bufLen = sizeof("LPTxF");
  919. PCHAR buffer = ExAllocatePool( NonPagedPool, bufLen );
  920. if( buffer ) {
  921. RtlZeroMemory( buffer, bufLen );
  922. _snprintf( buffer, bufLen, "%.4SF", PortName );
  923. Fdx->Location = buffer;
  924. }
  925. }
  926. //
  927. // Empty list of interrupt service routines, interrupt NOT connected
  928. //
  929. InitializeListHead( &Fdx->IsrList );
  930. Fdx->InterruptObject = NULL;
  931. Fdx->InterruptRefCount = 0;
  932. //
  933. // Initialize the free port DPC.
  934. //
  935. KeInitializeDpc( &Fdx->FreePortDpc, PptFreePortDpc, Fdx );
  936. //
  937. // Save Device Name in our extension
  938. //
  939. {
  940. ULONG bufferLength = UniNameString->MaximumLength + sizeof(UNICODE_NULL);
  941. Fdx->DeviceName.Buffer = ExAllocatePool(NonPagedPool, bufferLength);
  942. if( !Fdx->DeviceName.Buffer ) {
  943. return STATUS_INSUFFICIENT_RESOURCES;
  944. }
  945. RtlZeroMemory( Fdx->DeviceName.Buffer, bufferLength );
  946. Fdx->DeviceName.Length = 0;
  947. Fdx->DeviceName.MaximumLength = UniNameString->MaximumLength;
  948. RtlCopyUnicodeString( &Fdx->DeviceName, UniNameString );
  949. }
  950. //
  951. // Port is in default mode and mode has not been set
  952. // by a lower filter driver
  953. //
  954. Fdx->PnpInfo.CurrentMode = INITIAL_MODE;
  955. Fdx->FilterMode = FALSE;
  956. return STATUS_SUCCESS;
  957. }
  958. NTSTATUS
  959. PptGetPortNumberFromLptName(
  960. IN PWSTR PortName,
  961. OUT PULONG PortNumber
  962. )
  963. /*++
  964. Routine Description:
  965. Verify that the LptName is of the form LPTn, if so then return
  966. the integer value of n
  967. Arguments:
  968. PortName - the PortName extracted from the devnode - expected to be
  969. of the form: "LPTn"
  970. PortNumber - points to the UNLONG that will hold the result on success
  971. Return Value:
  972. STATUS_SUCCESS on success - *PortNumber will contain the integer value of n
  973. error status otherwise
  974. --*/
  975. {
  976. NTSTATUS status;
  977. UNICODE_STRING str;
  978. //
  979. // Verify that the PortName looks like LPTx where x is a number
  980. //
  981. if( PortName[0] != L'L' || PortName[1] != L'P' || PortName[2] != L'T' ) {
  982. DD(NULL,DDE,"PptGetPortNumberFromLptName - name prefix doesn't look like LPT\n");
  983. return STATUS_UNSUCCESSFUL;
  984. }
  985. //
  986. // prefix is LPT, check for integer suffix with value > 0
  987. //
  988. RtlInitUnicodeString( &str, (PWSTR)&PortName[3] );
  989. status = RtlUnicodeStringToInteger( &str, 10, PortNumber );
  990. if( !NT_SUCCESS( status ) ) {
  991. DD(NULL,DDT,"util::PptGetPortNumberFromLptName - name suffix doesn't look like an integer\n");
  992. return STATUS_UNSUCCESSFUL;
  993. }
  994. if( *PortNumber == 0 ) {
  995. DD(NULL,DDT,"util::PptGetPortNumberFromLptName - name suffix == 0 - FAIL - Invalid value\n");
  996. return STATUS_UNSUCCESSFUL;
  997. }
  998. DD(NULL,DDT,"util::PptGetPortNumberFromLptName - LPT name suffix= %d\n", *PortNumber);
  999. return STATUS_SUCCESS;
  1000. }
  1001. PDEVICE_OBJECT
  1002. PptBuildFdo(
  1003. IN PDRIVER_OBJECT DriverObject,
  1004. IN PDEVICE_OBJECT PhysicalDeviceObject
  1005. )
  1006. /*++
  1007. Routine Description:
  1008. This routine constructs and initializes a parport FDO
  1009. Arguments:
  1010. DriverObject - Pointer to the parport driver object
  1011. PhysicalDeviceObject - Pointer to the PDO whose stack we will attach to
  1012. Return Value:
  1013. Pointer to the new ParPort Device Object on Success
  1014. NULL otherwise
  1015. --*/
  1016. {
  1017. UNICODE_STRING uniNameString = {0,0,0};
  1018. ULONG portNumber = 0;
  1019. PWSTR portName = NULL;
  1020. NTSTATUS status = STATUS_SUCCESS;
  1021. PDEVICE_OBJECT deviceObject = NULL;
  1022. //
  1023. // Get the LPTx name for this port from the registry.
  1024. //
  1025. // The initial LPTx name for a port is determined by the ports class installer
  1026. // msports.dll, but the name can subsequently be changed by the user via
  1027. // a device manager property page.
  1028. //
  1029. portName = PptGetPortNameFromPhysicalDeviceObject( PhysicalDeviceObject );
  1030. if( NULL == portName ) {
  1031. DD(NULL,DDE,"PptBuildFdo - get LPTx Name from registry - FAILED\n");
  1032. goto targetExit;
  1033. }
  1034. //
  1035. // Extract the preferred port number N to use for the \Device\ParallelPortN
  1036. // DeviceName from the LPTx name
  1037. //
  1038. // Preferred DeviceName for LPT(n) is ParallelPort(n-1) - e.g., LPT3 -> ParallelPort2
  1039. //
  1040. status = PptGetPortNumberFromLptName( portName, &portNumber );
  1041. if( !NT_SUCCESS( status ) ) {
  1042. DD(NULL,DDE,"PptBuildFdo - extract portNumber from LPTx Name - FAILED\n");
  1043. ExFreePool( portName );
  1044. goto targetExit;
  1045. }
  1046. --portNumber; // convert 1 (LPT) based number to 0 (ParallelPort) based number
  1047. //
  1048. // Build a DeviceName of the form: \Device\ParallelPortN
  1049. //
  1050. status = PptBuildParallelPortDeviceName(portNumber, &uniNameString);
  1051. if( !NT_SUCCESS( status ) ) {
  1052. // we couldn't make up a name - bail out
  1053. DD(NULL,DDE,"PptBuildFdo - Build ParallelPort DeviceName - FAILED\n");
  1054. ExFreePool( portName );
  1055. goto targetExit;
  1056. }
  1057. //
  1058. // Create the device object for this device.
  1059. //
  1060. status = IoCreateDevice(DriverObject, sizeof(FDO_EXTENSION), &uniNameString,
  1061. FILE_DEVICE_PARALLEL_PORT, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject);
  1062. if( STATUS_OBJECT_NAME_COLLISION == status ) {
  1063. //
  1064. // Preferred DeviceName already exists - try made up names
  1065. //
  1066. DD(NULL,DDW,"PptBuildFdo - Initial Device Creation FAILED - Name Collision\n");
  1067. //
  1068. // use an offset so that our made up names won't collide with
  1069. // the preferred names of ports that have not yet started
  1070. // (start with ParallelPort8)
  1071. //
  1072. #define PPT_CLASSNAME_OFFSET 7
  1073. portNumber = PPT_CLASSNAME_OFFSET;
  1074. do {
  1075. RtlFreeUnicodeString( &uniNameString );
  1076. ++portNumber;
  1077. status = PptBuildParallelPortDeviceName(portNumber, &uniNameString);
  1078. if( !NT_SUCCESS( status ) ) {
  1079. // we couldn't make up a name - bail out
  1080. DD(NULL,DDE,"PptBuildFdo - Build ParallelPort DeviceName - FAILED\n");
  1081. ExFreePool( portName );
  1082. goto targetExit;
  1083. }
  1084. DD(NULL,DDT,"PptBuildFdo - Trying Device Creation <%wZ>\n", &uniNameString);
  1085. status = IoCreateDevice(DriverObject, sizeof(FDO_EXTENSION), &uniNameString,
  1086. FILE_DEVICE_PARALLEL_PORT, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject);
  1087. } while( STATUS_OBJECT_NAME_COLLISION == status );
  1088. }
  1089. if( !NT_SUCCESS( status ) ) {
  1090. // we got a failure other than a name collision - bail out
  1091. DD(NULL,DDE,"PptBuildFdo - Device Creation FAILED - status=%x\n",status);
  1092. deviceObject = NULL;
  1093. ExFreePool( portName );
  1094. goto targetExit;
  1095. }
  1096. //
  1097. // We have a deviceObject - Initialize DeviceExtension
  1098. //
  1099. status = PptInitializeDeviceExtension( DriverObject, PhysicalDeviceObject, deviceObject,
  1100. &uniNameString, portName, portNumber );
  1101. if( !NT_SUCCESS( status ) ) {
  1102. // failure initializing the device extension - clean up and bail out
  1103. DD(NULL,DDE,"PptBuildFdo - Device Initialization FAILED - status=%x\n",status);
  1104. IoDeleteDevice( deviceObject );
  1105. deviceObject = NULL;
  1106. ExFreePool( portName );
  1107. goto targetExit;
  1108. }
  1109. //
  1110. // Propagate the power pagable flag of the PDO to our new FDO
  1111. //
  1112. if( PhysicalDeviceObject->Flags & DO_POWER_PAGABLE ) {
  1113. deviceObject->Flags |= DO_POWER_PAGABLE;
  1114. }
  1115. DD(NULL,DDT,"PptBuildFdo - SUCCESS\n");
  1116. targetExit:
  1117. RtlFreeUnicodeString( &uniNameString );
  1118. return deviceObject;
  1119. }
  1120. VOID
  1121. PptPdoGetPortInfoFromFdo( PDEVICE_OBJECT Pdo )
  1122. {
  1123. PPDO_EXTENSION pdx = Pdo->DeviceExtension;
  1124. PDEVICE_OBJECT fdo = pdx->Fdo;
  1125. PFDO_EXTENSION fdx = fdo->DeviceExtension;
  1126. pdx->OriginalController = fdx->PortInfo.OriginalController;
  1127. pdx->Controller = fdx->PortInfo.Controller;
  1128. pdx->SpanOfController = fdx->PortInfo.SpanOfController;
  1129. pdx->TryAllocatePort = fdx->PortInfo.TryAllocatePort;
  1130. pdx->FreePort = fdx->PortInfo.FreePort;
  1131. pdx->QueryNumWaiters = fdx->PortInfo.QueryNumWaiters;
  1132. pdx->PortContext = fdx->PortInfo.Context;
  1133. pdx->EcrController = fdx->PnpInfo.EcpController;
  1134. pdx->HardwareCapabilities = fdx->PnpInfo.HardwareCapabilities;
  1135. pdx->TrySetChipMode = fdx->PnpInfo.TrySetChipMode;
  1136. pdx->ClearChipMode = fdx->PnpInfo.ClearChipMode;
  1137. pdx->TrySelectDevice = fdx->PnpInfo.TrySelectDevice;
  1138. pdx->DeselectDevice = fdx->PnpInfo.DeselectDevice;
  1139. pdx->FifoDepth = fdx->PnpInfo.FifoDepth;
  1140. pdx->FifoWidth = fdx->PnpInfo.FifoWidth;
  1141. }
  1142. VOID
  1143. P4WritePortNameToDevNode( PDEVICE_OBJECT Pdo, PCHAR Location )
  1144. {
  1145. HANDLE handle;
  1146. NTSTATUS status;
  1147. WCHAR portName[8]; // expect: L"LPTx:" (L"LPTx.y:" for DaisyChain PDOs)
  1148. PPDO_EXTENSION pdx = Pdo->DeviceExtension;
  1149. RtlZeroMemory( portName, sizeof(portName) );
  1150. PptAssert( NULL != Location );
  1151. switch( pdx->PdoType ) {
  1152. case PdoTypeLegacyZip:
  1153. case PdoTypeDaisyChain:
  1154. // At least one vendor uses the y from LPTx.y to determine the
  1155. // location of their device in the 1284.3 daisy chain. We
  1156. // have chastised this vendor for using this undocumented
  1157. // interface and they have apologized profusely and promised
  1158. // to try to avoid using undocumented interfaces in the future
  1159. // (at least without telling us that they are doing so).
  1160. _snwprintf( portName, sizeof(portName)/sizeof(WCHAR), L"%.6S:\0", Location );
  1161. PptAssert( 7 == wcslen(portName) );
  1162. break;
  1163. case PdoTypeRawPort:
  1164. case PdoTypeEndOfChain:
  1165. // don't confuse printing with the .4 suffix for an EndOfChain device
  1166. _snwprintf( portName, sizeof(portName)/sizeof(WCHAR), L"%.4S:\0", Location );
  1167. PptAssert( 5 == wcslen(portName) );
  1168. break;
  1169. default:
  1170. DD((PCE)pdx,DDE,"P4WritePortNameToDevNode - invalid pdx->PdoType\n");
  1171. }
  1172. PptAssert( wcsncmp( portName, L"LPT", sizeof(L"LPT")/sizeof(WCHAR)) ) ;
  1173. status = IoOpenDeviceRegistryKey( Pdo, PLUGPLAY_REGKEY_DEVICE, KEY_ALL_ACCESS, &handle );
  1174. if( STATUS_SUCCESS == status ) {
  1175. UNICODE_STRING name;
  1176. RtlInitUnicodeString( &name, L"PortName" );
  1177. ZwSetValueKey( handle, &name, 0, REG_SZ, portName, (wcslen(portName)+1)*sizeof(WCHAR) );
  1178. ZwClose(handle);
  1179. }
  1180. }
  1181. PCHAR
  1182. P4ReadRawIeee1284DeviceId(
  1183. IN PUCHAR Controller
  1184. )
  1185. {
  1186. IEEE_STATE ieeeState = { 0, // CurrentEvent
  1187. PHASE_FORWARD_IDLE, // CurrentPhase
  1188. FALSE, // Connected in IEEE mode?
  1189. FALSE, // IsIeeeTerminateOk
  1190. FAMILY_NONE }; // ProtocolFamily - Centronics => FAMILY_NONE
  1191. NTSTATUS status;
  1192. PCHAR devIdBuffer = NULL;
  1193. ULONG bytesTransferred = 0;
  1194. ULONG tryCount = 1;
  1195. const ULONG maxTries = 3;
  1196. const ULONG minValidDevId = 14; // 2 size bytes + "MFG:x;" + "MDL:y;"
  1197. BOOLEAN ignoreXflag = FALSE;
  1198. ULONG deviceIndex;
  1199. targetRetry:
  1200. status = P4IeeeEnter1284Mode( Controller, ( NIBBLE_EXTENSIBILITY | DEVICE_ID_REQ ), &ieeeState );
  1201. if( STATUS_SUCCESS == status ) {
  1202. // Negotiation for 1284 device ID succeeded
  1203. const ULONG tmpBufLen = 1024; // reasonable max length for IEEE 1284 Device ID string
  1204. PCHAR tmpBuf = ExAllocatePool( PagedPool, tmpBufLen );
  1205. if( tmpBuf ) {
  1206. RtlZeroMemory( tmpBuf, tmpBufLen );
  1207. // try to read the 1284 device ID from the peripheral
  1208. ieeeState.CurrentPhase = PHASE_NEGOTIATION;
  1209. status = P4NibbleModeRead( Controller, tmpBuf, tmpBufLen-1, &bytesTransferred, &ieeeState );
  1210. if( NT_SUCCESS( status ) ) {
  1211. UCHAR highLengthByte = 0xff & tmpBuf[0];
  1212. UCHAR lowLengthByte = 0xff & tmpBuf[1];
  1213. PCHAR idString = &tmpBuf[2];
  1214. DD(NULL,DDT,"P4ReadRawIeee1284DeviceId - len:%02x %02x - string:<%s>\n",highLengthByte,lowLengthByte,idString);
  1215. if( highLengthByte > 2 ) {
  1216. DD(NULL,DDT,"P4ReadRawIeee1284DeviceId - len:%02x %02x - looks bogus - ignore this ID\n",highLengthByte,lowLengthByte);
  1217. devIdBuffer = NULL;
  1218. } else {
  1219. if( bytesTransferred >= minValidDevId ) {
  1220. // looks like this might be a valid 1284 id
  1221. devIdBuffer = ExAllocatePool( PagedPool, bytesTransferred + 1 );
  1222. if( devIdBuffer ) {
  1223. ULONG length = (highLengthByte * 256) + lowLengthByte;
  1224. ULONG truncationIndex = ( (length >= minValidDevId) && (length < bytesTransferred) ) ? length : bytesTransferred;
  1225. RtlCopyMemory( devIdBuffer, tmpBuf, bytesTransferred );
  1226. devIdBuffer[ truncationIndex ] = '\0';
  1227. } else {
  1228. DD(NULL,DDT,"P4ReadRawIeee1284DeviceId - P4IeeeEnter1284Mode FAILED - no pool for devIdBuffer\n");
  1229. }
  1230. }
  1231. }
  1232. } else {
  1233. DD(NULL,DDT,"P4ReadRawIeee1284DeviceId - P4NibbleModeRead FAILED - looks like no device there\n");
  1234. }
  1235. ExFreePool( tmpBuf );
  1236. } else {
  1237. DD(NULL,DDT,"P4ReadRawIeee1284DeviceId - P4IeeeEnter1284Mode FAILED - no pool for tmpBuf\n");
  1238. }
  1239. ieeeState.ProtocolFamily = FAMILY_REVERSE_NIBBLE;
  1240. //check brother product
  1241. if(devIdBuffer &&
  1242. ( strstr(devIdBuffer+2,"Brother") ||
  1243. strstr(devIdBuffer+2,"PitneyBowes") ||
  1244. strstr(devIdBuffer+2,"LEGEND") ||
  1245. strstr(devIdBuffer+2,"Legend") ||
  1246. strstr(devIdBuffer+2,"HBP") ))
  1247. {
  1248. // look for device that needs to ignore XFlag on event 24
  1249. for(deviceIndex = 0; deviceIndex < NUMOFBROTHERPRODUCT;
  1250. deviceIndex++)
  1251. {
  1252. if(XflagOnEvent24Devices[deviceIndex][0] == NULL)
  1253. {
  1254. break;
  1255. }
  1256. if(strstr(devIdBuffer+2,
  1257. XflagOnEvent24Devices[deviceIndex][IDMFG] ) )
  1258. {
  1259. if(strstr(devIdBuffer+2,
  1260. XflagOnEvent24Devices[deviceIndex][IDMDL] ) )
  1261. {
  1262. // found a match, so set our flag and get out
  1263. ignoreXflag = TRUE;
  1264. break;
  1265. }
  1266. }
  1267. }
  1268. }
  1269. if(ignoreXflag)
  1270. {
  1271. // work around Brother's firmware handling of XFlag on Event 24
  1272. P4IeeeTerminate1284Mode( Controller, &ieeeState, IgnoreXFlagOnEvent24 );
  1273. } else {
  1274. // normal handling
  1275. P4IeeeTerminate1284Mode( Controller, &ieeeState, UseXFlagOnEvent24 );
  1276. }
  1277. } else {
  1278. DD(NULL,DDT,"P4ReadRawIeee1284DeviceId - P4IeeeEnter1284Mode FAILED - looks like no device there\n");
  1279. }
  1280. //
  1281. // add retry if we got some bytes, but not enough for a valid ID
  1282. //
  1283. if( (NULL == devIdBuffer) && // we didn't get an ID
  1284. (bytesTransferred > 0 ) && // peripheral reported some bytes
  1285. (bytesTransferred < minValidDevId ) && // but not enough
  1286. (tryCount < maxTries ) ) { // we haven't exhausted our retries
  1287. ++tryCount;
  1288. bytesTransferred = 0;
  1289. goto targetRetry;
  1290. }
  1291. return devIdBuffer;
  1292. }
  1293. VOID
  1294. P4ReleaseBus( PDEVICE_OBJECT Fdo )
  1295. {
  1296. PFDO_EXTENSION fdx = Fdo->DeviceExtension;
  1297. DD((PCE)fdx,DDT,"P4ReleaseBus\n");
  1298. fdx->FdoWaitingOnPort = FALSE;
  1299. if( 0 == d3 ) {
  1300. PptFreePort( fdx );
  1301. }
  1302. }
  1303. NTSTATUS
  1304. P4CompleteRequest(
  1305. IN PIRP Irp,
  1306. IN NTSTATUS Status,
  1307. IN ULONG_PTR Information
  1308. )
  1309. {
  1310. P5TraceIrpCompletion( Irp );
  1311. Irp->IoStatus.Status = Status;
  1312. Irp->IoStatus.Information = Information;
  1313. IoCompleteRequest( Irp, IO_NO_INCREMENT );
  1314. return Status;
  1315. }
  1316. NTSTATUS
  1317. P4CompleteRequestReleaseRemLock(
  1318. IN PIRP Irp,
  1319. IN NTSTATUS Status,
  1320. IN ULONG_PTR Information,
  1321. IN PIO_REMOVE_LOCK RemLock
  1322. )
  1323. {
  1324. P4CompleteRequest( Irp, Status, Information );
  1325. PptReleaseRemoveLock( RemLock, Irp );
  1326. return Status;
  1327. }
  1328. // pcutil.c follows:
  1329. //============================================================================
  1330. // NAME: BusReset()
  1331. //
  1332. // Performs a bus reset as defined in Chapter 7.2 of the
  1333. // 1284-1994 spec.
  1334. //
  1335. // PARAMETERS:
  1336. // DCRController - Supplies the base address of of the DCR.
  1337. //
  1338. // RETURNS:
  1339. // nothing
  1340. //============================================================================
  1341. void BusReset(
  1342. IN PUCHAR DCRController
  1343. )
  1344. {
  1345. UCHAR dcr;
  1346. dcr = P5ReadPortUchar(DCRController);
  1347. // Set 1284 and nInit low.
  1348. dcr = UPDATE_DCR(dcr, DONT_CARE, DONT_CARE, INACTIVE, INACTIVE, DONT_CARE, DONT_CARE);
  1349. P5WritePortUchar(DCRController, dcr);
  1350. KeStallExecutionProcessor(100); // Legacy Zip will hold what looks to be
  1351. // a bus reset for 9us. Since this proc is used
  1352. // to trigger a logic analyzer... let's hold
  1353. // for 100us
  1354. }
  1355. BOOLEAN
  1356. CheckTwoPorts(
  1357. PUCHAR pPortAddr1,
  1358. UCHAR bMask1,
  1359. UCHAR bValue1,
  1360. PUCHAR pPortAddr2,
  1361. UCHAR bMask2,
  1362. UCHAR bValue2,
  1363. USHORT msTimeDelay
  1364. )
  1365. {
  1366. UCHAR bPort1;
  1367. UCHAR bPort2;
  1368. LARGE_INTEGER Wait;
  1369. LARGE_INTEGER Start;
  1370. LARGE_INTEGER End;
  1371. // Do a quick check in case we have one stinkingly fast peripheral!
  1372. bPort1 = P5ReadPortUchar( pPortAddr1 );
  1373. if ( ( bPort1 & bMask1 ) == bValue1 ) {
  1374. return TRUE;
  1375. }
  1376. bPort2 = P5ReadPortUchar( pPortAddr2 );
  1377. if ( ( bPort2 & bMask2 ) == bValue2 ) {
  1378. return FALSE;
  1379. }
  1380. Wait.QuadPart = (msTimeDelay * 10 * 1000) + KeQueryTimeIncrement();
  1381. KeQueryTickCount(&Start);
  1382. for(;;) {
  1383. KeQueryTickCount(&End);
  1384. bPort1 = P5ReadPortUchar( pPortAddr1 );
  1385. if ( ( bPort1 & bMask1 ) == bValue1 ) {
  1386. return TRUE;
  1387. }
  1388. bPort2 = P5ReadPortUchar( pPortAddr2 );
  1389. if ( ( bPort2 & bMask2 ) == bValue2 ) {
  1390. return FALSE;
  1391. }
  1392. if ((End.QuadPart - Start.QuadPart) * KeQueryTimeIncrement() > Wait.QuadPart) {
  1393. // We timed out!!! - Recheck the values
  1394. bPort1 = P5ReadPortUchar( pPortAddr1 );
  1395. if ( ( bPort1 & bMask1 ) == bValue1 ) {
  1396. return TRUE;
  1397. }
  1398. bPort2 = P5ReadPortUchar( pPortAddr2 );
  1399. if ( ( bPort2 & bMask2 ) == bValue2 ) {
  1400. return FALSE;
  1401. }
  1402. #if DVRH_BUS_RESET_ON_ERROR
  1403. BusReset(pPortAddr1+1); // Pass in the dcr address
  1404. #endif
  1405. // Device never responded, return timeout status.
  1406. return FALSE;
  1407. }
  1408. } // forever;
  1409. } // CheckPort2...
  1410. PWSTR
  1411. ParCreateWideStringFromUnicodeString(PUNICODE_STRING UnicodeString)
  1412. /*++
  1413. Routine Description:
  1414. Create a UNICODE_NULL terminated WSTR given a UNICODE_STRING.
  1415. This function allocates PagedPool, copies the UNICODE_STRING buffer
  1416. to the allocation, and appends a UNICODE_NULL to terminate the WSTR
  1417. *** This function allocates pool. ExFreePool must be called to free
  1418. the allocation when the buffer is no longer needed.
  1419. Arguments:
  1420. UnicodeString - The source
  1421. Return Value:
  1422. PWSTR - if successful
  1423. NULL - otherwise
  1424. --*/
  1425. {
  1426. PWSTR buffer;
  1427. ULONG length = UnicodeString->Length;
  1428. buffer = ExAllocatePool( PagedPool, length + sizeof(UNICODE_NULL) );
  1429. if(!buffer) {
  1430. return NULL; // unable to allocate pool, bail out
  1431. } else {
  1432. RtlCopyMemory(buffer, UnicodeString->Buffer, length);
  1433. buffer[length/2] = UNICODE_NULL;
  1434. return buffer;
  1435. }
  1436. }
  1437. VOID
  1438. ParInitializeExtension1284Info(
  1439. IN PPDO_EXTENSION Pdx
  1440. )
  1441. // make this a function since it is now called from two places:
  1442. // - 1) when initializing a new devobj
  1443. // - 2) from CreateOpen
  1444. {
  1445. USHORT i;
  1446. Pdx->Connected = FALSE;
  1447. if (DefaultModes)
  1448. {
  1449. USHORT rev = (USHORT) (DefaultModes & 0xffff);
  1450. USHORT fwd = (USHORT)((DefaultModes & 0xffff0000)>>16);
  1451. switch (fwd)
  1452. {
  1453. case BOUNDED_ECP:
  1454. Pdx->IdxForwardProtocol = BOUNDED_ECP_FORWARD;
  1455. break;
  1456. case ECP_HW_NOIRQ:
  1457. case ECP_HW_IRQ:
  1458. Pdx->IdxForwardProtocol = ECP_HW_FORWARD_NOIRQ;
  1459. break;
  1460. case ECP_SW:
  1461. Pdx->IdxForwardProtocol = ECP_SW_FORWARD;
  1462. break;
  1463. case EPP_HW:
  1464. Pdx->IdxForwardProtocol = EPP_HW_FORWARD;
  1465. break;
  1466. case EPP_SW:
  1467. Pdx->IdxForwardProtocol = EPP_SW_FORWARD;
  1468. break;
  1469. case IEEE_COMPATIBILITY:
  1470. Pdx->IdxForwardProtocol = IEEE_COMPAT_MODE;
  1471. break;
  1472. case CENTRONICS:
  1473. default:
  1474. Pdx->IdxForwardProtocol = CENTRONICS_MODE;
  1475. break;
  1476. }
  1477. switch (rev)
  1478. {
  1479. case BOUNDED_ECP:
  1480. Pdx->IdxReverseProtocol = BOUNDED_ECP_REVERSE;
  1481. break;
  1482. case ECP_HW_NOIRQ:
  1483. case ECP_HW_IRQ:
  1484. Pdx->IdxReverseProtocol = ECP_HW_REVERSE_NOIRQ;
  1485. break;
  1486. case ECP_SW:
  1487. Pdx->IdxReverseProtocol = ECP_SW_REVERSE;
  1488. break;
  1489. case EPP_HW:
  1490. Pdx->IdxReverseProtocol = EPP_HW_REVERSE;
  1491. break;
  1492. case EPP_SW:
  1493. Pdx->IdxReverseProtocol = EPP_SW_REVERSE;
  1494. break;
  1495. case BYTE_BIDIR:
  1496. Pdx->IdxReverseProtocol = BYTE_MODE;
  1497. break;
  1498. case CHANNEL_NIBBLE:
  1499. case NIBBLE:
  1500. default:
  1501. Pdx->IdxReverseProtocol = NIBBLE_MODE;
  1502. break;
  1503. }
  1504. }
  1505. else
  1506. {
  1507. Pdx->IdxReverseProtocol = NIBBLE_MODE;
  1508. Pdx->IdxForwardProtocol = CENTRONICS_MODE;
  1509. }
  1510. Pdx->bShadowBuffer = FALSE;
  1511. Pdx->ProtocolModesSupported = 0;
  1512. Pdx->BadProtocolModes = 0;
  1513. Pdx->fnRead = NULL;
  1514. Pdx->fnWrite = NULL;
  1515. Pdx->ForwardInterfaceAddress = DEFAULT_ECP_CHANNEL;
  1516. Pdx->ReverseInterfaceAddress = DEFAULT_ECP_CHANNEL;
  1517. Pdx->SetForwardAddress = FALSE;
  1518. Pdx->SetReverseAddress = FALSE;
  1519. Pdx->bIsHostRecoverSupported = FALSE;
  1520. Pdx->IsIeeeTerminateOk = FALSE;
  1521. for (i = FAMILY_NONE; i < FAMILY_MAX; i++) {
  1522. Pdx->ProtocolData[i] = 0;
  1523. }
  1524. }
  1525. NTSTATUS
  1526. ParBuildSendInternalIoctl(
  1527. IN ULONG IoControlCode,
  1528. IN PDEVICE_OBJECT TargetDeviceObject,
  1529. IN PVOID InputBuffer OPTIONAL,
  1530. IN ULONG InputBufferLength,
  1531. OUT PVOID OutputBuffer OPTIONAL,
  1532. IN ULONG OutputBufferLength,
  1533. IN PLARGE_INTEGER RequestedTimeout OPTIONAL
  1534. )
  1535. /*++dvdf
  1536. Routine Description:
  1537. This routine builds and sends an Internal IOCTL to the TargetDeviceObject, waits
  1538. for the IOCTL to complete, and returns status to the caller.
  1539. *** WORKWORK - dvdf 12Dec98: This function does not support Input and Output in the same IOCTL
  1540. Arguments:
  1541. IoControlCode - the IOCTL to send
  1542. TargetDeviceObject - who to send the IOCTL to
  1543. InputBuffer - pointer to input buffer, if any
  1544. InputBufferLength, - length of input buffer
  1545. OutputBuffer - pointer to output buffer, if any
  1546. OutputBufferLength, - length of output buffer
  1547. Timeout - how long to wait for request to complete, NULL==use driver global AcquirePortTimeout
  1548. Return Value:
  1549. Status
  1550. --*/
  1551. {
  1552. NTSTATUS status;
  1553. PIRP irp;
  1554. LARGE_INTEGER timeout;
  1555. KEVENT event;
  1556. PIO_STACK_LOCATION irpSp;
  1557. PAGED_CODE();
  1558. //
  1559. // Current limitation is that this function does not handle a request with
  1560. // both InputBufferLength and OutputBufferLength > 0
  1561. //
  1562. if( InputBufferLength != 0 && OutputBufferLength != 0 ) {
  1563. return STATUS_UNSUCCESSFUL;
  1564. }
  1565. //
  1566. // Allocate and initialize IRP
  1567. //
  1568. irp = IoAllocateIrp( (CCHAR)(TargetDeviceObject->StackSize + 1), FALSE );
  1569. if( !irp ) {
  1570. return STATUS_INSUFFICIENT_RESOURCES;
  1571. }
  1572. irpSp = IoGetNextIrpStackLocation( irp );
  1573. irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  1574. irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
  1575. irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
  1576. irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
  1577. if( InputBufferLength != 0 ) {
  1578. irp->AssociatedIrp.SystemBuffer = InputBuffer;
  1579. } else if( OutputBufferLength != 0 ) {
  1580. irp->AssociatedIrp.SystemBuffer = OutputBuffer;
  1581. }
  1582. //
  1583. // Set completion routine and send IRP
  1584. //
  1585. KeInitializeEvent( &event, NotificationEvent, FALSE );
  1586. IoSetCompletionRoutine( irp, ParSynchCompletionRoutine, &event, TRUE, TRUE, TRUE );
  1587. status = ParCallDriver(TargetDeviceObject, irp);
  1588. if( !NT_SUCCESS(status) ) {
  1589. DD(NULL,DDE,"ParBuildSendInternalIoctl - ParCallDriver FAILED w/status=%x\n",status);
  1590. IoFreeIrp( irp );
  1591. return status;
  1592. }
  1593. //
  1594. // Set timeout and wait
  1595. //
  1596. // user specified : default
  1597. timeout = (NULL != RequestedTimeout) ? *RequestedTimeout : AcquirePortTimeout;
  1598. status = KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout);
  1599. //
  1600. // Did we timeout or did the IRP complete?
  1601. //
  1602. if( status == STATUS_TIMEOUT ) {
  1603. // we timed out - cancel the IRP
  1604. IoCancelIrp( irp );
  1605. KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
  1606. }
  1607. //
  1608. // Irp is complete, grab the status and free the irp
  1609. //
  1610. status = irp->IoStatus.Status;
  1611. IoFreeIrp( irp );
  1612. return status;
  1613. }
  1614. UCHAR
  1615. ParInitializeDevice(
  1616. IN PPDO_EXTENSION Pdx
  1617. )
  1618. /*++
  1619. Routine Description:
  1620. This routine is invoked to initialize the parallel port drive.
  1621. It performs the following actions:
  1622. o Send INIT to the driver and if the device is online.
  1623. Arguments:
  1624. Context - Really the device extension.
  1625. Return Value:
  1626. The last value that we got from the status register.
  1627. --*/
  1628. {
  1629. KIRQL OldIrql;
  1630. UCHAR DeviceStatus = 0;
  1631. LARGE_INTEGER StartOfSpin = {0,0};
  1632. LARGE_INTEGER NextQuery = {0,0};
  1633. LARGE_INTEGER Difference = {0,0};
  1634. //
  1635. // Tim Wells (WestTek, L.L.C.)
  1636. //
  1637. // - Removed the deferred initialization code from DriverEntry, device creation
  1638. // code. This code will be better utilized in the Create/Open logic or from
  1639. // the calling application.
  1640. //
  1641. // - Changed this code to always reset when asked, and to return after a fixed
  1642. // interval reqardless of the response. Additional responses can be provided by
  1643. // read and write code.
  1644. //
  1645. //
  1646. // Clear the register.
  1647. //
  1648. if (GetControl(Pdx->Controller) & PAR_CONTROL_NOT_INIT) {
  1649. //
  1650. // We should stall for at least 60 microseconds after the init.
  1651. //
  1652. KeRaiseIrql( DISPATCH_LEVEL, &OldIrql );
  1653. StoreControl( Pdx->Controller, (UCHAR)(PAR_CONTROL_WR_CONTROL | PAR_CONTROL_SLIN) );
  1654. KeStallExecutionProcessor(60);
  1655. KeLowerIrql(OldIrql);
  1656. }
  1657. StoreControl( Pdx->Controller,
  1658. (UCHAR)(PAR_CONTROL_WR_CONTROL | PAR_CONTROL_NOT_INIT | PAR_CONTROL_SLIN) );
  1659. //
  1660. // Spin waiting for the device to initialize.
  1661. //
  1662. KeQueryTickCount(&StartOfSpin);
  1663. do {
  1664. KeQueryTickCount(&NextQuery);
  1665. Difference.QuadPart = NextQuery.QuadPart - StartOfSpin.QuadPart;
  1666. ASSERT(KeQueryTimeIncrement() <= MAXLONG);
  1667. if (Difference.QuadPart*KeQueryTimeIncrement() >= Pdx->AbsoluteOneSecond.QuadPart) {
  1668. //
  1669. // Give up on getting PAR_OK.
  1670. //
  1671. DD((PCE)Pdx,DDT,"Did spin of one second - StartOfSpin: %x NextQuery: %x\n", StartOfSpin.LowPart,NextQuery.LowPart);
  1672. break;
  1673. }
  1674. DeviceStatus = GetStatus(Pdx->Controller);
  1675. } while (!PAR_OK(DeviceStatus));
  1676. return (DeviceStatus);
  1677. }
  1678. VOID
  1679. ParNotInitError(
  1680. IN PPDO_EXTENSION Pdx,
  1681. IN UCHAR DeviceStatus
  1682. )
  1683. /*++
  1684. Routine Description:
  1685. Arguments:
  1686. Pdx - Supplies the device extension.
  1687. deviceStatus - Last read status.
  1688. Return Value:
  1689. None.
  1690. --*/
  1691. {
  1692. PIRP Irp = Pdx->CurrentOpIrp;
  1693. if (PAR_OFF_LINE(DeviceStatus)) {
  1694. Irp->IoStatus.Status = STATUS_DEVICE_OFF_LINE;
  1695. DD((PCE)Pdx,DDE,"Init Error - off line - STATUS/INFORMATON: %x/%x\n", Irp->IoStatus.Status, Irp->IoStatus.Information);
  1696. } else if (PAR_NO_CABLE(DeviceStatus)) {
  1697. Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
  1698. DD((PCE)Pdx,DDE,"Init Error - no cable - not connected - STATUS/INFORMATON: %x/%x\n", Irp->IoStatus.Status, Irp->IoStatus.Information);
  1699. } else if (PAR_PAPER_EMPTY(DeviceStatus)) {
  1700. Irp->IoStatus.Status = STATUS_DEVICE_PAPER_EMPTY;
  1701. DD((PCE)Pdx,DDE,"Init Error - paper empty - STATUS/INFORMATON: %x/%x\n", Irp->IoStatus.Status, Irp->IoStatus.Information);
  1702. } else if (PAR_POWERED_OFF(DeviceStatus)) {
  1703. Irp->IoStatus.Status = STATUS_DEVICE_POWERED_OFF;
  1704. DD((PCE)Pdx,DDE,"Init Error - power off - STATUS/INFORMATON: %x/%x\n", Irp->IoStatus.Status, Irp->IoStatus.Information);
  1705. } else {
  1706. Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
  1707. DD((PCE)Pdx,DDE,"Init Error - not conn - STATUS/INFORMATON: %x/%x\n", Irp->IoStatus.Status, Irp->IoStatus.Information);
  1708. }
  1709. }
  1710. VOID
  1711. ParCancelRequest(
  1712. PDEVICE_OBJECT DevObj,
  1713. PIRP Irp
  1714. )
  1715. /*++
  1716. Routine Description:
  1717. This routine is used to cancel any request in the parallel driver.
  1718. Arguments:
  1719. DevObj - Pointer to the device object for this device
  1720. Irp - Pointer to the IRP to be canceled.
  1721. Return Value:
  1722. None.
  1723. --*/
  1724. {
  1725. UNREFERENCED_PARAMETER( DevObj );
  1726. //
  1727. // The only reason that this irp can be on the queue is
  1728. // if it's not the current irp. Pull it off the queue
  1729. // and complete it as canceled.
  1730. //
  1731. ASSERT(!IsListEmpty(&Irp->Tail.Overlay.ListEntry));
  1732. RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
  1733. IoReleaseCancelSpinLock(Irp->CancelIrql);
  1734. P4CompleteRequest( Irp, STATUS_CANCELLED, 0 );
  1735. }
  1736. #if PAR_NO_FAST_CALLS
  1737. // temp debug functions so params show up on stack trace
  1738. NTSTATUS
  1739. ParCallDriver(
  1740. IN PDEVICE_OBJECT DeviceObject,
  1741. IN OUT PIRP Irp
  1742. )
  1743. {
  1744. return IoCallDriver(DeviceObject, Irp);
  1745. }
  1746. #endif // PAR_NO_FAST_CALLS
  1747. NTSTATUS
  1748. ParSynchCompletionRoutine(
  1749. IN PDEVICE_OBJECT DeviceObject,
  1750. IN PIRP Irp,
  1751. IN PKEVENT Event
  1752. )
  1753. /*++
  1754. Routine Description:
  1755. This routine is for use with synchronous IRP processing.
  1756. All it does is signal an event, so the driver knows it
  1757. can continue.
  1758. Arguments:
  1759. DriverObject - Pointer to driver object created by system.
  1760. Irp - Irp that just completed
  1761. Event - Event we'll signal to say Irp is done
  1762. Return Value:
  1763. None.
  1764. --*/
  1765. {
  1766. UNREFERENCED_PARAMETER( DeviceObject );
  1767. UNREFERENCED_PARAMETER( Irp );
  1768. KeSetEvent(Event, 0, FALSE);
  1769. return STATUS_MORE_PROCESSING_REQUIRED;
  1770. }
  1771. VOID
  1772. ParCheckParameters(
  1773. IN OUT PPDO_EXTENSION Pdx
  1774. )
  1775. /*++
  1776. Routine Description:
  1777. This routine reads the parameters section of the registry and modifies
  1778. the device extension as specified by the parameters.
  1779. Arguments:
  1780. RegistryPath - Supplies the registry path.
  1781. Pdx - Supplies the device extension.
  1782. Return Value:
  1783. None.
  1784. --*/
  1785. {
  1786. RTL_QUERY_REGISTRY_TABLE ParamTable[4];
  1787. ULONG UsePIWriteLoop;
  1788. ULONG UseNT35Priority;
  1789. ULONG Zero = 0;
  1790. NTSTATUS Status;
  1791. HANDLE hRegistry;
  1792. if (Pdx->PhysicalDeviceObject) {
  1793. Status = IoOpenDeviceRegistryKey (Pdx->PhysicalDeviceObject,
  1794. PLUGPLAY_REGKEY_DRIVER,
  1795. STANDARD_RIGHTS_ALL,
  1796. &hRegistry);
  1797. if (NT_SUCCESS(Status)) {
  1798. RtlZeroMemory(ParamTable, sizeof(ParamTable));
  1799. ParamTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
  1800. ParamTable[0].Name = (PWSTR)L"UsePIWriteLoop";
  1801. ParamTable[0].EntryContext = &UsePIWriteLoop;
  1802. ParamTable[0].DefaultType = REG_DWORD;
  1803. ParamTable[0].DefaultData = &Zero;
  1804. ParamTable[0].DefaultLength = sizeof(ULONG);
  1805. ParamTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
  1806. ParamTable[1].Name = (PWSTR)L"UseNT35Priority";
  1807. ParamTable[1].EntryContext = &UseNT35Priority;
  1808. ParamTable[1].DefaultType = REG_DWORD;
  1809. ParamTable[1].DefaultData = &Zero;
  1810. ParamTable[1].DefaultLength = sizeof(ULONG);
  1811. ParamTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
  1812. ParamTable[2].Name = (PWSTR)L"InitializationTimeout";
  1813. ParamTable[2].EntryContext = &(Pdx->InitializationTimeout);
  1814. ParamTable[2].DefaultType = REG_DWORD;
  1815. ParamTable[2].DefaultData = &Zero;
  1816. ParamTable[2].DefaultLength = sizeof(ULONG);
  1817. Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
  1818. hRegistry, ParamTable, NULL, NULL);
  1819. if (NT_SUCCESS(Status)) {
  1820. if(UsePIWriteLoop) {
  1821. Pdx->UsePIWriteLoop = TRUE;
  1822. }
  1823. if(UseNT35Priority) {
  1824. Pdx->UseNT35Priority = TRUE;
  1825. }
  1826. if(Pdx->InitializationTimeout == 0) {
  1827. Pdx->InitializationTimeout = 15;
  1828. }
  1829. }
  1830. } else {
  1831. Pdx->InitializationTimeout = 15;
  1832. }
  1833. ZwClose (hRegistry);
  1834. } else {
  1835. Pdx->InitializationTimeout = 15;
  1836. }
  1837. }
  1838. BOOLEAN
  1839. String2Num(
  1840. IN OUT PCHAR *lpp_Str,
  1841. IN CHAR c,
  1842. OUT ULONG *num
  1843. )
  1844. {
  1845. int cc;
  1846. int cnt = 0;
  1847. DD(NULL,DDT,"String2Num. string [%s]\n", lpp_Str);
  1848. *num = 0;
  1849. if (!*lpp_Str) {
  1850. *num = 0;
  1851. return FALSE;
  1852. }
  1853. // At this point, we should have a string that is a
  1854. // positive hex value. I will not be checking for
  1855. // validity of the string. If peripheral handed me a
  1856. // bogus value then I'm gonna make their life
  1857. // miserable.
  1858. String2Num_Start:
  1859. cc = (int)(unsigned char)**lpp_Str;
  1860. if (cc >= '0' && cc <= '9') {
  1861. *num = 16 * *num + (cc - '0'); /* accumulate digit */
  1862. } else if (cc >= 'A' && cc <= 'F') {
  1863. *num = 16 * *num + (cc - 55); /* accumulate digit */
  1864. } else if (cc >= 'a' && cc <= 'f') {
  1865. *num = 16 * *num + (cc - 87); /* accumulate digit */
  1866. } else if (cc == c || cc == 0) {
  1867. *lpp_Str = 0;
  1868. return TRUE;
  1869. } else if (cc == 'y' || cc == 'Y') {
  1870. *lpp_Str = 0;
  1871. *num = (ULONG)~0; /* Special case */
  1872. return FALSE;
  1873. } else {
  1874. *lpp_Str = 0;
  1875. *num = 0; /* It's all messed up */
  1876. return FALSE;
  1877. }
  1878. DD(NULL,DDT,"String2Num. num [%x]\n", *num);
  1879. (*lpp_Str)++;
  1880. if (cnt++ > 100) {
  1881. // If our string is this large, then I'm gonna assume somethings wrong
  1882. DD(NULL,DDE,"String2Num. String too long\n");
  1883. goto String2Num_End;
  1884. }
  1885. goto String2Num_Start;
  1886. String2Num_End:
  1887. DD(NULL,DDE,"String2Num. Something's wrong with String\n");
  1888. *num = 0;
  1889. return FALSE;
  1890. }
  1891. UCHAR
  1892. StringCountValues(
  1893. IN PCHAR string,
  1894. IN CHAR delimeter
  1895. )
  1896. {
  1897. PUCHAR lpKey = (PUCHAR)string;
  1898. UCHAR cnt = 1;
  1899. if(!string) {
  1900. return 0;
  1901. }
  1902. while(*lpKey) {
  1903. if( *lpKey==delimeter ) {
  1904. ++cnt;
  1905. }
  1906. lpKey++;
  1907. }
  1908. return cnt;
  1909. }
  1910. PCHAR
  1911. StringChr(
  1912. IN PCHAR string,
  1913. IN CHAR c
  1914. )
  1915. {
  1916. if(!string) {
  1917. return(NULL);
  1918. }
  1919. while(*string) {
  1920. if( *string==c ) {
  1921. return string;
  1922. }
  1923. string++;
  1924. }
  1925. return NULL;
  1926. }
  1927. VOID
  1928. StringSubst(
  1929. IN PCHAR lpS,
  1930. IN CHAR chTargetChar,
  1931. IN CHAR chReplacementChar,
  1932. IN USHORT cbS
  1933. )
  1934. {
  1935. USHORT iCnt = 0;
  1936. while ((lpS != '\0') && (iCnt++ < cbS))
  1937. if (*lpS == chTargetChar)
  1938. *lpS++ = chReplacementChar;
  1939. else
  1940. ++lpS;
  1941. }
  1942. VOID
  1943. ParFixupDeviceId(
  1944. IN OUT PUCHAR DeviceId
  1945. )
  1946. /*++
  1947. Routine Description:
  1948. This routine parses the NULL terminated string and replaces any invalid
  1949. characters with an underscore character.
  1950. Invalid characters are:
  1951. c <= 0x20 (' ')
  1952. c > 0x7F
  1953. c == 0x2C (',')
  1954. Arguments:
  1955. DeviceId - specifies a device id string (or part of one), must be
  1956. null-terminated.
  1957. Return Value:
  1958. None.
  1959. --*/
  1960. {
  1961. PUCHAR p;
  1962. for( p = DeviceId; *p; ++p ) {
  1963. if( (*p <= ' ') || (*p > (UCHAR)0x7F) || (*p == ',') ) {
  1964. *p = '_';
  1965. }
  1966. }
  1967. }
  1968. VOID
  1969. ParDetectDot3DataLink(
  1970. IN PPDO_EXTENSION Pdx,
  1971. IN PCHAR DeviceId
  1972. )
  1973. {
  1974. PCHAR DOT3DL = NULL; // 1284.3 Data Link Channels
  1975. PCHAR DOT3C = NULL; // 1284.3 Data Link Services
  1976. PCHAR DOT4DL = NULL; // 1284.4 Data Link for peripherals that were implemented prior to 1284.3
  1977. PCHAR CMDField = NULL; // The command field for parsing legacy MLC
  1978. PCHAR DOT3M = NULL; // 1284 physical layer modes that will break this device
  1979. DD((PCE)Pdx,DDT,"ParDetectDot3DataLink: DeviceId [%s]\n", DeviceId);
  1980. ParDot3ParseDevId(&DOT3DL, &DOT3C, &CMDField, &DOT4DL, &DOT3M, DeviceId);
  1981. ParDot3ParseModes(Pdx,DOT3M);
  1982. if (DOT4DL) {
  1983. DD((PCE)Pdx,DDT,"ParDot3ParseModes - 1284.4 with MLC Data Link Detected. DOT4DL [%s]\n", DOT4DL);
  1984. ParDot4CreateObject(Pdx, DOT4DL);
  1985. } else if (DOT3DL) {
  1986. DD((PCE)Pdx,DDT,"ParDot4CreateObject - 1284.3 Data Link Detected DL:[%s] C:[%s]\n", DOT3DL, DOT3C);
  1987. ParDot3CreateObject(Pdx, DOT3DL, DOT3C);
  1988. } else if (CMDField) {
  1989. DD((PCE)Pdx,DDT,"ParDot3CreateObject - MLC Data Link Detected. MLC [%s]\n", CMDField);
  1990. ParMLCCreateObject(Pdx, CMDField);
  1991. } else {
  1992. DD((PCE)Pdx,DDT,"ParDot3CreateObject - No Data Link Detected\n");
  1993. }
  1994. }
  1995. VOID
  1996. ParDot3ParseDevId(
  1997. PCHAR *lpp_DL,
  1998. PCHAR *lpp_C,
  1999. PCHAR *lpp_CMD,
  2000. PCHAR *lpp_4DL,
  2001. PCHAR *lpp_M,
  2002. PCHAR lpDeviceID
  2003. )
  2004. {
  2005. PCHAR lpKey = lpDeviceID; // Pointer to the Key to look at
  2006. PCHAR lpValue; // Pointer to the Key's value
  2007. USHORT wKeyLength; // Length for the Key (for stringcmps)
  2008. // While there are still keys to look at.
  2009. while (lpKey != NULL) {
  2010. while (*lpKey == ' ')
  2011. ++lpKey;
  2012. // Is there a terminating COLON character for the current key?
  2013. lpValue = StringChr((PCHAR)lpKey, ':');
  2014. if( NULL == lpValue ) {
  2015. // N: OOPS, somthing wrong with the Device ID
  2016. return;
  2017. }
  2018. // The actual start of the Key value is one past the COLON
  2019. ++lpValue;
  2020. //
  2021. // Compute the Key length for Comparison, including the COLON
  2022. // which will serve as a terminator
  2023. //
  2024. wKeyLength = (USHORT)(lpValue - lpKey);
  2025. //
  2026. // Compare the Key to the Know quantities. To speed up the comparison
  2027. // a Check is made on the first character first, to reduce the number
  2028. // of strings to compare against.
  2029. // If a match is found, the appropriate lpp parameter is set to the
  2030. // key's value, and the terminating SEMICOLON is converted to a NULL
  2031. // In all cases lpKey is advanced to the next key if there is one.
  2032. //
  2033. switch (*lpKey) {
  2034. case '1':
  2035. // Look for DOT3 Datalink
  2036. if((RtlCompareMemory(lpKey, "1284.4DL:", wKeyLength)==9))
  2037. {
  2038. *lpp_4DL = lpValue;
  2039. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=NULL)
  2040. {
  2041. *lpKey = '\0';
  2042. ++lpKey;
  2043. }
  2044. } else if((RtlCompareMemory(lpKey, "1284.3DL:", wKeyLength)==9))
  2045. {
  2046. *lpp_DL = lpValue;
  2047. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=NULL)
  2048. {
  2049. *lpKey = '\0';
  2050. ++lpKey;
  2051. }
  2052. } else if((RtlCompareMemory(lpKey, "1284.3C:", wKeyLength)==8))
  2053. {
  2054. *lpp_C = lpValue;
  2055. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  2056. *lpKey = '\0';
  2057. ++lpKey;
  2058. }
  2059. } else if((RtlCompareMemory(lpKey, "1284.3M:", wKeyLength)==8))
  2060. {
  2061. *lpp_M = lpValue;
  2062. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  2063. *lpKey = '\0';
  2064. ++lpKey;
  2065. }
  2066. } else if((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  2067. *lpKey = '\0';
  2068. ++lpKey;
  2069. }
  2070. break;
  2071. case '.':
  2072. // Look for for .3 extras
  2073. if ((RtlCompareMemory(lpKey, ".3C:", wKeyLength)==4) ) {
  2074. *lpp_C = lpValue;
  2075. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  2076. *lpKey = '\0';
  2077. ++lpKey;
  2078. }
  2079. } else if ((RtlCompareMemory(lpKey, ".3M:", wKeyLength)==4) ) {
  2080. *lpp_M = lpValue;
  2081. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  2082. *lpKey = '\0';
  2083. ++lpKey;
  2084. }
  2085. } else if((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  2086. *lpKey = '\0';
  2087. ++lpKey;
  2088. }
  2089. break;
  2090. case 'C':
  2091. // Look for MLC Datalink
  2092. if( (RtlCompareMemory(lpKey, "CMD:", wKeyLength)==4 ) ||
  2093. (RtlCompareMemory(lpKey, "COMMAND SET:", wKeyLength)==12) ) {
  2094. *lpp_CMD = lpValue;
  2095. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  2096. *lpKey = '\0';
  2097. ++lpKey;
  2098. }
  2099. } else if((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  2100. *lpKey = '\0';
  2101. ++lpKey;
  2102. }
  2103. break;
  2104. default:
  2105. // The key is uninteresting. Go to the next Key
  2106. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  2107. *lpKey = '\0';
  2108. ++lpKey;
  2109. }
  2110. break;
  2111. }
  2112. }
  2113. }
  2114. NTSTATUS
  2115. ParPnpGetId(
  2116. IN PCHAR DeviceIdString,
  2117. IN ULONG Type,
  2118. OUT PCHAR resultString,
  2119. OUT PCHAR descriptionString OPTIONAL
  2120. )
  2121. /*
  2122. Description:
  2123. Creates Id's from the device id retrieved from the printer
  2124. Parameters:
  2125. DeviceId - String with raw device id
  2126. Type - What of id we want as a result
  2127. Id - requested id
  2128. Return Value:
  2129. NTSTATUS
  2130. */
  2131. {
  2132. NTSTATUS status = STATUS_SUCCESS;
  2133. USHORT checkSum = 0; // A 16 bit check sum
  2134. CHAR nodeName[16] = "LPTENUM\\";
  2135. // The following are used to generate sub-strings from the Device ID string
  2136. // to get the DevNode name, and to update the registry
  2137. PCHAR MFG = NULL; // Manufacturer name
  2138. PCHAR MDL = NULL; // Model name
  2139. PCHAR CLS = NULL; // Class name
  2140. PCHAR AID = NULL; // Hardare ID
  2141. PCHAR CID = NULL; // Compatible IDs
  2142. PCHAR DES = NULL; // Device Description
  2143. switch(Type) {
  2144. case BusQueryDeviceID:
  2145. // Extract the usefull fields from the DeviceID string. We want
  2146. // MANUFACTURE (MFG):
  2147. // MODEL (MDL):
  2148. // AUTOMATIC ID (AID):
  2149. // COMPATIBLE ID (CID):
  2150. // DESCRIPTION (DES):
  2151. // CLASS (CLS):
  2152. ParPnpFindDeviceIdKeys(&MFG, &MDL, &CLS, &DES, &AID, &CID, DeviceIdString);
  2153. // Check to make sure we got MFG and MDL as absolute minimum fields. If not
  2154. // we cannot continue.
  2155. if (!MFG || !MDL)
  2156. {
  2157. status = STATUS_NOT_FOUND;
  2158. goto ParPnpGetId_Cleanup;
  2159. }
  2160. //
  2161. // Concatenate the provided MFG and MDL P1284 fields
  2162. // Checksum the entire MFG+MDL string
  2163. //
  2164. sprintf(resultString, "%s%s\0",MFG,MDL);
  2165. if (descriptionString) {
  2166. sprintf((PCHAR)descriptionString, "%s %s\0",MFG,MDL);
  2167. }
  2168. break;
  2169. case BusQueryHardwareIDs:
  2170. GetCheckSum(DeviceIdString, (USHORT)strlen((const PCHAR)DeviceIdString), &checkSum);
  2171. sprintf(resultString,"%s%.20s%04X",nodeName,DeviceIdString,checkSum);
  2172. break;
  2173. case BusQueryCompatibleIDs:
  2174. //
  2175. // return only 1 id
  2176. //
  2177. GetCheckSum(DeviceIdString, (USHORT)strlen((const PCHAR)DeviceIdString), &checkSum);
  2178. sprintf(resultString,"%.20s%04X",DeviceIdString,checkSum);
  2179. break;
  2180. }
  2181. if (Type!=BusQueryDeviceID) {
  2182. //
  2183. // Convert and spaces in the Hardware ID to underscores
  2184. //
  2185. StringSubst (resultString, ' ', '_', (USHORT)strlen((const PCHAR)resultString));
  2186. }
  2187. ParPnpGetId_Cleanup:
  2188. return(status);
  2189. }
  2190. VOID
  2191. ParPnpFindDeviceIdKeys(
  2192. PCHAR *lppMFG,
  2193. PCHAR *lppMDL,
  2194. PCHAR *lppCLS,
  2195. PCHAR *lppDES,
  2196. PCHAR *lppAID,
  2197. PCHAR *lppCID,
  2198. PCHAR lpDeviceID
  2199. )
  2200. /*
  2201. Description:
  2202. This function will parse a P1284 Device ID string looking for keys
  2203. of interest to the LPT enumerator. Got it from win95 lptenum
  2204. Parameters:
  2205. lppMFG Pointer to MFG string pointer
  2206. lppMDL Pointer to MDL string pointer
  2207. lppMDL Pointer to CLS string pointer
  2208. lppDES Pointer to DES string pointer
  2209. lppCIC Pointer to CID string pointer
  2210. lppAID Pointer to AID string pointer
  2211. lpDeviceID Pointer to the Device ID string
  2212. Return Value:
  2213. no return VALUE.
  2214. If found the lpp parameters are set to the approprate portions
  2215. of the DeviceID string, and they are NULL terminated.
  2216. The actual DeviceID string is used, and the lpp Parameters just
  2217. reference sections, with appropriate null thrown in.
  2218. */
  2219. {
  2220. PCHAR lpKey = lpDeviceID; // Pointer to the Key to look at
  2221. PCHAR lpValue; // Pointer to the Key's value
  2222. USHORT wKeyLength; // Length for the Key (for stringcmps)
  2223. // While there are still keys to look at.
  2224. DD(NULL,DDT,"ParPnpFindDeviceIdKeys - enter\n");
  2225. if( lppMFG ) { *lppMFG = NULL; }
  2226. if( lppMDL ) { *lppMDL = NULL; }
  2227. if( lppCLS ) { *lppCLS = NULL; }
  2228. if( lppDES ) { *lppDES = NULL; }
  2229. if( lppAID ) { *lppAID = NULL; }
  2230. if( lppCID ) { *lppCID = NULL; }
  2231. if( !lpDeviceID ) {
  2232. PptAssert(!"ParPnpFindDeviceIdKeys - NULL lpDeviceID");
  2233. return;
  2234. }
  2235. while (lpKey != NULL)
  2236. {
  2237. while (*lpKey == ' ')
  2238. ++lpKey;
  2239. // Is there a terminating COLON character for the current key?
  2240. lpValue = StringChr(lpKey, ':');
  2241. if( NULL == lpValue ) {
  2242. // N: OOPS, somthing wrong with the Device ID
  2243. return;
  2244. }
  2245. // The actual start of the Key value is one past the COLON
  2246. ++lpValue;
  2247. //
  2248. // Compute the Key length for Comparison, including the COLON
  2249. // which will serve as a terminator
  2250. //
  2251. wKeyLength = (USHORT)(lpValue - lpKey);
  2252. //
  2253. // Compare the Key to the Know quantities. To speed up the comparison
  2254. // a Check is made on the first character first, to reduce the number
  2255. // of strings to compare against.
  2256. // If a match is found, the appropriate lpp parameter is set to the
  2257. // key's value, and the terminating SEMICOLON is converted to a NULL
  2258. // In all cases lpKey is advanced to the next key if there is one.
  2259. //
  2260. switch (*lpKey) {
  2261. case 'M':
  2262. // Look for MANUFACTURE (MFG) or MODEL (MDL)
  2263. if((RtlCompareMemory(lpKey, "MANUFACTURER", wKeyLength)>5) ||
  2264. (RtlCompareMemory(lpKey, "MFG", wKeyLength)==3) ) {
  2265. *lppMFG = lpValue;
  2266. if ((lpKey = StringChr(lpValue, ';'))!=NULL) {
  2267. *lpKey = '\0';
  2268. ++lpKey;
  2269. }
  2270. } else if((RtlCompareMemory(lpKey, "MODEL", wKeyLength)==5) ||
  2271. (RtlCompareMemory(lpKey, "MDL", wKeyLength)==3) ) {
  2272. *lppMDL = lpValue;
  2273. if ((lpKey = StringChr(lpValue, ';'))!=0) {
  2274. *lpKey = '\0';
  2275. ++lpKey;
  2276. }
  2277. } else if((lpKey = StringChr(lpValue, ';'))!=0) {
  2278. *lpKey = '\0';
  2279. ++lpKey;
  2280. }
  2281. break;
  2282. case 'C':
  2283. // Look for CLASS (CLS) or COMPATIBLEID (CID)
  2284. if ((RtlCompareMemory(lpKey, "CLASS", wKeyLength)==5) ||
  2285. (RtlCompareMemory(lpKey, "CLS", wKeyLength)==3) ) {
  2286. *lppCLS = lpValue;
  2287. if ((lpKey = StringChr(lpValue, ';'))!=0) {
  2288. *lpKey = '\0';
  2289. ++lpKey;
  2290. }
  2291. } else if ((RtlCompareMemory(lpKey, "COMPATIBLEID", wKeyLength)>5) ||
  2292. (RtlCompareMemory(lpKey, "CID", wKeyLength)==3) ) {
  2293. *lppCID = lpValue;
  2294. if ((lpKey = StringChr(lpValue, ';'))!=0) {
  2295. *lpKey = '\0';
  2296. ++lpKey;
  2297. }
  2298. } else if ((lpKey = StringChr(lpValue,';'))!=0) {
  2299. *lpKey = '\0';
  2300. ++lpKey;
  2301. }
  2302. break;
  2303. case 'D':
  2304. // Look for DESCRIPTION (DES)
  2305. if(RtlCompareMemory(lpKey, "DESCRIPTION", wKeyLength) ||
  2306. RtlCompareMemory(lpKey, "DES", wKeyLength) ) {
  2307. *lppDES = lpValue;
  2308. if((lpKey = StringChr(lpValue, ';'))!=0) {
  2309. *lpKey = '\0';
  2310. ++lpKey;
  2311. }
  2312. } else if ((lpKey = StringChr(lpValue, ';'))!=0) {
  2313. *lpKey = '\0';
  2314. ++lpKey;
  2315. }
  2316. break;
  2317. case 'A':
  2318. // Look for AUTOMATIC ID (AID)
  2319. if (RtlCompareMemory(lpKey, "AUTOMATICID", wKeyLength) ||
  2320. RtlCompareMemory(lpKey, "AID", wKeyLength) ) {
  2321. *lppAID = lpValue;
  2322. if ((lpKey = StringChr(lpValue, ';'))!=0) {
  2323. *lpKey = '\0';
  2324. ++lpKey;
  2325. }
  2326. } else if ((lpKey = StringChr(lpValue, ';'))!=0) {
  2327. *lpKey = '\0';
  2328. ++lpKey;
  2329. }
  2330. break;
  2331. default:
  2332. // The key is uninteresting. Go to the next Key
  2333. if ((lpKey = StringChr(lpValue, ';'))!=0) {
  2334. *lpKey = '\0';
  2335. ++lpKey;
  2336. }
  2337. break;
  2338. }
  2339. }
  2340. }
  2341. VOID
  2342. GetCheckSum(
  2343. PCHAR Block,
  2344. USHORT Len,
  2345. PUSHORT CheckSum
  2346. )
  2347. {
  2348. USHORT i;
  2349. // UCHAR lrc;
  2350. USHORT crc = 0;
  2351. unsigned short crc16a[] = {
  2352. 0000000, 0140301, 0140601, 0000500,
  2353. 0141401, 0001700, 0001200, 0141101,
  2354. 0143001, 0003300, 0003600, 0143501,
  2355. 0002400, 0142701, 0142201, 0002100,
  2356. };
  2357. unsigned short crc16b[] = {
  2358. 0000000, 0146001, 0154001, 0012000,
  2359. 0170001, 0036000, 0024000, 0162001,
  2360. 0120001, 0066000, 0074000, 0132001,
  2361. 0050000, 0116001, 0104001, 0043000,
  2362. };
  2363. //
  2364. // Calculate CRC using tables.
  2365. //
  2366. UCHAR tmp;
  2367. for ( i=0; i<Len; i++) {
  2368. tmp = (UCHAR)(Block[i] ^ (UCHAR)crc);
  2369. crc = (USHORT)((crc >> 8) ^ crc16a[tmp & 0x0f] ^ crc16b[tmp >> 4]);
  2370. }
  2371. *CheckSum = crc;
  2372. }
  2373. PCHAR
  2374. Par3QueryDeviceId(
  2375. IN PPDO_EXTENSION Pdx,
  2376. OUT PCHAR CallerDeviceIdBuffer, OPTIONAL
  2377. IN ULONG CallerBufferSize,
  2378. OUT PULONG DeviceIdSize,
  2379. IN BOOLEAN bReturnRawString, // TRUE == include the 2 size bytes in the returned string
  2380. // FALSE == discard the 2 size bytes
  2381. IN BOOLEAN bBuildStlDeviceId
  2382. )
  2383. /*++
  2384. This is the replacement function for SppQueryDeviceId.
  2385. This function uses the caller supplied buffer if the supplied buffer
  2386. is large enough to hold the device id. Otherwise, a buffer is
  2387. allocated from paged pool to hold the device ID and a pointer to
  2388. the allocated buffer is returned to the caller. The caller determines
  2389. whether a buffer was allocated by comparing the returned PCHAR with
  2390. the DeviceIdBuffer parameter passed to this function. A NULL return
  2391. value indicates that an error occurred.
  2392. *** this function assumes that the caller has already acquired
  2393. the port (and selected the device if needed in the case
  2394. of a 1284.3 daisy chain device).
  2395. *** If this function returns a pointer to a paged pool allocation then
  2396. the caller is responsible for freeing the buffer when it is no
  2397. longer needed.
  2398. --*/
  2399. {
  2400. PUCHAR Controller = Pdx->Controller;
  2401. NTSTATUS Status;
  2402. UCHAR idSizeBuffer[2];
  2403. ULONG bytesToRead;
  2404. ULONG bytesRead = 0;
  2405. USHORT deviceIdSize;
  2406. USHORT deviceIdBufferSize;
  2407. PCHAR deviceIdBuffer;
  2408. PCHAR readPtr;
  2409. BOOLEAN allocatedBuffer = FALSE;
  2410. DD((PCE)Pdx,DDT,"Enter pnp::Par3QueryDeviceId: Controller=%x\n", Controller);
  2411. if( TRUE == bBuildStlDeviceId ) {
  2412. // if this is a legacy stl, forward call to special handler
  2413. return ParStlQueryStlDeviceId(Pdx,
  2414. CallerDeviceIdBuffer, CallerBufferSize,
  2415. DeviceIdSize, bReturnRawString);
  2416. }
  2417. if( Pdx->Ieee1284_3DeviceId == DOT3_LEGACY_ZIP_ID ) {
  2418. // if this is a legacy Zip, forward call to special handler
  2419. return Par3QueryLegacyZipDeviceId(Pdx,
  2420. CallerDeviceIdBuffer, CallerBufferSize,
  2421. DeviceIdSize, bReturnRawString);
  2422. }
  2423. //
  2424. // Take a 40ms nap - there is at least one printer that can't handle
  2425. // back to back 1284 device ID queries without a minimum 20-30ms delay
  2426. // between the queries which breaks PnP'ing the printer
  2427. //
  2428. if( KeGetCurrentIrql() == PASSIVE_LEVEL ) {
  2429. LARGE_INTEGER delay;
  2430. delay.QuadPart = - 10 * 1000 * 40; // 40 ms
  2431. KeDelayExecutionThread( KernelMode, FALSE, &delay );
  2432. }
  2433. *DeviceIdSize = 0;
  2434. //
  2435. // If we are currently connected to the peripheral via any 1284 mode
  2436. // other than Compatibility/Spp mode (which does not require an IEEE
  2437. // negotiation), we must first terminate the current mode/connection.
  2438. //
  2439. ParTerminate( Pdx );
  2440. //
  2441. // Negotiate the peripheral into nibble device id mode.
  2442. //
  2443. Status = ParEnterNibbleMode(Pdx, REQUEST_DEVICE_ID);
  2444. if( !NT_SUCCESS(Status) ) {
  2445. DD((PCE)Pdx,DDT,"pnp::Par3QueryDeviceId: call to ParEnterNibbleMode FAILED\n");
  2446. ParTerminateNibbleMode(Pdx);
  2447. return NULL;
  2448. }
  2449. //
  2450. // Read first two bytes to get the total (including the 2 size bytes) size
  2451. // of the Device Id string.
  2452. //
  2453. bytesToRead = 2;
  2454. Status = ParNibbleModeRead(Pdx, idSizeBuffer, bytesToRead, &bytesRead);
  2455. if( !NT_SUCCESS( Status ) || ( bytesRead != bytesToRead ) ) {
  2456. DD((PCE)Pdx,DDT,"pnp::Par3QueryDeviceId: read of DeviceID size FAILED\n");
  2457. return NULL;
  2458. }
  2459. //
  2460. // Compute size of DeviceId string (including the 2 byte size prefix)
  2461. //
  2462. deviceIdSize = (USHORT)( idSizeBuffer[0]*0x100 + idSizeBuffer[1] );
  2463. DD((PCE)Pdx,DDT,"pnp::Par3QueryDeviceId: DeviceIdSize (including 2 size bytes) reported as %d\n", deviceIdSize);
  2464. //
  2465. // Allocate a buffer to hold the DeviceId string and read the DeviceId into it.
  2466. //
  2467. if( bReturnRawString ) {
  2468. //
  2469. // Caller wants the raw string including the 2 size bytes
  2470. //
  2471. *DeviceIdSize = deviceIdSize;
  2472. deviceIdBufferSize = (USHORT)(deviceIdSize + sizeof(CHAR)); // ID size + ID + terminating NULL
  2473. } else {
  2474. //
  2475. // Caller does not want the 2 byte size prefix
  2476. //
  2477. *DeviceIdSize = deviceIdSize - 2*sizeof(CHAR);
  2478. deviceIdBufferSize = (USHORT)(deviceIdSize - 2*sizeof(CHAR) + sizeof(CHAR)); // ID + terminating NULL
  2479. }
  2480. //
  2481. // If caller's buffer is large enough use it, otherwise allocate a buffer
  2482. // to hold the device ID
  2483. //
  2484. if( CallerDeviceIdBuffer && (CallerBufferSize >= deviceIdBufferSize) ) {
  2485. //
  2486. // Use caller's buffer - *** NOTE: we are creating an alias for the caller buffer
  2487. //
  2488. deviceIdBuffer = CallerDeviceIdBuffer;
  2489. DD((PCE)Pdx,DDT,"pnp::Par3QueryDeviceId: using Caller supplied buffer\n");
  2490. } else {
  2491. //
  2492. // Either caller did not supply a buffer or supplied a buffer that is not
  2493. // large enough to hold the device ID, so allocate a buffer.
  2494. //
  2495. DD((PCE)Pdx,DDT,"pnp::Par3QueryDeviceId: Caller's Buffer TOO_SMALL - CallerBufferSize= %d, deviceIdBufferSize= %d\n",
  2496. CallerBufferSize, deviceIdBufferSize);
  2497. DD((PCE)Pdx,DDT,"pnp::Par3QueryDeviceId: will allocate and return ptr to buffer\n");
  2498. deviceIdBuffer = (PCHAR)ExAllocatePool(PagedPool, deviceIdBufferSize);
  2499. if( !deviceIdBuffer ) {
  2500. DD((PCE)Pdx,DDT,"pnp::Par3QueryDeviceId: ExAllocatePool FAILED\n");
  2501. return NULL;
  2502. }
  2503. allocatedBuffer = TRUE; // note that we allocated our own buffer rather than using caller's buffer
  2504. }
  2505. //
  2506. // NULL out the ID buffer to be safe
  2507. //
  2508. RtlZeroMemory( deviceIdBuffer, deviceIdBufferSize );
  2509. //
  2510. // Does the caller want the 2 byte size prefix?
  2511. //
  2512. if( bReturnRawString ) {
  2513. //
  2514. // Yes, caller wants the size prefix. Copy prefix to buffer to return.
  2515. //
  2516. *(deviceIdBuffer+0) = idSizeBuffer[0];
  2517. *(deviceIdBuffer+1) = idSizeBuffer[1];
  2518. readPtr = deviceIdBuffer + 2;
  2519. } else {
  2520. //
  2521. // No, discard size prefix
  2522. //
  2523. readPtr = deviceIdBuffer;
  2524. }
  2525. //
  2526. // Read remainder of DeviceId from device
  2527. //
  2528. bytesToRead = deviceIdSize - 2; // already have the 2 size bytes
  2529. Status = ParNibbleModeRead(Pdx, readPtr, bytesToRead, &bytesRead);
  2530. ParTerminateNibbleMode( Pdx );
  2531. P5WritePortUchar(Controller + DCR_OFFSET, DCR_NEUTRAL);
  2532. if( !NT_SUCCESS(Status) || (bytesRead < 1) ) {
  2533. if( allocatedBuffer ) {
  2534. // we're using our own allocated buffer rather than a caller supplied buffer - free it
  2535. DD((PCE)Pdx,DDE,"Par3QueryDeviceId:: read of DeviceId FAILED - discarding buffer\n");
  2536. ExFreePool( deviceIdBuffer );
  2537. }
  2538. return NULL;
  2539. }
  2540. if ( bytesRead < bytesToRead ) {
  2541. //
  2542. // Device likely reported incorrect value for IEEE 1284 Device ID length
  2543. //
  2544. // This spew is on by default in checked builds to try to get
  2545. // a feel for how many types of devices are broken in this way
  2546. //
  2547. DD((PCE)Pdx,DDE,"pnp::Par3QueryDeviceId - ID shorter than expected\n");
  2548. }
  2549. return deviceIdBuffer;
  2550. }
  2551. VOID
  2552. ParReleasePortInfoToPortDevice(
  2553. IN PPDO_EXTENSION Pdx
  2554. )
  2555. /*++
  2556. Routine Description:
  2557. This routine will release the port information back to the port driver.
  2558. Arguments:
  2559. Extension - Supplies the device extension.
  2560. Return Value:
  2561. None.
  2562. --*/
  2563. {
  2564. //
  2565. // ParPort treats this as a NO-OP in Win2K, so don't bother sending the IOCTL.
  2566. //
  2567. // In follow-on to Win2K parport may use this to page the entire driver as
  2568. // it was originally intended, so we'll turn this back on then.
  2569. //
  2570. UNREFERENCED_PARAMETER( Pdx );
  2571. return;
  2572. }
  2573. VOID
  2574. ParFreePort(
  2575. IN PPDO_EXTENSION Pdx
  2576. )
  2577. /*++
  2578. Routine Description:
  2579. This routine calls the internal free port ioctl. This routine
  2580. should be called before completing an IRP that has allocated
  2581. the port.
  2582. Arguments:
  2583. Extension - Supplies the device extension.
  2584. Return Value:
  2585. None.
  2586. --*/
  2587. {
  2588. // Don't allow multiple releases
  2589. if( Pdx->bAllocated ) {
  2590. DD((PCE)Pdx,DDT,"ParFreePort - calling ParPort's FreePort function\n");
  2591. Pdx->FreePort( Pdx->PortContext );
  2592. } else {
  2593. DD((PCE)Pdx,DDT,"ParFreePort - we don't have the Port! (!Ext->bAllocated)\n");
  2594. }
  2595. Pdx->bAllocated = FALSE;
  2596. }
  2597. NTSTATUS
  2598. ParAllocPortCompletionRoutine(
  2599. IN PDEVICE_OBJECT DeviceObject,
  2600. IN PIRP Irp,
  2601. IN PVOID Event
  2602. )
  2603. /*++
  2604. Routine Description:
  2605. This routine is the completion routine for a port allocate request.
  2606. Arguments:
  2607. DeviceObject - Supplies the device object.
  2608. Irp - Supplies the I/O request packet.
  2609. Context - Supplies the notification event.
  2610. Return Value:
  2611. STATUS_MORE_PROCESSING_REQUIRED - The Irp still requires processing.
  2612. --*/
  2613. {
  2614. UNREFERENCED_PARAMETER( Irp );
  2615. UNREFERENCED_PARAMETER( DeviceObject );
  2616. KeSetEvent((PKEVENT) Event, 0, FALSE);
  2617. return STATUS_MORE_PROCESSING_REQUIRED;
  2618. }
  2619. BOOLEAN
  2620. ParAllocPort(
  2621. IN PPDO_EXTENSION Pdx
  2622. )
  2623. /*++
  2624. Routine Description:
  2625. This routine takes the given Irp and sends it down as a port allocate
  2626. request. When this request completes, the Irp will be queued for
  2627. processing.
  2628. Arguments:
  2629. Pdx - Supplies the device extension.
  2630. Return Value:
  2631. FALSE - The port was not successfully allocated.
  2632. TRUE - The port was successfully allocated.
  2633. --*/
  2634. {
  2635. PIO_STACK_LOCATION NextSp;
  2636. KEVENT Event;
  2637. PIRP Irp;
  2638. BOOLEAN bAllocated;
  2639. NTSTATUS Status;
  2640. LARGE_INTEGER Timeout;
  2641. // Don't allow multiple allocations
  2642. if (Pdx->bAllocated) {
  2643. DD((PCE)Pdx,DDT,"ParAllocPort - controller=%x - port already allocated\n", Pdx->Controller);
  2644. return TRUE;
  2645. }
  2646. Irp = Pdx->CurrentOpIrp;
  2647. KeInitializeEvent(&Event, NotificationEvent, FALSE);
  2648. NextSp = IoGetNextIrpStackLocation(Irp);
  2649. NextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  2650. NextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE;
  2651. IoSetCompletionRoutine( Irp, ParAllocPortCompletionRoutine, &Event, TRUE, TRUE, TRUE );
  2652. ParCallDriver(Pdx->PortDeviceObject, Irp);
  2653. Timeout.QuadPart = -((LONGLONG) Pdx->TimerStart*10*1000*1000);
  2654. Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &Timeout);
  2655. if (Status == STATUS_TIMEOUT) {
  2656. IoCancelIrp(Irp);
  2657. KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
  2658. }
  2659. bAllocated = (BOOLEAN)NT_SUCCESS(Irp->IoStatus.Status);
  2660. Pdx->bAllocated = bAllocated;
  2661. if (!bAllocated) {
  2662. Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
  2663. DD((PCE)Pdx,DDE,"ParAllocPort - controller=%x - FAILED - DEVICE_BUSY timeout\n",Pdx->Controller);
  2664. }
  2665. return bAllocated;
  2666. }
  2667.