Leaked source code of windows server 2003
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.

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