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.

1415 lines
48 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1993 - 2000
  3. Module Name:
  4. hwecp.c
  5. Abstract:
  6. This module contains code for the host to utilize HardwareECP if it has been
  7. detected and successfully enabled.
  8. Author:
  9. Robbie Harris (Hewlett-Packard) 21-May-1998
  10. Environment:
  11. Kernel mode
  12. Revision History :
  13. --*/
  14. #include "pch.h"
  15. VOID
  16. ParCleanupHwEcpPort(
  17. IN PPDO_EXTENSION Pdx
  18. )
  19. /*++
  20. Routine Description:
  21. Cleans up prior to a normal termination from ECP mode. Puts the
  22. port HW back into Compatibility mode.
  23. Arguments:
  24. Controller - Supplies the parallel port's controller address.
  25. Return Value:
  26. None.
  27. --*/
  28. {
  29. //----------------------------------------------------------------------
  30. // Set the ECR to mode 001 (PS2 Mode).
  31. //----------------------------------------------------------------------
  32. Pdx->ClearChipMode( Pdx->PortContext, ECR_ECP_PIO_MODE );
  33. Pdx->PortHWMode = HW_MODE_PS2;
  34. ParCleanupSwEcpPort(Pdx);
  35. //----------------------------------------------------------------------
  36. // Set the ECR to mode 000 (Compatibility Mode).
  37. //----------------------------------------------------------------------
  38. Pdx->PortHWMode = HW_MODE_COMPATIBILITY;
  39. }
  40. // Drain data from Shadow Buffer
  41. VOID PptEcpHwDrainShadowBuffer(
  42. IN Queue *pShadowBuffer,
  43. IN PUCHAR lpsBufPtr,
  44. IN ULONG dCount,
  45. OUT ULONG *fifoCount)
  46. {
  47. *fifoCount = 0;
  48. if( Queue_IsEmpty( pShadowBuffer ) ) {
  49. return;
  50. }
  51. while( dCount > 0 ) {
  52. // Break out the Queue_Dequeue from the pointer increment so we can
  53. // observe the data if needed.
  54. if( FALSE == Queue_Dequeue( pShadowBuffer, lpsBufPtr ) ) { // Get byte from queue.
  55. return;
  56. }
  57. ++lpsBufPtr;
  58. --dCount; // Decrement count.
  59. ++(*fifoCount);
  60. }
  61. }
  62. //============================================================================
  63. // NAME: HardwareECP::EmptyFIFO()
  64. //
  65. // Empties HW FIFO into a shadow buffer. This must be done before
  66. // turning the direction from reverse to forward, if the printer has
  67. // stuffed data in that no one has read yet.
  68. //
  69. // PARAMETERS:
  70. // Controller - Supplies the base address of the parallel port.
  71. //
  72. // RETURNS: STATUS_SUCCESS or ....
  73. //
  74. // NOTES:
  75. // Called ZIP_EmptyFIFO in the original 16 bit code.
  76. //
  77. //============================================================================
  78. NTSTATUS ParEcpHwEmptyFIFO(IN PPDO_EXTENSION Pdx)
  79. {
  80. NTSTATUS nError = STATUS_SUCCESS;
  81. Queue *pShadowBuffer;
  82. UCHAR bData;
  83. PUCHAR wPortDFIFO = Pdx->EcrController; // IO address of ECP Data FIFO
  84. PUCHAR wPortECR = Pdx->EcrController + ECR_OFFSET; // IO address of Extended Control Register (ECR)
  85. // While data exists in the FIFO, read it and put it into shadow buffer.
  86. // If the shadow buffer fills up before the FIFO is exhausted, an
  87. // error condition exists.
  88. pShadowBuffer = &(Pdx->ShadowBuffer);
  89. #if 1 == DBG_SHOW_BYTES
  90. if( DbgShowBytes ) {
  91. DbgPrint("r: ");
  92. }
  93. #endif
  94. while ((P5ReadPortUchar(wPortECR) & ECR_FIFO_EMPTY) == 0 ) {
  95. // Break out the Port Read so we can observe the data if needed
  96. bData = P5ReadPortUchar(wPortDFIFO);
  97. #if 1 == DBG_SHOW_BYTES
  98. if( DbgShowBytes ) {
  99. DbgPrint("%02x ",bData);
  100. }
  101. #endif
  102. // Put byte in queue.
  103. if (FALSE == Queue_Enqueue(pShadowBuffer, bData)) {
  104. DD((PCE)Pdx,DDT,"ParEcpHwEmptyFIFO - Shadow buffer full, FIFO not empty\n");
  105. nError = STATUS_BUFFER_OVERFLOW;
  106. goto ParEcpHwEmptyFIFO_ExitLabel;
  107. }
  108. }
  109. #if 1 == DBG_SHOW_BYTES
  110. if( DbgShowBytes ) {
  111. DbgPrint("zz\n");
  112. }
  113. #endif
  114. if( ( !Queue_IsEmpty(pShadowBuffer) && (Pdx->P12843DL.bEventActive) )) {
  115. KeSetEvent(Pdx->P12843DL.Event, 0, FALSE);
  116. }
  117. ParEcpHwEmptyFIFO_ExitLabel:
  118. return nError;
  119. }
  120. //=========================================================
  121. // HardwareECP::ExitForwardPhase
  122. //
  123. // Description : Exit from HWECP Forward Phase to the common phase (FWD IDLE, PS/2)
  124. //
  125. //=========================================================
  126. NTSTATUS ParEcpHwExitForwardPhase( IN PPDO_EXTENSION Pdx )
  127. {
  128. NTSTATUS status;
  129. DD((PCE)Pdx,DDT,"ParEcpHwExitForwardPhase\n");
  130. // First, there could be data in the FIFO. Wait for it to empty
  131. // and then put the bus in the common state (PHASE_FORWARD_IDLE with
  132. // ECRMode set to PS/2
  133. status = ParEcpHwWaitForEmptyFIFO( Pdx );
  134. P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
  135. return status;
  136. }
  137. //=========================================================
  138. // HardwareECP::EnterReversePhase
  139. //
  140. // Description : Go from the common phase to HWECP Reverse Phase
  141. //
  142. //=========================================================
  143. NTSTATUS PptEcpHwEnterReversePhase( IN PPDO_EXTENSION Pdx )
  144. {
  145. NTSTATUS status;
  146. PUCHAR Controller;
  147. PUCHAR wPortECR; // I/O address of Extended Control Register
  148. PUCHAR wPortDCR; // I/O address of Device Control Register
  149. UCHAR dcr;
  150. Controller = Pdx->Controller;
  151. wPortECR = Pdx->EcrController + ECR_OFFSET;
  152. wPortDCR = Controller + OFFSET_DCR;
  153. // EnterReversePhase assumes that we are in PHASE_FORWARD_IDLE,
  154. // and that the ECPMode is set to PS/2 mode at entry.
  155. //----------------------------------------------------------------------
  156. // Set the ECR to mode 001 (PS2 Mode).
  157. //----------------------------------------------------------------------
  158. Pdx->ClearChipMode( Pdx->PortContext, ECR_ECP_PIO_MODE );
  159. // We need to be in PS/2 (BiDi) mode in order to disable the host
  160. // driving the data lines when we flip the direction bit in the DCR.
  161. // This is a requirement for entering ECP state 38 in the 1284 spec.
  162. // Changed - 2000-02-11
  163. status = Pdx->TrySetChipMode( Pdx->PortContext, ECR_BYTE_MODE );
  164. // ignore status - subsequent operations may still work
  165. Pdx->PortHWMode = HW_MODE_PS2;
  166. if ( Pdx->ModeSafety == SAFE_MODE ) {
  167. // Reverse the bus first (using ECP::EnterReversePhase)
  168. status = ParEcpEnterReversePhase(Pdx);
  169. if ( NT_SUCCESS(status) ) {
  170. //----------------------------------------------------------------------
  171. // Wait for nAckReverse low (ECP State 40)
  172. //----------------------------------------------------------------------
  173. if ( !CHECK_DSR(Controller, DONT_CARE, DONT_CARE, INACTIVE, ACTIVE, DONT_CARE, IEEE_MAXTIME_TL) ) {
  174. DD((PCE)Pdx,DDT,"PptEcpHwEnterReversePhase: State 40 failed\n");
  175. status = ParEcpHwRecoverPort( Pdx, RECOVER_28 );
  176. if ( NT_SUCCESS(status))
  177. status = STATUS_LINK_FAILED;
  178. goto PptEcpHwEnterReversePhase_ExitLabel;
  179. } else {
  180. P5SetPhase( Pdx, PHASE_REVERSE_IDLE );
  181. }
  182. }
  183. } else {
  184. //----------------------------------------------------------------------
  185. // Set Dir=1 in DCR for reading.
  186. //----------------------------------------------------------------------
  187. dcr = P5ReadPortUchar( wPortDCR ); // Get content of DCR.
  188. dcr = UPDATE_DCR( dcr, DIR_READ, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE );
  189. P5WritePortUchar(wPortDCR, dcr);
  190. }
  191. //----------------------------------------------------------------------
  192. // Set the ECR to mode 011 (ECP Mode). DmaEnable=0.
  193. //----------------------------------------------------------------------
  194. status = Pdx->TrySetChipMode ( Pdx->PortContext, ECR_ECP_PIO_MODE );
  195. if ( !NT_SUCCESS(status) ) {
  196. DD((PCE)Pdx,DDT,"PptEcpHwEnterReversePhase - TrySetChipMode failed\n");
  197. }
  198. Pdx->PortHWMode = HW_MODE_ECP;
  199. //----------------------------------------------------------------------
  200. // Set nStrobe=0 and nAutoFd=0 in DCR, so that ECP HW can control.
  201. //----------------------------------------------------------------------
  202. dcr = P5ReadPortUchar( wPortDCR ); // Get content of DCR.
  203. dcr = UPDATE_DCR( dcr, DIR_READ, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE);
  204. P5WritePortUchar( wPortDCR, dcr );
  205. // Set the phase variable to ReverseIdle
  206. P5SetPhase( Pdx, PHASE_REVERSE_IDLE );
  207. PptEcpHwEnterReversePhase_ExitLabel:
  208. return status;
  209. }
  210. //=========================================================
  211. // HardwareECP::ExitReversePhase
  212. //
  213. // Description : Get out of HWECP Reverse Phase to the common state
  214. //
  215. //=========================================================
  216. NTSTATUS ParEcpHwExitReversePhase( IN PPDO_EXTENSION Pdx )
  217. {
  218. NTSTATUS nError = STATUS_SUCCESS;
  219. UCHAR bDCR;
  220. UCHAR bECR;
  221. PUCHAR wPortECR;
  222. PUCHAR wPortDCR;
  223. PUCHAR Controller;
  224. DD((PCE)Pdx,DDT,"ParEcpHwExitReversePhase - enter\n");
  225. Controller = Pdx->Controller;
  226. wPortECR = Pdx->EcrController + ECR_OFFSET;
  227. wPortDCR = Controller + OFFSET_DCR;
  228. //----------------------------------------------------------------------
  229. // Set status byte to indicate Reverse To Forward Mode.
  230. //----------------------------------------------------------------------
  231. P5SetPhase( Pdx, PHASE_REV_TO_FWD );
  232. if ( Pdx->ModeSafety == SAFE_MODE ) {
  233. //----------------------------------------------------------------------
  234. // Assert nReverseRequest high. This should stop further data transfer
  235. // into the FIFO. [[REVISIT: does the chip handle this correctly
  236. // if it occurs in the middle of a byte transfer (states 43-46)??
  237. // Answer (10/9/95) no, it doesn't!!]]
  238. //----------------------------------------------------------------------
  239. bDCR = P5ReadPortUchar(wPortDCR); // Get content of DCR.
  240. bDCR = UPDATE_DCR( bDCR, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, DONT_CARE, DONT_CARE );
  241. P5WritePortUchar(wPortDCR, bDCR );
  242. //----------------------------------------------------------------------
  243. // Wait for PeriphAck low and PeriphClk high (ECP state 48) together
  244. // with nAckReverse high (ECP state 49).
  245. //----------------------------------------------------------------------
  246. if( !CHECK_DSR(Controller, INACTIVE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE, DEFAULT_RECEIVE_TIMEOUT) ) {
  247. DD((PCE)Pdx,DDW,"ParEcpHwExitReversePhase: Periph failed state 48/49.\n");
  248. nError = ParEcpHwRecoverPort( Pdx, RECOVER_37 ); // Reset port.
  249. if( NT_SUCCESS(nError) ) {
  250. return STATUS_LINK_FAILED;
  251. }
  252. return nError;
  253. }
  254. //-----------------------------------------------------------------------
  255. // Empty the HW FIFO of any bytes that may have already come in.
  256. // This must be done before changing ECR modes because the FIFO is reset
  257. // when that occurs.
  258. //-----------------------------------------------------------------------
  259. bECR = P5ReadPortUchar(wPortECR); // Get content of ECR.
  260. if ((bECR & ECR_FIFO_EMPTY) == 0) { // Check if FIFO is not empty.
  261. if( (nError = ParEcpHwEmptyFIFO(Pdx)) != STATUS_SUCCESS ) {
  262. DD((PCE)Pdx,DDT,"ParEcpHwExitReversePhase: Attempt to empty ECP chip failed.\n");
  263. return nError;
  264. }
  265. }
  266. //----------------------------------------------------------------------
  267. // Assert HostAck and HostClk high. [[REVISIT: is this necessary?
  268. // should already be high...]]
  269. //----------------------------------------------------------------------
  270. bDCR = UPDATE_DCR( bDCR, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE );
  271. P5WritePortUchar(wPortDCR, bDCR );
  272. } // SAFE_MODE
  273. //----------------------------------------------------------------------
  274. // Set the ECR to PS2 Mode so we can change bus direction.
  275. //----------------------------------------------------------------------
  276. Pdx->ClearChipMode( Pdx->PortContext, ECR_ECP_PIO_MODE );
  277. Pdx->PortHWMode = HW_MODE_PS2;
  278. //----------------------------------------------------------------------
  279. // Set Dir=0 (Write) in DCR.
  280. //----------------------------------------------------------------------
  281. bDCR = P5ReadPortUchar(wPortDCR);
  282. bDCR = UPDATE_DCR( bDCR, DIR_WRITE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE );
  283. P5WritePortUchar(wPortDCR, bDCR );
  284. //----------------------------------------------------------------------
  285. // Set the ECR back to ECP Mode. DmaEnable=0.
  286. //----------------------------------------------------------------------
  287. nError = Pdx->TrySetChipMode ( Pdx->PortContext, ECR_ECP_PIO_MODE );
  288. Pdx->PortHWMode = HW_MODE_ECP;
  289. P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
  290. return nError;
  291. }
  292. BOOLEAN
  293. PptEcpHwHaveReadData (
  294. IN PPDO_EXTENSION Pdx
  295. )
  296. {
  297. Queue *pQueue;
  298. // check shadow buffer
  299. pQueue = &(Pdx->ShadowBuffer);
  300. if (!Queue_IsEmpty(pQueue)) {
  301. return TRUE;
  302. }
  303. // check periph
  304. if (ParEcpHaveReadData(Pdx))
  305. return TRUE;
  306. // Check if FIFO is not empty.
  307. return (BOOLEAN)( (UCHAR)0 == (P5ReadPortUchar(Pdx->EcrController + ECR_OFFSET) & ECR_FIFO_EMPTY) );
  308. }
  309. NTSTATUS
  310. ParEcpHwHostRecoveryPhase(
  311. IN PPDO_EXTENSION Pdx
  312. )
  313. {
  314. NTSTATUS status = STATUS_SUCCESS;
  315. PUCHAR pPortDCR; // I/O address of Device Control Register
  316. PUCHAR pPortDSR; // I/O address of Device Status Register
  317. PUCHAR pPortECR; // I/O address of Extended Control Register
  318. UCHAR bDCR; // Contents of DCR
  319. UCHAR bDSR; // Contents of DSR
  320. if( !Pdx->bIsHostRecoverSupported ) {
  321. return STATUS_SUCCESS;
  322. }
  323. DD((PCE)Pdx,DDT,"ParEcpHwHostRecoveryPhase - enter\n");
  324. // Calculate I/O port addresses for common registers
  325. pPortDCR = Pdx->Controller + OFFSET_DCR;
  326. pPortDSR = Pdx->Controller + OFFSET_DSR;
  327. pPortECR = Pdx->EcrController + ECR_OFFSET;
  328. // Set the ECR to mode 001 (PS2 Mode)
  329. // Don't need to flip to Byte mode. The ECR arbitrator will handle this.
  330. Pdx->PortHWMode = HW_MODE_PS2;
  331. // Set Dir=1 in DCR to disable host bus drive, because the peripheral may
  332. // try to drive the bus during host recovery phase. We are not really going
  333. // to let any data handshake across, because we don't set HostAck low, and
  334. // we don't enable the ECP chip during this phase.
  335. bDCR = P5ReadPortUchar(pPortDCR); // Get content of DCR.
  336. bDCR = UPDATE_DCR( bDCR, DIR_READ, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE );
  337. P5WritePortUchar(pPortDCR, bDCR );
  338. // Check the DCR to see if it has been stomped on
  339. bDCR = P5ReadPortUchar( pPortDCR );
  340. if( TEST_DCR( bDCR, DIR_WRITE, DONT_CARE, ACTIVE, ACTIVE, DONT_CARE, DONT_CARE ) ) {
  341. // DCR ok, now test DSR for valid state, ignoring PeriphAck since it could change
  342. bDSR = P5ReadPortUchar( pPortDSR );
  343. // 11/21/95 LLL, CGM: change test to look for XFlag high
  344. if( TEST_DSR( bDSR, DONT_CARE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE ) ) {
  345. // Drop ReverseRequest to initiate host recovery
  346. bDCR = UPDATE_DCR( bDCR, DONT_CARE, DONT_CARE, DONT_CARE, INACTIVE, DONT_CARE, DONT_CARE );
  347. P5WritePortUchar( pPortDCR, bDCR );
  348. // Wait for nAckReverse response
  349. // 11/21/95 LLL, CGM: tightened test to include PeriphClk and XFlag.
  350. // "ZIP_HRP: state 73, DSR"
  351. if( CHECK_DSR( Pdx->Controller, DONT_CARE, ACTIVE, INACTIVE, ACTIVE, DONT_CARE, IEEE_MAXTIME_TL) ) {
  352. // Yes, raise nReverseRequest, HostClk and HostAck (HostAck high so HW can drive)
  353. bDCR = UPDATE_DCR( bDCR, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE, ACTIVE );
  354. P5WritePortUchar( pPortDCR, bDCR );
  355. // Wait for nAckReverse response
  356. // 11/21/95 LLL, CGM: tightened test to include XFlag and PeriphClk.
  357. // "ZIP_HRP: state 75, DSR"
  358. if( CHECK_DSR( Pdx->Controller, DONT_CARE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE, IEEE_MAXTIME_TL) ) {
  359. // Let the host drive the bus again.
  360. bDCR = P5ReadPortUchar(pPortDCR); // Get content of DCR.
  361. bDCR = UPDATE_DCR( bDCR, DIR_WRITE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE );
  362. P5WritePortUchar(pPortDCR, bDCR );
  363. // Recovery is complete, let the caller decide what to do now
  364. status = STATUS_SUCCESS;
  365. P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
  366. } else {
  367. status = STATUS_IO_TIMEOUT;
  368. DD((PCE)Pdx,DDW,"ParEcpHwHostRecoveryPhase - error prior to state 75\n");
  369. }
  370. } else {
  371. status = STATUS_IO_TIMEOUT;
  372. DD((PCE)Pdx,DDW,"ParEcpHwHostRecoveryPhase - error prior to state 73\n");
  373. }
  374. } else {
  375. #if DVRH_BUS_RESET_ON_ERROR
  376. BusReset(pPortDCR); // Pass in the dcr address
  377. #endif
  378. DD((PCE)Pdx,DDT, "ParEcpHwHostRecoveryPhase: VE_LINK_FAILURE \n");
  379. status = STATUS_LINK_FAILED;
  380. }
  381. } else {
  382. DD((PCE)Pdx,DDW,"ParEcpHwHostRecoveryPhase: VE_PORT_STOMPED\n");
  383. status = STATUS_DEVICE_PROTOCOL_ERROR;
  384. }
  385. if (!NT_SUCCESS(status)) {
  386. // Make sure both HostAck and HostClk are high before leaving
  387. // Also let the host drive the bus again.
  388. bDCR = P5ReadPortUchar( pPortDCR );
  389. bDCR = UPDATE_DCR( bDCR, DIR_WRITE, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE );
  390. P5WritePortUchar( pPortDCR, bDCR );
  391. // [[REVISIT]] pSDCB->wCurrentPhase = PHASE_UNKNOWN;
  392. }
  393. // Set the ECR to ECP mode, disable DMA
  394. status = Pdx->TrySetChipMode ( Pdx->PortContext, ECR_ECP_PIO_MODE );
  395. Pdx->PortHWMode = HW_MODE_ECP;
  396. DD((PCE)Pdx,DDT,"ParEcpHwHostRecoveryPhase - Exit w/status = %x\n", status);
  397. return status;
  398. }
  399. NTSTATUS
  400. ParEcpHwRead(
  401. IN PPDO_EXTENSION Pdx,
  402. IN PVOID Buffer,
  403. IN ULONG BufferSize,
  404. OUT PULONG BytesTransferred
  405. )
  406. /*++
  407. Routine Description:
  408. This routine performs a 1284 ECP mode read under Hardware control
  409. into the given buffer for no more than 'BufferSize' bytes.
  410. Arguments:
  411. Pdx - Supplies the device extension.
  412. Buffer - Supplies the buffer to read into.
  413. BufferSize - Supplies the number of bytes in the buffer.
  414. BytesTransferred - Returns the number of bytes transferred.
  415. --*/
  416. {
  417. NTSTATUS status = STATUS_SUCCESS;
  418. PUCHAR lpsBufPtr = (PUCHAR)Buffer; // Pointer to buffer cast to desired data type
  419. ULONG dCount = BufferSize; // Working copy of caller's original request count
  420. UCHAR bDSR; // Contents of DSR
  421. UCHAR bPeriphRequest; // Calculated state of nPeriphReq signal, used in loop
  422. PUCHAR wPortDSR = Pdx->Controller + DSR_OFFSET;
  423. PUCHAR wPortECR = Pdx->EcrController + ECR_OFFSET;
  424. PUCHAR wPortDFIFO = Pdx->EcrController;
  425. LARGE_INTEGER WaitPerByteTimer;
  426. LARGE_INTEGER StartPerByteTimer;
  427. LARGE_INTEGER EndPerByteTimer;
  428. BOOLEAN bResetTimer = TRUE;
  429. ULONG wBurstCount; // Calculated amount of data in FIFO
  430. UCHAR ecrFIFO;
  431. WaitPerByteTimer.QuadPart = (35 * 10 * 1000) + KeQueryTimeIncrement();
  432. //----------------------------------------------------------------------
  433. // Set status byte to indicate Reverse Transfer Phase.
  434. //----------------------------------------------------------------------
  435. P5SetPhase( Pdx, PHASE_REVERSE_XFER );
  436. //----------------------------------------------------------------------
  437. // We've already checked the shadow in ParRead. So go right to the
  438. // Hardware FIFO and pull more data across.
  439. //----------------------------------------------------------------------
  440. KeQueryTickCount(&StartPerByteTimer); // Start the timer
  441. ParEcpHwRead_ReadLoopStart:
  442. //------------------------------------------------------------------
  443. // Determine whether the FIFO has any data and respond accordingly
  444. //------------------------------------------------------------------
  445. ecrFIFO = (UCHAR)(P5ReadPortUchar(wPortECR) & (UCHAR)ECR_FIFO_MASK);
  446. if (ECR_FIFO_FULL == ecrFIFO) {
  447. wBurstCount = ( dCount > Pdx->FifoDepth ? Pdx->FifoDepth : dCount );
  448. dCount -= wBurstCount;
  449. P5ReadPortBufferUchar(wPortDFIFO, lpsBufPtr, wBurstCount);
  450. lpsBufPtr += wBurstCount;
  451. bResetTimer = TRUE;
  452. } else if (ECR_FIFO_SOME_DATA == ecrFIFO) {
  453. // Read just one byte at a time, since we don't know exactly how much is
  454. // in the FIFO.
  455. *lpsBufPtr = P5ReadPortUchar(wPortDFIFO);
  456. lpsBufPtr++;
  457. dCount--;
  458. bResetTimer = TRUE;
  459. } else { // ECR_FIFO_EMPTY
  460. DD((PCE)Pdx,DDW,"ParEcpHwRead - ECR_FIFO_EMPTY - slow or bad periph?\n");
  461. // Nothing to do. We either have a slow peripheral or a bad peripheral.
  462. // We don't have a good way to figure out if its bad. Let's chew up our
  463. // time and hope for the best.
  464. bResetTimer = FALSE;
  465. } // ECR_FIFO_EMPTY a.k.a. else clause of (ECR_FIFO_FULL == ecrFIFO)
  466. if (dCount == 0)
  467. goto ParEcpHwRead_ReadLoopEnd;
  468. else {
  469. // Limit the overall time we spend in this loop.
  470. if (bResetTimer) {
  471. bResetTimer = FALSE;
  472. KeQueryTickCount(&StartPerByteTimer); // Restart the timer
  473. } else {
  474. KeQueryTickCount(&EndPerByteTimer);
  475. if (((EndPerByteTimer.QuadPart - StartPerByteTimer.QuadPart) * KeQueryTimeIncrement()) > WaitPerByteTimer.QuadPart)
  476. goto ParEcpHwRead_ReadLoopEnd;
  477. }
  478. }
  479. goto ParEcpHwRead_ReadLoopStart;
  480. ParEcpHwRead_ReadLoopEnd:
  481. P5SetPhase( Pdx, PHASE_REVERSE_IDLE );
  482. *BytesTransferred = BufferSize - dCount; // Set current count.
  483. Pdx->log.HwEcpReadCount += *BytesTransferred;
  484. if (0 == *BytesTransferred) {
  485. bDSR = P5ReadPortUchar(wPortDSR);
  486. bPeriphRequest = (UCHAR)TEST_DSR( bDSR, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, INACTIVE );
  487. // Only flag a timeout error if the device still said it had data to send.
  488. if ( bPeriphRequest ) {
  489. //
  490. // Periph still says that it has data, but we timed out trying to read the data.
  491. //
  492. DD((PCE)Pdx,DDE,"ParEcpHwRead - read timout with nPeriphRequest asserted and no data read - error - STATUS_IO_TIMEOUT\n");
  493. status = STATUS_IO_TIMEOUT;
  494. if ((TRUE == Pdx->P12843DL.bEventActive) ) {
  495. //
  496. // Signal transport that it should try another read
  497. //
  498. KeSetEvent(Pdx->P12843DL.Event, 0, FALSE);
  499. }
  500. }
  501. }
  502. DD((PCE)Pdx,DDT,"ParEcpHwRead: Exit - status=%x, BytesTransferred[%d] dsr[%02x] dcr[%02x] ecr[%02x]\n",
  503. status, *BytesTransferred, P5ReadPortUchar(wPortDSR),
  504. P5ReadPortUchar(Pdx->Controller + OFFSET_DCR), P5ReadPortUchar(wPortECR));
  505. #if 1 == DBG_SHOW_BYTES
  506. if( DbgShowBytes ) {
  507. if( NT_SUCCESS( status ) && (*BytesTransferred > 0) ) {
  508. const ULONG maxBytes = 32;
  509. ULONG i;
  510. PUCHAR bytePtr = (PUCHAR)Buffer;
  511. DbgPrint("R: ");
  512. for( i=0 ; (i < *BytesTransferred) && (i < maxBytes ) ; ++i ) {
  513. DbgPrint("%02x ",*bytePtr++);
  514. }
  515. if( *BytesTransferred > maxBytes ) {
  516. DbgPrint("... ");
  517. }
  518. DbgPrint("zz\n");
  519. }
  520. }
  521. #endif
  522. return status;
  523. } // ParEcpHwRead
  524. NTSTATUS
  525. ParEcpHwRecoverPort(
  526. PPDO_EXTENSION Pdx,
  527. UCHAR bRecoverCode
  528. )
  529. {
  530. NTSTATUS status = STATUS_SUCCESS;
  531. PUCHAR wPortDCR; // IO address of Device Control Register (DCR)
  532. PUCHAR wPortDSR; // IO address of Device Status Register (DSR)
  533. PUCHAR wPortECR; // IO address of Extended Control Register (ECR)
  534. PUCHAR wPortData; // IO address of Data Register
  535. UCHAR bDCR; // Contents of DCR
  536. UCHAR bDSR; // Contents of DSR
  537. UCHAR bDSRmasked; // DSR after masking low order bits
  538. DD((PCE)Pdx,DDT,"ParEcpHwRecoverPort: enter %d\n", bRecoverCode );
  539. // Calculate I/O port addresses for common registers
  540. wPortDCR = Pdx->Controller + OFFSET_DCR;
  541. wPortDSR = Pdx->Controller + OFFSET_DSR;
  542. wPortECR = Pdx->EcrController + ECR_OFFSET;
  543. wPortData = Pdx->Controller + OFFSET_DATA;
  544. //----------------------------------------------------------------------
  545. // Check if port is stomped.
  546. //----------------------------------------------------------------------
  547. bDCR = P5ReadPortUchar(wPortDCR); // Get content of DCR.
  548. if ( ! TEST_DCR( bDCR, DONT_CARE, DONT_CARE, ACTIVE, DONT_CARE, DONT_CARE, DONT_CARE ) )
  549. {
  550. #if DVRH_BUS_RESET_ON_ERROR
  551. BusReset(wPortDCR); // Pass in the dcr address
  552. #endif
  553. DD((PCE)Pdx,DDE,"ParEcpHwRecoverPort - port stomped\n");
  554. status = STATUS_DEVICE_PROTOCOL_ERROR;
  555. }
  556. //----------------------------------------------------------------------
  557. // Attempt a termination phase to get the peripheral recovered.
  558. // Ignore the error return, we've already got that figured out.
  559. //----------------------------------------------------------------------
  560. IeeeTerminate1284Mode(Pdx );
  561. //----------------------------------------------------------------------
  562. // Set the ECR to PS2 Mode so we can change bus direction.
  563. //----------------------------------------------------------------------
  564. Pdx->ClearChipMode( Pdx->PortContext, ECR_ECP_PIO_MODE );
  565. Pdx->PortHWMode = HW_MODE_PS2;
  566. //----------------------------------------------------------------------
  567. // Assert nSelectIn low, nInit high, nStrobe high, and nAutoFd high.
  568. //----------------------------------------------------------------------
  569. bDCR = P5ReadPortUchar(wPortDCR); // Get content of DCR.
  570. bDCR = UPDATE_DCR( bDCR, DIR_WRITE, DONT_CARE, INACTIVE, ACTIVE, ACTIVE, ACTIVE );
  571. P5WritePortUchar(wPortDCR, bDCR);
  572. P5WritePortUchar(wPortData, bRecoverCode); // Output the error ID
  573. KeStallExecutionProcessor(100); // Hold long enough to capture
  574. P5WritePortUchar(wPortData, 0); // Now clear the data lines.
  575. //----------------------------------------------------------------------
  576. // Set the ECR to mode 000 (Compatibility Mode).
  577. //----------------------------------------------------------------------
  578. // Nothing needs to be done here.
  579. Pdx->PortHWMode = HW_MODE_COMPATIBILITY;
  580. //----------------------------------------------------------------------
  581. // Check for any link errors if nothing bad found yet.
  582. //----------------------------------------------------------------------
  583. bDSR = P5ReadPortUchar(wPortDSR); // Get content of DSR.
  584. bDSRmasked = (UCHAR)(bDSR | 0x07); // Set first 3 bits (don't cares).
  585. if( NT_SUCCESS(status) ) {
  586. if (bDSRmasked != 0xDF) {
  587. DD((PCE)Pdx,DDE,"ParEcpHwRecoverPort - DSR Exp value: 0xDF, Act value: 0x%X\n",bDSRmasked);
  588. // Get DSR again just to make sure...
  589. bDSR = P5ReadPortUchar(wPortDSR); // Get content of DSR.
  590. bDSRmasked = (UCHAR)(bDSR | 0x07); // Set first 3 bits (don't cares).
  591. if( (CHKPRNOFF1 == bDSRmasked ) || (CHKPRNOFF2 == bDSRmasked ) ) { // Check for printer off.
  592. DD((PCE)Pdx,DDW,"ParEcpHwRecoverPort - DSR value: 0x%X, Printer Off\n", bDSRmasked);
  593. status = STATUS_DEVICE_POWERED_OFF;
  594. } else {
  595. if( CHKNOCABLE == bDSRmasked ) { // Check for cable unplugged.
  596. DD((PCE)Pdx,DDW,"ParEcpHwRecoverPort - DSR value: 0x%X, Cable Unplugged\n",bDSRmasked);
  597. status = STATUS_DEVICE_NOT_CONNECTED;
  598. } else {
  599. DD((PCE)Pdx,DDW,"ParEcpHwRecoverPort - DSR value: 0x%X, Unknown error\n",bDSRmasked);
  600. status = STATUS_LINK_FAILED;
  601. }
  602. }
  603. }
  604. }
  605. //----------------------------------------------------------------------
  606. // Set status byte to indicate Compatibility Mode.
  607. //----------------------------------------------------------------------
  608. P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
  609. return status;
  610. } // ParEcpHwRecoverPort
  611. NTSTATUS
  612. ParEcpHwSetAddress(
  613. IN PPDO_EXTENSION Pdx,
  614. IN UCHAR Address
  615. )
  616. /*++
  617. Routine Description:
  618. Sets the ECP Address.
  619. Arguments:
  620. Pdx - Supplies the device extension.
  621. Address - The bus address to be set.
  622. Return Value:
  623. None.
  624. --*/
  625. {
  626. NTSTATUS status = STATUS_SUCCESS;
  627. PUCHAR wPortDSR; // IO address of Device Status Register
  628. PUCHAR wPortECR; // IO address of Extended Control Register
  629. PUCHAR wPortAFIFO; // IO address of ECP Address FIFO
  630. UCHAR bDSR; // Contents of DSR
  631. UCHAR bECR; // Contents of ECR
  632. BOOLEAN bDone;
  633. DD((PCE)Pdx,DDT,"ParEcpHwSetAddress, Start\n");
  634. // Calculate I/O port addresses for common registers
  635. wPortDSR = Pdx->Controller + DSR_OFFSET;
  636. wPortECR = Pdx->EcrController + ECR_OFFSET;
  637. wPortAFIFO = Pdx->Controller + AFIFO_OFFSET;
  638. //----------------------------------------------------------------------
  639. // Check for any link errors.
  640. //----------------------------------------------------------------------
  641. //ZIP_CHECK_PORT( DONT_CARE, DONT_CARE, ACTIVE, ACTIVE, DONT_CARE, DONT_CARE,
  642. // "ZIP_SCA: init DCR", RECOVER_40, errorExit );
  643. //ZIP_CHECK_LINK( DONT_CARE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE,
  644. // "ZIP_SCA: init DSR", RECOVER_41, errorExit );
  645. // Set state to indicate ECP forward transfer phase
  646. P5SetPhase( Pdx, PHASE_FORWARD_XFER );
  647. //----------------------------------------------------------------------
  648. // Send ECP channel address to AFIFO.
  649. //----------------------------------------------------------------------
  650. if ( ! ( TEST_ECR_FIFO( P5ReadPortUchar( wPortECR), ECR_FIFO_EMPTY ) ? TRUE :
  651. CheckPort( wPortECR, ECR_FIFO_MASK, ECR_FIFO_EMPTY, IEEE_MAXTIME_TL ) ) ) {
  652. status = ParEcpHwHostRecoveryPhase(Pdx);
  653. DD((PCE)Pdx,DDT,"ParEcpHwSetAddress: FIFO full, timeout sending ECP channel address\n");
  654. status = STATUS_IO_DEVICE_ERROR;
  655. } else {
  656. // Send the address byte. The most significant bit must be set to distinquish
  657. // it as an address (as opposed to a run-length compression count).
  658. P5WritePortUchar(wPortAFIFO, (UCHAR)(Address | 0x80));
  659. }
  660. if ( NT_SUCCESS(status) ) {
  661. // If there have been no previous errors, and synchronous writes
  662. // have been requested, wait for the FIFO to empty and the device to
  663. // complete the last PeriphAck handshake before returning success.
  664. if ( Pdx->bSynchWrites ) {
  665. LARGE_INTEGER Wait;
  666. LARGE_INTEGER Start;
  667. LARGE_INTEGER End;
  668. // we wait up to 35 milliseconds.
  669. Wait.QuadPart = (IEEE_MAXTIME_TL * 10 * 1000) + KeQueryTimeIncrement(); // 35ms
  670. KeQueryTickCount(&Start);
  671. bDone = FALSE;
  672. while ( ! bDone )
  673. {
  674. bECR = P5ReadPortUchar( wPortECR );
  675. bDSR = P5ReadPortUchar( wPortDSR );
  676. // LLL/CGM 10/9/95: Tighten up link test - PeriphClk high
  677. if ( TEST_ECR_FIFO( bECR, ECR_FIFO_EMPTY ) &&
  678. TEST_DSR( bDSR, INACTIVE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE ) ) {
  679. bDone = TRUE;
  680. } else {
  681. KeQueryTickCount(&End);
  682. if ((End.QuadPart - Start.QuadPart) * KeQueryTimeIncrement() > Wait.QuadPart) {
  683. DD((PCE)Pdx,DDT,"ParEcpHwSetAddress, timeout during synch\n");
  684. bDone = TRUE;
  685. status = ParEcpHwHostRecoveryPhase(Pdx);
  686. status = STATUS_IO_DEVICE_ERROR;
  687. }
  688. }
  689. } // of while...
  690. } // if bSynchWrites...
  691. }
  692. if ( NT_SUCCESS(status) ) {
  693. // Update the state to reflect that we are back in an idle phase
  694. P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
  695. } else if ( status == STATUS_IO_DEVICE_ERROR ) {
  696. // Update the state to reflect that we are back in an idle phase
  697. P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
  698. }
  699. return status;
  700. }
  701. NTSTATUS
  702. ParEcpHwSetupPhase(
  703. IN PPDO_EXTENSION Pdx
  704. )
  705. /*++
  706. Routine Description:
  707. This routine performs 1284 Setup Phase.
  708. Arguments:
  709. Controller - Supplies the port address.
  710. Return Value:
  711. STATUS_SUCCESS - Successful negotiation.
  712. otherwise - Unsuccessful negotiation.
  713. --*/
  714. {
  715. NTSTATUS Status = STATUS_SUCCESS;
  716. PUCHAR pPortDCR; // IO address of Device Control Register (DCR)
  717. PUCHAR pPortDSR; // IO address of Device Status Register (DSR)
  718. PUCHAR pPortECR; // IO address of Extended Control Register (ECR)
  719. UCHAR bDCR; // Contents of DCR
  720. // Calculate I/O port addresses for common registers
  721. pPortDCR = Pdx->Controller + OFFSET_DCR;
  722. pPortDSR = Pdx->Controller + OFFSET_DSR;
  723. pPortECR = Pdx->EcrController + ECR_OFFSET;
  724. // Get the DCR and make sure port hasn't been stomped
  725. //ZIP_CHECK_PORT( DIR_WRITE, DONT_CARE, ACTIVE, ACTIVE, DONT_CARE, DONT_CARE,
  726. // "ZIP_SP: init DCR", RECOVER_44, exit1 );
  727. // Set HostAck low
  728. bDCR = P5ReadPortUchar(pPortDCR); // Get content of DCR.
  729. bDCR = UPDATE_DCR( bDCR, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, INACTIVE, DONT_CARE );
  730. P5WritePortUchar( pPortDCR, bDCR );
  731. // for some reason dvdr doesn't want an extra check in UNSAFE_MODE
  732. if ( Pdx->ModeSafety == SAFE_MODE ) {
  733. // Wait for nAckReverse to go high
  734. // LLL/CGM 10/9/95: look for PeriphAck low, PeriphClk high as per 1284 spec.
  735. if ( !CHECK_DSR(Pdx->Controller, INACTIVE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE,
  736. IEEE_MAXTIME_TL ) )
  737. {
  738. // Any failure leaves us in an unknown state to recover from.
  739. P5SetPhase( Pdx, PHASE_UNKNOWN );
  740. Status = STATUS_IO_DEVICE_ERROR;
  741. goto HWECP_SetupPhaseExitLabel;
  742. }
  743. }
  744. //----------------------------------------------------------------------
  745. // Set the ECR to mode 001 (PS2 Mode).
  746. //----------------------------------------------------------------------
  747. Status = Pdx->TrySetChipMode ( Pdx->PortContext, ECR_ECP_PIO_MODE );
  748. // Set DCR: DIR=0 for output, HostAck and HostClk high so HW can drive
  749. bDCR = UPDATE_DCR( bDCR, DIR_WRITE, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE );
  750. P5WritePortUchar( pPortDCR, bDCR );
  751. // Set the ECR to ECP mode, disable DMA
  752. Pdx->PortHWMode = HW_MODE_ECP;
  753. // If setup was successful, mark the new ECP phase.
  754. P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
  755. Status = STATUS_SUCCESS;
  756. HWECP_SetupPhaseExitLabel:
  757. DD((PCE)Pdx,DDT,"ParEcpHwSetupPhase - exit w/status=%x\n",Status);
  758. return Status;
  759. }
  760. NTSTATUS ParEcpHwWaitForEmptyFIFO(IN PPDO_EXTENSION Pdx)
  761. /*++
  762. Routine Description:
  763. This routine will babysit the Fifo.
  764. Arguments:
  765. Pdx - The device extension.
  766. Return Value:
  767. NTSTATUS.
  768. --*/
  769. {
  770. UCHAR bDSR; // Contents of DSR
  771. UCHAR bECR; // Contents of ECR
  772. UCHAR bDCR; // Contents of ECR
  773. BOOLEAN bDone = FALSE;
  774. PUCHAR wPortDSR;
  775. PUCHAR wPortECR;
  776. PUCHAR wPortDCR;
  777. LARGE_INTEGER Wait;
  778. LARGE_INTEGER Start;
  779. LARGE_INTEGER End;
  780. NTSTATUS status = STATUS_SUCCESS;
  781. // Calculate I/O port addresses for common registers
  782. wPortDSR = Pdx->Controller + OFFSET_DSR;
  783. wPortECR = Pdx->EcrController + ECR_OFFSET;
  784. wPortDCR = Pdx->Controller + OFFSET_DCR;
  785. Wait.QuadPart = (330 * 10 * 1000) + KeQueryTimeIncrement(); // 330ms
  786. KeQueryTickCount(&Start);
  787. //--------------------------------------------------------------------
  788. // wait for the FIFO to empty and the last
  789. // handshake of PeriphAck to complete before returning success.
  790. //--------------------------------------------------------------------
  791. while ( ! bDone )
  792. {
  793. bECR = P5ReadPortUchar(wPortECR);
  794. bDSR = P5ReadPortUchar(wPortDSR);
  795. bDCR = P5ReadPortUchar(wPortDCR);
  796. #if 0 // one bit differs - keep alternate around until we know which to really use
  797. if ( TEST_ECR_FIFO( bECR, ECR_FIFO_EMPTY ) &&
  798. TEST_DCR( bDCR, INACTIVE, ***INACTIVE***, ACTIVE, ACTIVE, DONT_CARE, ACTIVE ) &&
  799. TEST_DSR( bDSR, INACTIVE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE ) ) {
  800. #else
  801. if ( TEST_ECR_FIFO( bECR, ECR_FIFO_EMPTY ) &&
  802. TEST_DCR( bDCR, INACTIVE, DONT_CARE, ACTIVE, ACTIVE, DONT_CARE, ACTIVE ) &&
  803. TEST_DSR( bDSR, INACTIVE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE ) ) {
  804. #endif
  805. // FIFO is empty, exit without error.
  806. bDone = TRUE;
  807. } else {
  808. KeQueryTickCount(&End);
  809. if ((End.QuadPart - Start.QuadPart) * KeQueryTimeIncrement() > Wait.QuadPart) {
  810. // FIFO not empty, timeout occurred, exit with error.
  811. // NOTE: There is not a good way to determine how many bytes
  812. // are stuck in the fifo
  813. DD((PCE)Pdx,DDT,"ParEcpHwWaitForEmptyFIFO: timeout during synch\n");
  814. status = STATUS_IO_TIMEOUT;
  815. bDone = TRUE;
  816. }
  817. }
  818. } // of while...
  819. return status;
  820. }
  821. NTSTATUS
  822. ParEcpHwWrite(
  823. IN PPDO_EXTENSION Pdx,
  824. IN PVOID Buffer,
  825. IN ULONG BufferSize,
  826. OUT PULONG BytesTransferred
  827. )
  828. /*++
  829. Routine Description:
  830. Writes data to the peripheral using the ECP protocol under hardware
  831. control.
  832. Arguments:
  833. Pdx - Supplies the device extension.
  834. Buffer - Supplies the buffer to write from.
  835. BufferSize - Supplies the number of bytes in the buffer.
  836. BytesTransferred - Returns the number of bytes transferred.
  837. Return Value:
  838. None.
  839. --*/
  840. {
  841. PUCHAR wPortDSR;
  842. PUCHAR wPortECR;
  843. PUCHAR wPortDFIFO;
  844. ULONG bytesToWrite = BufferSize;
  845. UCHAR dsr;
  846. UCHAR ecr;
  847. UCHAR ecrFIFO;
  848. LARGE_INTEGER WaitPerByteTimer;
  849. LARGE_INTEGER StartPerByteTimer;
  850. LARGE_INTEGER EndPerByteTimer;
  851. BOOLEAN bResetTimer = TRUE;
  852. ULONG wBurstCount; // Length of burst to write when FIFO empty
  853. PUCHAR pBuffer;
  854. NTSTATUS status = STATUS_SUCCESS;
  855. wPortDSR = Pdx->Controller + DSR_OFFSET;
  856. wPortECR = Pdx->EcrController + ECR_OFFSET;
  857. wPortDFIFO = Pdx->EcrController;
  858. pBuffer = Buffer;
  859. status = ParTestEcpWrite(Pdx);
  860. if (!NT_SUCCESS(status)) {
  861. P5SetPhase( Pdx, PHASE_UNKNOWN );
  862. Pdx->Connected = FALSE;
  863. DD((PCE)Pdx,DDT,"ParEcpHwWrite: Invalid Entry State\n");
  864. goto ParEcpHwWrite_ExitLabel; // Use a goto so we can see Debug info located at the end of proc!
  865. }
  866. P5SetPhase( Pdx, PHASE_FORWARD_XFER );
  867. //----------------------------------------------------------------------
  868. // Setup Timer Stuff.
  869. //----------------------------------------------------------------------
  870. // we wait up to 35 milliseconds.
  871. WaitPerByteTimer.QuadPart = (35 * 10 * 1000) + KeQueryTimeIncrement(); // 35ms
  872. // Set up the timer that limits the time allowed for per-byte handshakes.
  873. KeQueryTickCount(&StartPerByteTimer);
  874. //----------------------------------------------------------------------
  875. // Send the data to the DFIFO.
  876. //----------------------------------------------------------------------
  877. HWECP_WriteLoop_Start:
  878. //------------------------------------------------------------------
  879. // Determine whether the FIFO has space and respond accordingly.
  880. //------------------------------------------------------------------
  881. ecrFIFO = (UCHAR)(P5ReadPortUchar(wPortECR) & ECR_FIFO_MASK);
  882. if ( ECR_FIFO_EMPTY == ecrFIFO ) {
  883. wBurstCount = (bytesToWrite > Pdx->FifoDepth) ? Pdx->FifoDepth : bytesToWrite;
  884. bytesToWrite -= wBurstCount;
  885. P5WritePortBufferUchar(wPortDFIFO, pBuffer, wBurstCount);
  886. pBuffer += wBurstCount;
  887. bResetTimer = TRUE;
  888. } else if (ECR_FIFO_SOME_DATA == ecrFIFO) {
  889. // Write just one byte at a time, since we don't know exactly how much
  890. // room there is in the FIFO.
  891. P5WritePortUchar(wPortDFIFO, *pBuffer++);
  892. bytesToWrite--;
  893. bResetTimer = TRUE;
  894. } else { // ECR_FIFO_FULL
  895. // Need to figure out whether to keep attempting to send, or to quit
  896. // with a timeout status.
  897. // Reset the per-byte timer if a byte was received since the last
  898. // timer check.
  899. if ( bResetTimer ) {
  900. KeQueryTickCount(&StartPerByteTimer);
  901. bResetTimer = FALSE;
  902. }
  903. KeQueryTickCount(&EndPerByteTimer);
  904. if ((EndPerByteTimer.QuadPart - StartPerByteTimer.QuadPart) * KeQueryTimeIncrement() > WaitPerByteTimer.QuadPart) {
  905. status = STATUS_TIMEOUT;
  906. // Peripheral is either busy or stalled. If the peripheral
  907. // is busy then they should be using SWECP to allow for
  908. // relaxed timings. Let's punt!
  909. goto HWECP_WriteLoop_End;
  910. }
  911. }
  912. if (bytesToWrite == 0) {
  913. goto HWECP_WriteLoop_End; // Transfer completed.
  914. }
  915. goto HWECP_WriteLoop_Start; // Start over
  916. HWECP_WriteLoop_End:
  917. if ( NT_SUCCESS(status) ) {
  918. // If there have been no previous errors, and synchronous writes
  919. // have been requested, wait for the FIFO to empty and the last
  920. // handshake of PeriphAck to complete before returning success.
  921. if (Pdx->bSynchWrites ) {
  922. BOOLEAN bDone = FALSE;
  923. KeQueryTickCount(&StartPerByteTimer);
  924. while( !bDone ) {
  925. ecr = P5ReadPortUchar(wPortECR);
  926. dsr = P5ReadPortUchar(wPortDSR);
  927. // LLL/CGM 10/9/95: tighten up DSR test - PeriphClk should be high
  928. if ( TEST_ECR_FIFO( ecr, ECR_FIFO_EMPTY ) &&
  929. TEST_DSR( dsr, INACTIVE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE ) ) {
  930. // FIFO is empty, exit without error.
  931. bDone = TRUE;
  932. } else {
  933. KeQueryTickCount(&EndPerByteTimer);
  934. if ((EndPerByteTimer.QuadPart - StartPerByteTimer.QuadPart) * KeQueryTimeIncrement() > WaitPerByteTimer.QuadPart) {
  935. // FIFO not empty, timeout occurred, exit with error.
  936. status = STATUS_TIMEOUT;
  937. bDone = TRUE;
  938. }
  939. }
  940. } // of while...
  941. }
  942. }
  943. P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
  944. ParEcpHwWrite_ExitLabel:
  945. *BytesTransferred = BufferSize - bytesToWrite;
  946. Pdx->log.HwEcpWriteCount += *BytesTransferred;
  947. DD((PCE)Pdx,DDT,"ParEcpHwWrite: exit w/status=%x, BytesTransferred=%d, dsr=%02x dcr=%02x, ecr=%02x\n",
  948. status, *BytesTransferred, P5ReadPortUchar(wPortDSR), P5ReadPortUchar(Pdx->Controller + OFFSET_DCR), P5ReadPortUchar(wPortECR));
  949. #if 1 == DBG_SHOW_BYTES
  950. if( DbgShowBytes ) {
  951. if( NT_SUCCESS( status ) && (*BytesTransferred > 0) ) {
  952. const ULONG maxBytes = 32;
  953. ULONG i;
  954. PUCHAR bytePtr = (PUCHAR)Buffer;
  955. DbgPrint("W: ");
  956. for( i=0 ; (i < *BytesTransferred) && (i < maxBytes) ; ++i ) {
  957. DbgPrint("%02x ",*bytePtr++);
  958. }
  959. if( *BytesTransferred > maxBytes ) {
  960. DbgPrint("... ");
  961. }
  962. DbgPrint("zz\n");
  963. }
  964. }
  965. #endif
  966. return status;
  967. }
  968. NTSTATUS
  969. ParEnterEcpHwMode(
  970. IN PPDO_EXTENSION Pdx,
  971. IN BOOLEAN DeviceIdRequest
  972. )
  973. /*++
  974. Routine Description:
  975. This routine performs 1284 negotiation with the peripheral to the
  976. ECP mode protocol.
  977. Arguments:
  978. Controller - Supplies the port address.
  979. DeviceIdRequest - Supplies whether or not this is a request for a device
  980. id.
  981. Return Value:
  982. STATUS_SUCCESS - Successful negotiation.
  983. otherwise - Unsuccessful negotiation.
  984. --*/
  985. {
  986. NTSTATUS Status = STATUS_SUCCESS;
  987. PUCHAR Controller;
  988. Controller = Pdx->Controller;
  989. if ( Pdx->ModeSafety == SAFE_MODE ) {
  990. if (DeviceIdRequest) {
  991. Status = IeeeEnter1284Mode (Pdx, ECP_EXTENSIBILITY | DEVICE_ID_REQ);
  992. } else {
  993. Status = IeeeEnter1284Mode (Pdx, ECP_EXTENSIBILITY);
  994. }
  995. } else {
  996. Pdx->Connected = TRUE;
  997. }
  998. // LAC ENTEREXIT 5Dec97
  999. // Make sure that the ECR is in PS/2 mode, and that wPortHWMode
  1000. // has the correct value. (This is the common entry mode);
  1001. Pdx->PortHWMode = HW_MODE_PS2;
  1002. if (NT_SUCCESS(Status)) {
  1003. Status = ParEcpHwSetupPhase(Pdx);
  1004. Pdx->bSynchWrites = TRUE; // NOTE this is a temp hack!!! dvrh
  1005. if (!Pdx->bShadowBuffer)
  1006. {
  1007. Queue_Create(&(Pdx->ShadowBuffer), Pdx->FifoDepth * 2);
  1008. Pdx->bShadowBuffer = TRUE;
  1009. }
  1010. Pdx->IsIeeeTerminateOk = TRUE;
  1011. }
  1012. return Status;
  1013. }
  1014. BOOLEAN
  1015. ParIsEcpHwSupported(
  1016. IN PPDO_EXTENSION Pdx
  1017. )
  1018. /*++
  1019. Routine Description:
  1020. This routine determines whether or not ECP mode is suported
  1021. in the write direction by trying to negotiate when asked.
  1022. Arguments:
  1023. Pdx - The device extension.
  1024. Return Value:
  1025. BOOLEAN.
  1026. --*/
  1027. {
  1028. NTSTATUS Status;
  1029. if (Pdx->BadProtocolModes & ECP_HW_NOIRQ)
  1030. return FALSE;
  1031. if (Pdx->ProtocolModesSupported & ECP_HW_NOIRQ)
  1032. return TRUE;
  1033. if (!(Pdx->HardwareCapabilities & PPT_ECP_PRESENT))
  1034. return FALSE;
  1035. if (0 == Pdx->FifoWidth)
  1036. return FALSE;
  1037. if (Pdx->ProtocolModesSupported & ECP_SW)
  1038. return TRUE;
  1039. // Must use HWECP Enter and Terminate for this test.
  1040. // Internel state machines will fail otherwise. --dvrh
  1041. Status = ParEnterEcpHwMode (Pdx, FALSE);
  1042. ParTerminateHwEcpMode (Pdx);
  1043. if (NT_SUCCESS(Status)) {
  1044. Pdx->ProtocolModesSupported |= ECP_HW_NOIRQ;
  1045. return TRUE;
  1046. }
  1047. return FALSE;
  1048. }
  1049. VOID
  1050. ParTerminateHwEcpMode(
  1051. IN PPDO_EXTENSION Pdx
  1052. )
  1053. /*++
  1054. Routine Description:
  1055. This routine terminates the interface back to compatibility mode.
  1056. Arguments:
  1057. Controller - Supplies the parallel port's controller address.
  1058. Return Value:
  1059. None.
  1060. --*/
  1061. {
  1062. // Need to check current phase -- if its reverse, need to flip bus
  1063. // If its not forward -- its an incorrect phase and termination will fail.
  1064. if( Pdx->ModeSafety == SAFE_MODE ) {
  1065. switch( Pdx->CurrentPhase ) {
  1066. case PHASE_FORWARD_IDLE: // Legal state to terminate from
  1067. break;
  1068. case PHASE_TERMINATE: // already terminated, nothing to do
  1069. DD((PCE)Pdx,DDW,"ParTerminateHwEcpMode - Already Terminated - Why are we trying to terminate again?\n");
  1070. goto target_exit;
  1071. break;
  1072. case PHASE_REVERSE_IDLE: // Flip bus to forward so we can terminate
  1073. {
  1074. NTSTATUS status = ParEcpHwExitReversePhase( Pdx );
  1075. if( STATUS_SUCCESS == status ) {
  1076. status = ParEcpEnterForwardPhase( Pdx );
  1077. }
  1078. }
  1079. break;
  1080. case PHASE_FORWARD_XFER:
  1081. case PHASE_REVERSE_XFER:
  1082. default:
  1083. DD((PCE)Pdx,DDE,"ParTerminateHwEcpMode - Invalid Phase [%x] for termination\n", Pdx->CurrentPhase);
  1084. // Don't know what to do here!?!
  1085. }
  1086. ParEcpHwWaitForEmptyFIFO( Pdx );
  1087. ParCleanupHwEcpPort( Pdx );
  1088. IeeeTerminate1284Mode( Pdx );
  1089. } else {
  1090. // UNSAFE_MODE
  1091. ParCleanupHwEcpPort(Pdx);
  1092. Pdx->Connected = FALSE;
  1093. }
  1094. target_exit:
  1095. return;
  1096. }