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

926 lines
27 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1993 - 1999
  3. Module Name:
  4. spp.c
  5. Abstract:
  6. This module contains the code for standard parallel ports
  7. (centronics mode).
  8. Author:
  9. Anthony V. Ercolano 1-Aug-1992
  10. Norbert P. Kusters 22-Oct-1993
  11. Environment:
  12. Kernel mode
  13. Revision History :
  14. --*/
  15. #include "pch.h"
  16. ULONG
  17. SppWriteLoopPI(
  18. IN PUCHAR Controller,
  19. IN PUCHAR WriteBuffer,
  20. IN ULONG NumBytesToWrite,
  21. IN ULONG BusyDelay
  22. );
  23. ULONG
  24. SppCheckBusyDelay(
  25. IN PDEVICE_EXTENSION Extension,
  26. IN PUCHAR WriteBuffer,
  27. IN ULONG NumBytesToWrite
  28. );
  29. NTSTATUS
  30. SppWrite(
  31. IN PDEVICE_EXTENSION Extension,
  32. IN PVOID Buffer,
  33. IN ULONG BytesToWrite,
  34. OUT PULONG BytesTransferred
  35. );
  36. NTSTATUS
  37. SppQueryDeviceId(
  38. IN PDEVICE_EXTENSION Extension,
  39. OUT PUCHAR DeviceIdBuffer,
  40. IN ULONG BufferSize,
  41. OUT PULONG DeviceIdSize,
  42. IN BOOLEAN bReturnRawString
  43. );
  44. NTSTATUS
  45. ParEnterSppMode(
  46. IN PDEVICE_EXTENSION Extension,
  47. IN BOOLEAN DeviceIdRequest
  48. )
  49. {
  50. ParDump2(PARENTRY, ( "ParEnterSppMode: Enter!\r\n" ));
  51. Extension->CurrentPhase = PHASE_FORWARD_IDLE;
  52. Extension->Connected = TRUE;
  53. return STATUS_SUCCESS;
  54. }
  55. ULONG
  56. SppWriteLoopPI(
  57. IN PUCHAR Controller,
  58. IN PUCHAR WriteBuffer,
  59. IN ULONG NumBytesToWrite,
  60. IN ULONG BusyDelay
  61. )
  62. /*++
  63. Routine Description:
  64. This routine outputs the given write buffer to the parallel port
  65. using the standard centronics protocol.
  66. Arguments:
  67. Controller - Supplies the base address of the parallel port.
  68. WriteBuffer - Supplies the buffer to write to the port.
  69. NumBytesToWrite - Supplies the number of bytes to write out to the port.
  70. BusyDelay - Supplies the number of microseconds to delay before
  71. checking the busy bit.
  72. Return Value:
  73. The number of bytes successfully written out to the parallel port.
  74. --*/
  75. {
  76. ULONG i;
  77. UCHAR DeviceStatus;
  78. BOOLEAN atPassiveIrql = FALSE;
  79. LARGE_INTEGER sppLoopDelay;
  80. sppLoopDelay.QuadPart = -(LONG)(gSppLoopDelay); // in 100ns units
  81. if( KeGetCurrentIrql() == PASSIVE_LEVEL ) {
  82. atPassiveIrql = TRUE;
  83. }
  84. ParDump2(PARENTRY, ("spp::SppWriteLoopPI - Enter\n") );
  85. if (!BusyDelay) {
  86. BusyDelay = 1;
  87. }
  88. for (i = 0; i < NumBytesToWrite; i++) {
  89. DeviceStatus = GetStatus(Controller);
  90. if (PAR_ONLINE(DeviceStatus)) {
  91. //
  92. // Anytime we write out a character we will restart
  93. // the count down timer.
  94. //
  95. WRITE_PORT_UCHAR(Controller + PARALLEL_DATA_OFFSET, *WriteBuffer++);
  96. KeStallExecutionProcessor(1);
  97. StoreControl(Controller, (PAR_CONTROL_WR_CONTROL |
  98. PAR_CONTROL_SLIN |
  99. PAR_CONTROL_NOT_INIT |
  100. PAR_CONTROL_STROBE));
  101. KeStallExecutionProcessor(1);
  102. StoreControl(Controller, (PAR_CONTROL_WR_CONTROL |
  103. PAR_CONTROL_SLIN |
  104. PAR_CONTROL_NOT_INIT));
  105. KeStallExecutionProcessor(BusyDelay);
  106. } else {
  107. ParDump2(PARINFO, ("spp::SppWriteLoopPI - DeviceStatus = %x - NOT ONLINE\n", DeviceStatus) );
  108. break;
  109. }
  110. //
  111. // Try to reduce CPU util by parallel?
  112. //
  113. if( gSppLoopDelay && gSppLoopBytesPerDelay && atPassiveIrql && (i != 0) && !(i % gSppLoopBytesPerDelay) ) {
  114. // every SppLoopBytesPerDelay bytes - sleep to let other threads run
  115. KeDelayExecutionThread(KernelMode, FALSE, &sppLoopDelay);
  116. }
  117. }
  118. ParDump2(PAREXIT, ("Leaving SppWriteLoopPI(...): Bytes Written = %ld\n", i) );
  119. return i;
  120. }
  121. ULONG
  122. SppCheckBusyDelay(
  123. IN PDEVICE_EXTENSION Extension,
  124. IN PUCHAR WriteBuffer,
  125. IN ULONG NumBytesToWrite
  126. )
  127. /*++
  128. Routine Description:
  129. This routine determines if the current busy delay setting is
  130. adequate for this printer.
  131. Arguments:
  132. Extension - Supplies the device extension.
  133. WriteBuffer - Supplies the write buffer.
  134. NumBytesToWrite - Supplies the size of the write buffer.
  135. Return Value:
  136. The number of bytes strobed out to the printer.
  137. --*/
  138. {
  139. PUCHAR Controller;
  140. ULONG BusyDelay;
  141. LARGE_INTEGER Start;
  142. LARGE_INTEGER PerfFreq;
  143. LARGE_INTEGER End;
  144. LARGE_INTEGER GetStatusTime;
  145. LARGE_INTEGER CallOverhead;
  146. UCHAR DeviceStatus;
  147. ULONG i;
  148. ULONG NumberOfCalls;
  149. ULONG maxTries;
  150. #if (0 == DVRH_RAISE_IRQL)
  151. KIRQL OldIrql;
  152. #endif
  153. // ParDumpV( ("Enter SppCheckBusyDelay(...): NumBytesToWrite = %d\n", NumBytesToWrite) );
  154. Controller = Extension->Controller;
  155. BusyDelay = Extension->BusyDelay;
  156. // If the current busy delay value is 10 or greater then something
  157. // is weird and settle for 10.
  158. if (Extension->BusyDelay >= 10) {
  159. Extension->BusyDelayDetermined = TRUE;
  160. return 0;
  161. }
  162. // Take some performance measurements.
  163. #if (0 == DVRH_RAISE_IRQL)
  164. if (0 == SppNoRaiseIrql)
  165. KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
  166. #endif
  167. Start = KeQueryPerformanceCounter(&PerfFreq);
  168. DeviceStatus = GetStatus(Controller);
  169. End = KeQueryPerformanceCounter(&PerfFreq);
  170. GetStatusTime.QuadPart = End.QuadPart - Start.QuadPart;
  171. Start = KeQueryPerformanceCounter(&PerfFreq);
  172. End = KeQueryPerformanceCounter(&PerfFreq);
  173. #if (0 == DVRH_RAISE_IRQL)
  174. if (0 == SppNoRaiseIrql)
  175. KeLowerIrql(OldIrql);
  176. #endif
  177. CallOverhead.QuadPart = End.QuadPart - Start.QuadPart;
  178. GetStatusTime.QuadPart -= CallOverhead.QuadPart;
  179. if (GetStatusTime.QuadPart <= 0) {
  180. GetStatusTime.QuadPart = 1;
  181. }
  182. // Figure out how many calls to 'GetStatus' can be made in 20 us.
  183. NumberOfCalls = (ULONG) (PerfFreq.QuadPart*20/GetStatusTime.QuadPart/1000000) + 1;
  184. //
  185. // - check to make sure the device is ready to receive a byte before we start clocking
  186. // data out
  187. //
  188. // DVDF - 25Jan99 - added check
  189. //
  190. //
  191. // - nothing magic about 25 - just catch the case where NumberOfCalls may be bogus
  192. // and try something reasonable - empirically NumberOfCalls has ranged from 8-24
  193. //
  194. maxTries = (NumberOfCalls > 25) ? 25 : NumberOfCalls;
  195. for( i = 0 ; i < maxTries ; i++ ) {
  196. // spin for slow device to get ready to receive data - roughly 20us max
  197. DeviceStatus = GetStatus( Controller );
  198. if( PAR_ONLINE( DeviceStatus ) ) {
  199. // break out of loop as soon as device is ready
  200. break;
  201. }
  202. }
  203. if( !PAR_ONLINE( DeviceStatus ) ) {
  204. // device is still not online - bail out
  205. return 0;
  206. }
  207. // The printer is ready to accept a byte. Strobe one out
  208. // and check out the reaction time for BUSY.
  209. if (BusyDelay) {
  210. #if (0 == DVRH_RAISE_IRQL)
  211. if (0 == SppNoRaiseIrql)
  212. KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
  213. #endif
  214. WRITE_PORT_UCHAR(Controller + PARALLEL_DATA_OFFSET, *WriteBuffer++);
  215. KeStallExecutionProcessor(1);
  216. StoreControl(Controller, (PAR_CONTROL_WR_CONTROL |
  217. PAR_CONTROL_SLIN |
  218. PAR_CONTROL_NOT_INIT |
  219. PAR_CONTROL_STROBE));
  220. KeStallExecutionProcessor(1);
  221. StoreControl(Controller, (PAR_CONTROL_WR_CONTROL |
  222. PAR_CONTROL_SLIN |
  223. PAR_CONTROL_NOT_INIT));
  224. KeStallExecutionProcessor(BusyDelay);
  225. for (i = 0; i < NumberOfCalls; i++) {
  226. DeviceStatus = GetStatus(Controller);
  227. if (!(DeviceStatus & PAR_STATUS_NOT_BUSY)) {
  228. break;
  229. }
  230. }
  231. #if (0 == DVRH_RAISE_IRQL)
  232. if (0 == SppNoRaiseIrql)
  233. KeLowerIrql(OldIrql);
  234. #endif
  235. } else {
  236. #if (0 == DVRH_RAISE_IRQL)
  237. if (0 == SppNoRaiseIrql)
  238. KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
  239. #endif
  240. WRITE_PORT_UCHAR(Controller + PARALLEL_DATA_OFFSET, *WriteBuffer++);
  241. KeStallExecutionProcessor(1);
  242. StoreControl(Controller, (PAR_CONTROL_WR_CONTROL |
  243. PAR_CONTROL_SLIN |
  244. PAR_CONTROL_NOT_INIT |
  245. PAR_CONTROL_STROBE));
  246. KeStallExecutionProcessor(1);
  247. StoreControl(Controller, (PAR_CONTROL_WR_CONTROL |
  248. PAR_CONTROL_SLIN |
  249. PAR_CONTROL_NOT_INIT));
  250. for (i = 0; i < NumberOfCalls; i++) {
  251. DeviceStatus = GetStatus(Controller);
  252. if (!(DeviceStatus & PAR_STATUS_NOT_BUSY)) {
  253. break;
  254. }
  255. }
  256. #if (0 == DVRH_RAISE_IRQL)
  257. if (0 == SppNoRaiseIrql)
  258. KeLowerIrql(OldIrql);
  259. #endif
  260. }
  261. if (i == 0) {
  262. // In this case the BUSY was set as soon as we checked it.
  263. // Use this busyDelay with the PI code.
  264. Extension->UsePIWriteLoop = TRUE;
  265. Extension->BusyDelayDetermined = TRUE;
  266. } else if (i == NumberOfCalls) {
  267. // In this case the BUSY was never seen. This is a very fast
  268. // printer so use the fastest code possible.
  269. Extension->BusyDelayDetermined = TRUE;
  270. } else {
  271. // The test failed. The lines showed not BUSY and then BUSY
  272. // without strobing a byte in between.
  273. Extension->UsePIWriteLoop = TRUE;
  274. Extension->BusyDelay++;
  275. }
  276. return 1;
  277. }
  278. NTSTATUS
  279. SppWrite(
  280. IN PDEVICE_EXTENSION Extension,
  281. IN PVOID Buffer,
  282. IN ULONG BytesToWrite,
  283. OUT PULONG BytesTransferred
  284. )
  285. /*++
  286. Routine Description:
  287. Arguments:
  288. Extension - Supplies the device extension.
  289. Return Value:
  290. None.
  291. --*/
  292. {
  293. NTSTATUS status;
  294. UCHAR DeviceStatus;
  295. ULONG TimerStart;
  296. LONG CountDown;
  297. PUCHAR IrpBuffer;
  298. LARGE_INTEGER StartOfSpin;
  299. LARGE_INTEGER NextQuery;
  300. LARGE_INTEGER Difference;
  301. BOOLEAN DoDelays;
  302. BOOLEAN PortFree;
  303. ULONG NumBytesWritten;
  304. ULONG LoopNumber;
  305. ULONG NumberOfBusyChecks;
  306. ULONG MaxBusyDelay;
  307. ULONG MaxBytes;
  308. ParDump2(PARINFO, ("Enter SppWrite(...): %wZ, BytesToWrite = %d\n", &Extension->SymbolicLinkName, BytesToWrite) );
  309. IrpBuffer = (PUCHAR)Buffer;
  310. MaxBytes = BytesToWrite;
  311. TimerStart = Extension->TimerStart;
  312. CountDown = (LONG)TimerStart;
  313. NumberOfBusyChecks = 9;
  314. MaxBusyDelay = 0;
  315. ParGetDriverParameterDword( &RegistryPath, (PWSTR)L"SppLoopDelay", &gSppLoopDelay );
  316. // 0==feature disabled, otherwise min==1 (100ns), max==10000 (1ms)
  317. if( gSppLoopDelay > 10000 ) {
  318. gSppLoopDelay = 10000;
  319. }
  320. ParGetDriverParameterDword( &RegistryPath, (PWSTR)L"SppLoopBytesPerDelay", &gSppLoopBytesPerDelay );
  321. // 0==feature disabled, otherwise min==32, max==4096
  322. if( gSppLoopBytesPerDelay ) {
  323. if( gSppLoopBytesPerDelay < 32 ) {
  324. gSppLoopBytesPerDelay = 32;
  325. } else if( gSppLoopBytesPerDelay > 4096 ) {
  326. gSppLoopBytesPerDelay = 4096;
  327. }
  328. }
  329. #if DBG
  330. // RMT - remove following line - inserted only for testing to reduce timeout
  331. // TimerStart = 10;
  332. #endif
  333. // ParDumpV( ("TimerStart is: %d\n", TimerStart) );
  334. // Turn off the strobe in case it was left on by some other device sharing
  335. // the port.
  336. StoreControl(Extension->Controller, (PAR_CONTROL_WR_CONTROL |
  337. PAR_CONTROL_SLIN |
  338. PAR_CONTROL_NOT_INIT));
  339. PushSomeBytes:
  340. //
  341. // While we are strobing data we don't want to get context
  342. // switched away. Raise up to dispatch level to prevent that.
  343. //
  344. // The reason we can't afford the context switch is that
  345. // the device can't have the data strobe line on for more
  346. // than 500 microseconds.
  347. //
  348. // We never want to be at raised irql form more than
  349. // 200 microseconds, so we will do no more than 100
  350. // bytes at a time.
  351. //
  352. LoopNumber = 512;
  353. if (LoopNumber > BytesToWrite) {
  354. LoopNumber = BytesToWrite;
  355. }
  356. //
  357. // Enter the write loop
  358. //
  359. if (!Extension->BusyDelayDetermined) {
  360. ParDump2(PARINFO, ("SppWrite: Calling SppCheckBusyDelay\n"));
  361. NumBytesWritten = SppCheckBusyDelay(Extension, IrpBuffer, LoopNumber);
  362. if (Extension->BusyDelayDetermined) {
  363. if (Extension->BusyDelay > MaxBusyDelay) {
  364. MaxBusyDelay = Extension->BusyDelay;
  365. NumberOfBusyChecks = 10;
  366. }
  367. if (NumberOfBusyChecks) {
  368. NumberOfBusyChecks--;
  369. Extension->BusyDelayDetermined = FALSE;
  370. } else {
  371. Extension->BusyDelay = MaxBusyDelay + 1;
  372. }
  373. }
  374. } else if (Extension->UsePIWriteLoop) {
  375. NumBytesWritten = SppWriteLoopPI(Extension->Controller,
  376. IrpBuffer,
  377. LoopNumber,
  378. Extension->BusyDelay);
  379. } else {
  380. NumBytesWritten = SppWriteLoop(Extension->Controller,
  381. IrpBuffer,
  382. LoopNumber);
  383. }
  384. if (NumBytesWritten) {
  385. CountDown = TimerStart;
  386. IrpBuffer += NumBytesWritten;
  387. BytesToWrite -= NumBytesWritten;
  388. }
  389. //
  390. // Check to see if the io is done. If it is then call the
  391. // code to complete the request.
  392. //
  393. if (!BytesToWrite) {
  394. *BytesTransferred = MaxBytes;
  395. status = STATUS_SUCCESS;
  396. goto returnTarget;
  397. } else if ((Extension->CurrentOpIrp)->Cancel) {
  398. //
  399. // See if the IO has been canceled. The cancel routine
  400. // has been removed already (when this became the
  401. // current irp). Simply check the bit. We don't even
  402. // need to capture the lock. If we miss a round
  403. // it won't be that bad.
  404. //
  405. *BytesTransferred = MaxBytes - BytesToWrite;
  406. status = STATUS_CANCELLED;
  407. goto returnTarget;
  408. } else {
  409. //
  410. // We've taken care of the reasons that the irp "itself"
  411. // might want to be completed.
  412. // printer to see if it is in a state that might
  413. // cause us to complete the irp.
  414. //
  415. // First let's check if the device status is
  416. // ok and online. If it is then simply go back
  417. // to the byte pusher.
  418. //
  419. DeviceStatus = GetStatus(Extension->Controller);
  420. if (PAR_ONLINE(DeviceStatus)) {
  421. ParDump2(PARINFO, ("SppWrite: We are online. Push more bytes.\n"));
  422. goto PushSomeBytes;
  423. }
  424. //
  425. // Perhaps the operator took the device off line,
  426. // or forgot to put in enough paper. If so, then
  427. // let's hang out here for the until the timeout
  428. // period has expired waiting for them to make things
  429. // all better.
  430. //
  431. if (PAR_PAPER_EMPTY(DeviceStatus) ||
  432. PAR_OFF_LINE(DeviceStatus)) {
  433. if (CountDown > 0) {
  434. //
  435. // We'll wait 1 second increments.
  436. //
  437. ParDump(PARTHREAD | PARDUMP_VERBOSE_MAX,
  438. ("PARALLEL: "
  439. "decrementing countdown for PAPER_EMPTY/OFF_LINE - "
  440. "countDown: %d status: 0x%x\n",
  441. CountDown, DeviceStatus) );
  442. CountDown--;
  443. // If anyone is waiting for the port then let them have it,
  444. // since the printer is busy.
  445. ParFreePort(Extension);
  446. KeDelayExecutionThread(
  447. KernelMode,
  448. FALSE,
  449. &Extension->OneSecond
  450. );
  451. if (!ParAllocPort(Extension)) {
  452. *BytesTransferred = MaxBytes - BytesToWrite;
  453. ParDump(PARDUMP_VERBOSE_MAX,
  454. ("PARALLEL: "
  455. "In SppWrite(...): returning STATUS_DEVICE_BUSY\n") );
  456. status = STATUS_DEVICE_BUSY;
  457. goto returnTarget;
  458. }
  459. goto PushSomeBytes;
  460. } else {
  461. //
  462. // Timer has expired. Complete the request.
  463. //
  464. *BytesTransferred = MaxBytes - BytesToWrite;
  465. ParDump(PARTHREAD | PARDUMP_VERBOSE_MAX,
  466. ("PARALLEL: "
  467. "In SppWrite(...): Timer expired - "
  468. "DeviceStatus = %08x\n",
  469. DeviceStatus) );
  470. if (PAR_OFF_LINE(DeviceStatus)) {
  471. ParDump(PARDUMP_VERBOSE_MAX,
  472. ("PARALLEL: "
  473. "In SppWrite(...): returning STATUS_DEVICE_OFF_LINE\n") );
  474. status = STATUS_DEVICE_OFF_LINE;
  475. goto returnTarget;
  476. } else if (PAR_NO_CABLE(DeviceStatus)) {
  477. ParDump(PARDUMP_VERBOSE_MAX,
  478. ("PARALLEL: "
  479. "In SppWrite(...): returning STATUS_DEVICE_NOT_CONNECTED\n") );
  480. status = STATUS_DEVICE_NOT_CONNECTED;
  481. goto returnTarget;
  482. } else {
  483. ParDump(PARDUMP_VERBOSE_MAX,
  484. ("PARALLEL: "
  485. "In SppWrite(...): returning STATUS_DEVICE_PAPER_EMPTY\n") );
  486. status = STATUS_DEVICE_PAPER_EMPTY;
  487. goto returnTarget;
  488. }
  489. }
  490. } else if (PAR_POWERED_OFF(DeviceStatus) ||
  491. PAR_NOT_CONNECTED(DeviceStatus) ||
  492. PAR_NO_CABLE(DeviceStatus)) {
  493. //
  494. // We are in a "bad" state. Is what
  495. // happened to the printer (power off, not connected, or
  496. // the cable being pulled) something that will require us
  497. // to reinitialize the printer? If we need to
  498. // reinitialize the printer then we should complete
  499. // this IO so that the driving application can
  500. // choose what is the best thing to do about it's
  501. // io.
  502. //
  503. ParDumpV( ("In SppWrite(...): \"bad\" state - need to reinitialize printer?") );
  504. *BytesTransferred = MaxBytes - BytesToWrite;
  505. if (PAR_POWERED_OFF(DeviceStatus)) {
  506. ParDump(PARDUMP_VERBOSE_MAX,
  507. ("PARALLEL: "
  508. "In SppWrite(...): returning STATUS_DEVICE_POWERED_OFF\n") );
  509. status = STATUS_DEVICE_POWERED_OFF;
  510. goto returnTarget;
  511. } else if (PAR_NOT_CONNECTED(DeviceStatus) ||
  512. PAR_NO_CABLE(DeviceStatus)) {
  513. ParDump(PARDUMP_VERBOSE_MAX,
  514. ("PARALLEL: "
  515. "In SppWrite(...): returning STATUS_DEVICE_NOT_CONNECTED\n") );
  516. status = STATUS_DEVICE_NOT_CONNECTED;
  517. goto returnTarget;
  518. }
  519. }
  520. //
  521. // The device could simply be busy at this point. Simply spin
  522. // here waiting for the device to be in a state that we
  523. // care about.
  524. //
  525. // As we spin, get the system ticks. Every time that it looks
  526. // like a second has passed, decrement the countdown. If
  527. // it ever goes to zero, then timeout the request.
  528. //
  529. KeQueryTickCount(&StartOfSpin);
  530. DoDelays = FALSE;
  531. do {
  532. //
  533. // After about a second of spinning, let the rest of the
  534. // machine have time for a second.
  535. //
  536. if (DoDelays) {
  537. ParFreePort(Extension);
  538. PortFree = TRUE;
  539. ParDump2(PARINFO,
  540. ("Before delay thread of one second, dsr=%x DCR[%x]\n",
  541. READ_PORT_UCHAR(Extension->Controller + OFFSET_DSR),
  542. READ_PORT_UCHAR(Extension->Controller + OFFSET_DCR)) );
  543. KeDelayExecutionThread(KernelMode, FALSE, &Extension->OneSecond);
  544. ParDump2(PARINITDEV,
  545. ("Did delay thread of one second, CountDown=%d\n",
  546. CountDown) );
  547. CountDown--;
  548. } else {
  549. if (Extension->QueryNumWaiters(Extension->PortContext)) {
  550. ParFreePort(Extension);
  551. PortFree = TRUE;
  552. } else {
  553. PortFree = FALSE;
  554. }
  555. KeQueryTickCount(&NextQuery);
  556. Difference.QuadPart = NextQuery.QuadPart - StartOfSpin.QuadPart;
  557. if (Difference.QuadPart*KeQueryTimeIncrement() >=
  558. Extension->AbsoluteOneSecond.QuadPart) {
  559. ParDump(PARTHREAD | PARDUMP_VERBOSE_MAX,
  560. ("PARALLEL: "
  561. "Countdown: %d - device Status: "
  562. "%x lowpart: %x highpart: %x\n",
  563. CountDown,
  564. DeviceStatus,
  565. Difference.LowPart,
  566. Difference.HighPart) );
  567. CountDown--;
  568. DoDelays = TRUE;
  569. }
  570. }
  571. if (CountDown <= 0) {
  572. *BytesTransferred = MaxBytes - BytesToWrite;
  573. status = STATUS_DEVICE_BUSY;
  574. goto returnTarget;
  575. }
  576. if (PortFree && !ParAllocPort(Extension)) {
  577. *BytesTransferred = MaxBytes - BytesToWrite;
  578. status = STATUS_DEVICE_BUSY;
  579. goto returnTarget;
  580. }
  581. DeviceStatus = GetStatus(Extension->Controller);
  582. } while ((!PAR_ONLINE(DeviceStatus)) &&
  583. (!PAR_PAPER_EMPTY(DeviceStatus)) &&
  584. (!PAR_POWERED_OFF(DeviceStatus)) &&
  585. (!PAR_NOT_CONNECTED(DeviceStatus)) &&
  586. (!PAR_NO_CABLE(DeviceStatus)) &&
  587. !(Extension->CurrentOpIrp)->Cancel);
  588. if (CountDown != (LONG)TimerStart) {
  589. ParDump(PARTHREAD | PARDUMP_VERBOSE_MAX,
  590. ("PARALLEL: "
  591. "Leaving busy loop - countdown %d status %x\n",
  592. CountDown, DeviceStatus) );
  593. }
  594. goto PushSomeBytes;
  595. }
  596. returnTarget:
  597. // added single return point so we can save log of bytes transferred
  598. Extension->log.SppWriteCount += *BytesTransferred;
  599. return status;
  600. }
  601. NTSTATUS
  602. SppQueryDeviceId(
  603. IN PDEVICE_EXTENSION Extension,
  604. OUT PUCHAR DeviceIdBuffer,
  605. IN ULONG BufferSize,
  606. OUT PULONG DeviceIdSize,
  607. IN BOOLEAN bReturnRawString
  608. )
  609. /*++
  610. Routine Description:
  611. This routine is now a wrapper function around Par3QueryDeviceId that
  612. preserves the interface of the original SppQueryDeviceId function.
  613. Clients of this function should consider switching to Par3QueryDeviceId
  614. if possible because Par3QueryDeviceId will allocate and return a pointer
  615. to a buffer if the caller supplied buffer is too small to hold the
  616. device ID.
  617. Arguments:
  618. Extension - DeviceExtension/Legacy - used to get controller.
  619. DeviceIdBuffer - Buffer used to return ID.
  620. BufferSize - Size of supplied buffer.
  621. DeviceIdSize - Size of returned ID.
  622. bReturnRawString - Should the 2 byte size prefix be included? (TRUE==Yes)
  623. Return Value:
  624. STATUS_SUCCESS - ID query was successful
  625. STATUS_BUFFER_TOO_SMALL - We were able to read an ID from the device but the caller
  626. supplied buffer was not large enough to hold the ID. The
  627. size required to hold the ID is returned in DeviceIdSize.
  628. STATUS_UNSUCCESSFUL - ID query failed - Possibly interface or device is hung, missed
  629. timeouts during the handshake, or device may not be connected.
  630. --*/
  631. {
  632. PCHAR idBuffer;
  633. ParDumpV(("spp::SppQueryDeviceId: Enter - buffersize=%d\n", BufferSize));
  634. if ( Extension->Ieee1284Flags & ( 1 << Extension->Ieee1284_3DeviceId ) ) {
  635. idBuffer = Par3QueryDeviceId( Extension, DeviceIdBuffer, BufferSize, DeviceIdSize, bReturnRawString, TRUE );
  636. }
  637. else {
  638. idBuffer = Par3QueryDeviceId( Extension, DeviceIdBuffer, BufferSize, DeviceIdSize, bReturnRawString, FALSE );
  639. }
  640. if( idBuffer == NULL ) {
  641. //
  642. // Error at lower level - FAIL query
  643. //
  644. ParDumpV( ("spp::SppQueryDeviceId: call to Par3QueryDeviceId hard FAIL\n") );
  645. return STATUS_UNSUCCESSFUL;
  646. } else if( idBuffer != DeviceIdBuffer ) {
  647. //
  648. // We got a deviceId from the device, but caller's buffer was too small to hold it.
  649. // Free the buffer and tell the caller that the supplied buffer was too small.
  650. //
  651. ParDumpV( ("spp::SppQueryDeviceId: buffer too small - have buffer size=%d, need buffer size=%d\n",
  652. BufferSize, *DeviceIdSize) );
  653. ExFreePool( idBuffer );
  654. return STATUS_BUFFER_TOO_SMALL;
  655. } else {
  656. //
  657. // Query succeeded using caller's buffer (idBuffer == DeviceIdBuffer)
  658. //
  659. ParDumpV( ("spp::SppQueryDeviceId: SUCCESS - deviceId=<%s>\n", idBuffer) );
  660. return STATUS_SUCCESS;
  661. }
  662. }
  663. VOID
  664. ParTerminateSppMode(
  665. IN PDEVICE_EXTENSION Extension
  666. )
  667. /*++
  668. Routine Description:
  669. This routine terminates the interface back to compatibility mode.
  670. Arguments:
  671. Controller - Supplies the parallel port's controller address.
  672. Return Value:
  673. None.
  674. --*/
  675. {
  676. ParDump2(PARENTRY, ( "ParTerminateSppMode: Enter!\r\n" ));
  677. Extension->Connected = FALSE;
  678. Extension->CurrentPhase = PHASE_TERMINATE;
  679. return;
  680. }