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.

595 lines
18 KiB

  1. /*****************************************************************************
  2. * MPU.cpp - UART miniport implementation
  3. *****************************************************************************
  4. * Copyright (c) 1998-2000 Microsoft Corporation. All rights reserved.
  5. *
  6. * Sept 98 MartinP .
  7. */
  8. #include "private.h"
  9. #include "ksdebug.h"
  10. #define STR_MODULENAME "DMusUART:MPU: "
  11. #define UartFifoOkForWrite(status) ((status & MPU401_DRR) == 0)
  12. #define UartFifoOkForRead(status) ((status & MPU401_DSR) == 0)
  13. typedef struct
  14. {
  15. CMiniportDMusUART *Miniport;
  16. PUCHAR PortBase;
  17. PVOID BufferAddress;
  18. ULONG Length;
  19. PULONG BytesRead;
  20. }
  21. SYNCWRITECONTEXT, *PSYNCWRITECONTEXT;
  22. BOOLEAN TryMPU(IN PUCHAR PortBase);
  23. NTSTATUS WriteMPU(IN PUCHAR PortBase,IN BOOLEAN IsCommand,IN UCHAR Value);
  24. #pragma code_seg("PAGE")
  25. // make sure we're in UART mode
  26. NTSTATUS ResetHardware(PUCHAR portBase)
  27. {
  28. PAGED_CODE();
  29. return WriteMPU(portBase,COMMAND,MPU401_CMD_UART);
  30. }
  31. #pragma code_seg("PAGE")
  32. //
  33. // We initialize the UART with interrupts suppressed so we don't
  34. // try to service the chip prematurely.
  35. //
  36. NTSTATUS CMiniportDMusUART::InitializeHardware(PINTERRUPTSYNC interruptSync,PUCHAR portBase)
  37. {
  38. PAGED_CODE();
  39. NTSTATUS ntStatus;
  40. if (m_UseIRQ)
  41. {
  42. ntStatus = interruptSync->CallSynchronizedRoutine(InitMPU,PVOID(portBase));
  43. }
  44. else
  45. {
  46. ntStatus = InitMPU(NULL,PVOID(portBase));
  47. }
  48. if (NT_SUCCESS(ntStatus))
  49. {
  50. //
  51. // Start the UART (this should trigger an interrupt).
  52. //
  53. ntStatus = ResetHardware(portBase);
  54. }
  55. else
  56. {
  57. _DbgPrintF(DEBUGLVL_TERSE,("*** InitMPU returned with ntStatus 0x%08x ***",ntStatus));
  58. }
  59. m_fMPUInitialized = NT_SUCCESS(ntStatus);
  60. return ntStatus;
  61. }
  62. #pragma code_seg()
  63. /*****************************************************************************
  64. * InitMPU()
  65. *****************************************************************************
  66. * Synchronized routine to initialize the MPU401.
  67. */
  68. NTSTATUS
  69. InitMPU
  70. (
  71. IN PINTERRUPTSYNC InterruptSync,
  72. IN PVOID DynamicContext
  73. )
  74. {
  75. _DbgPrintF(DEBUGLVL_BLAB, ("InitMPU"));
  76. if (!DynamicContext)
  77. {
  78. return STATUS_INVALID_PARAMETER_2;
  79. }
  80. PUCHAR portBase = PUCHAR(DynamicContext);
  81. UCHAR status;
  82. ULONGLONG startTime;
  83. BOOLEAN success;
  84. NTSTATUS ntStatus = STATUS_SUCCESS;
  85. //
  86. // Reset the card (puts it into "smart mode")
  87. //
  88. ntStatus = WriteMPU(portBase,COMMAND,MPU401_CMD_RESET);
  89. // wait for the acknowledgement
  90. // NOTE: When the Ack arrives, it will trigger an interrupt.
  91. // Normally the DPC routine would read in the ack byte and we
  92. // would never see it, however since we have the hardware locked (HwEnter),
  93. // we can read the port before the DPC can and thus we receive the Ack.
  94. startTime = PcGetTimeInterval(0);
  95. success = FALSE;
  96. while(PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
  97. {
  98. status = READ_PORT_UCHAR(portBase + MPU401_REG_STATUS);
  99. if (UartFifoOkForRead(status)) // Is data waiting?
  100. {
  101. READ_PORT_UCHAR(portBase + MPU401_REG_DATA); // yep.. read ACK
  102. success = TRUE; // don't need to do more
  103. break;
  104. }
  105. KeStallExecutionProcessor(25); // microseconds
  106. }
  107. #if (DBG)
  108. if (!success)
  109. {
  110. _DbgPrintF(DEBUGLVL_VERBOSE,("First attempt to reset the MPU didn't get ACKed.\n"));
  111. }
  112. #endif // (DBG)
  113. // NOTE: We cannot check the ACK byte because if the card was already in
  114. // UART mode it will not send an ACK but it will reset.
  115. // reset the card again
  116. (void) WriteMPU(portBase,COMMAND,MPU401_CMD_RESET);
  117. // wait for ack (again)
  118. startTime = PcGetTimeInterval(0); // This might take a while
  119. BYTE dataByte = 0;
  120. success = FALSE;
  121. while (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
  122. {
  123. status = READ_PORT_UCHAR(portBase + MPU401_REG_STATUS);
  124. if (UartFifoOkForRead(status)) // Is data waiting?
  125. {
  126. dataByte = READ_PORT_UCHAR(portBase + MPU401_REG_DATA); // yep.. read ACK
  127. success = TRUE; // don't need to do more
  128. break;
  129. }
  130. KeStallExecutionProcessor(25);
  131. }
  132. if ((0xFE != dataByte) || !success) // Did we succeed? If no second ACK, something is hosed
  133. {
  134. _DbgPrintF(DEBUGLVL_TERSE,("Second attempt to reset the MPU didn't get ACKed.\n"));
  135. _DbgPrintF(DEBUGLVL_TERSE,("Init Reset failure error. Ack = %X", ULONG(dataByte) ) );
  136. ntStatus = STATUS_IO_DEVICE_ERROR;
  137. }
  138. return ntStatus;
  139. }
  140. #pragma code_seg()
  141. /*****************************************************************************
  142. * CMiniportDMusUARTStream::Write()
  143. *****************************************************************************
  144. * Writes outgoing MIDI data.
  145. */
  146. STDMETHODIMP_(NTSTATUS)
  147. CMiniportDMusUARTStream::
  148. Write
  149. (
  150. IN PVOID BufferAddress,
  151. IN ULONG Length,
  152. OUT PULONG BytesWritten
  153. )
  154. {
  155. _DbgPrintF(DEBUGLVL_BLAB, ("Write"));
  156. ASSERT(BytesWritten);
  157. if (!BufferAddress)
  158. {
  159. Length = 0;
  160. }
  161. NTSTATUS ntStatus = STATUS_SUCCESS;
  162. if (!m_fCapture)
  163. {
  164. PUCHAR pMidiData;
  165. ULONG count;
  166. count = 0;
  167. pMidiData = PUCHAR(BufferAddress);
  168. if (Length)
  169. {
  170. SYNCWRITECONTEXT context;
  171. context.Miniport = (m_pMiniport);
  172. context.PortBase = m_pPortBase;
  173. context.BufferAddress = pMidiData;
  174. context.Length = Length;
  175. context.BytesRead = &count;
  176. if (m_pMiniport->m_UseIRQ)
  177. {
  178. ntStatus = m_pMiniport->m_pInterruptSync->
  179. CallSynchronizedRoutine(SynchronizedDMusMPUWrite,PVOID(&context));
  180. }
  181. else // !m_UseIRQ
  182. {
  183. ntStatus = SynchronizedDMusMPUWrite(NULL,PVOID(&context));
  184. } // !m_UseIRQ
  185. if (count == 0)
  186. {
  187. m_NumFailedMPUTries++;
  188. if (m_NumFailedMPUTries >= 100)
  189. {
  190. ntStatus = STATUS_IO_DEVICE_ERROR;
  191. m_NumFailedMPUTries = 0;
  192. }
  193. }
  194. else
  195. {
  196. m_NumFailedMPUTries = 0;
  197. }
  198. } // if we have data at all
  199. *BytesWritten = count;
  200. }
  201. else // called write on the read stream
  202. {
  203. ntStatus = STATUS_INVALID_DEVICE_REQUEST;
  204. }
  205. return ntStatus;
  206. }
  207. #pragma code_seg()
  208. /*****************************************************************************
  209. * SynchronizedDMusMPUWrite()
  210. *****************************************************************************
  211. * Writes outgoing MIDI data.
  212. */
  213. NTSTATUS
  214. SynchronizedDMusMPUWrite
  215. (
  216. IN PINTERRUPTSYNC InterruptSync,
  217. IN PVOID syncWriteContext
  218. )
  219. {
  220. PSYNCWRITECONTEXT context;
  221. context = (PSYNCWRITECONTEXT)syncWriteContext;
  222. ASSERT(context->Miniport);
  223. ASSERT(context->PortBase);
  224. ASSERT(context->BufferAddress);
  225. ASSERT(context->Length);
  226. ASSERT(context->BytesRead);
  227. PUCHAR pChar = PUCHAR(context->BufferAddress);
  228. NTSTATUS ntStatus,readStatus;
  229. ntStatus = STATUS_SUCCESS;
  230. //
  231. // while we're not there yet, and
  232. // while we don't have to wait on an aligned byte (including 0)
  233. // (we never wait on a byte. Better to come back later)
  234. readStatus = DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
  235. while ( (*(context->BytesRead) < context->Length)
  236. && ( TryMPU(context->PortBase)
  237. || (*(context->BytesRead)%3)
  238. ) )
  239. {
  240. ntStatus = WriteMPU(context->PortBase,DATA,*pChar);
  241. if (NT_SUCCESS(ntStatus))
  242. {
  243. pChar++;
  244. *(context->BytesRead) = *(context->BytesRead) + 1;
  245. // readStatus = DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
  246. }
  247. else
  248. {
  249. _DbgPrintF(DEBUGLVL_TERSE,("SynchronizedDMusMPUWrite failed (0x%08x)",ntStatus));
  250. break;
  251. }
  252. }
  253. readStatus = DMusMPUInterruptServiceRoutine(InterruptSync,PVOID(context->Miniport));
  254. return ntStatus;
  255. }
  256. #define kMPUPollTimeout 2
  257. #pragma code_seg()
  258. /*****************************************************************************
  259. * TryMPU()
  260. *****************************************************************************
  261. * See if the MPU401 is free.
  262. */
  263. BOOLEAN
  264. TryMPU
  265. (
  266. IN PUCHAR PortBase
  267. )
  268. {
  269. BOOLEAN success;
  270. USHORT numPolls;
  271. UCHAR status;
  272. _DbgPrintF(DEBUGLVL_BLAB, ("TryMPU"));
  273. numPolls = 0;
  274. while (numPolls < kMPUPollTimeout)
  275. {
  276. status = READ_PORT_UCHAR(PortBase + MPU401_REG_STATUS);
  277. if (UartFifoOkForWrite(status)) // Is this a good time to write data?
  278. {
  279. break;
  280. }
  281. numPolls++;
  282. }
  283. if (numPolls >= kMPUPollTimeout)
  284. {
  285. success = FALSE;
  286. _DbgPrintF(DEBUGLVL_BLAB, ("TryMPU failed"));
  287. }
  288. else
  289. {
  290. success = TRUE;
  291. }
  292. return success;
  293. }
  294. #pragma code_seg()
  295. /*****************************************************************************
  296. * WriteMPU()
  297. *****************************************************************************
  298. * Write a byte out to the MPU401.
  299. */
  300. NTSTATUS
  301. WriteMPU
  302. (
  303. IN PUCHAR PortBase,
  304. IN BOOLEAN IsCommand,
  305. IN UCHAR Value
  306. )
  307. {
  308. _DbgPrintF(DEBUGLVL_BLAB, ("WriteMPU"));
  309. NTSTATUS ntStatus = STATUS_IO_DEVICE_ERROR;
  310. if (!PortBase)
  311. {
  312. _DbgPrintF(DEBUGLVL_TERSE, ("O: PortBase is zero\n"));
  313. return ntStatus;
  314. }
  315. PUCHAR deviceAddr = PortBase + MPU401_REG_DATA;
  316. if (IsCommand)
  317. {
  318. deviceAddr = PortBase + MPU401_REG_COMMAND;
  319. }
  320. ULONGLONG startTime = PcGetTimeInterval(0);
  321. while (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
  322. {
  323. UCHAR status
  324. = READ_PORT_UCHAR(PortBase + MPU401_REG_STATUS);
  325. if (UartFifoOkForWrite(status)) // Is this a good time to write data?
  326. { // yep (Jon comment)
  327. WRITE_PORT_UCHAR(deviceAddr,Value);
  328. _DbgPrintF(DEBUGLVL_BLAB, ("WriteMPU emitted 0x%02x",Value));
  329. ntStatus = STATUS_SUCCESS;
  330. break;
  331. }
  332. }
  333. return ntStatus;
  334. }
  335. #pragma code_seg()
  336. /*****************************************************************************
  337. * SnapTimeStamp()
  338. *****************************************************************************
  339. *
  340. * At synchronized execution to ISR, copy miniport's volatile m_InputTimeStamp
  341. * to stream's m_SnapshotTimeStamp and zero m_InputTimeStamp.
  342. *
  343. */
  344. STDMETHODIMP_(NTSTATUS)
  345. SnapTimeStamp(PINTERRUPTSYNC InterruptSync,PVOID pStream)
  346. {
  347. CMiniportDMusUARTStream *pMPStream = (CMiniportDMusUARTStream *)pStream;
  348. // cache the timestamp
  349. pMPStream->m_SnapshotTimeStamp = pMPStream->m_pMiniport->m_InputTimeStamp;
  350. // if the window is closed, zero the timestamp
  351. if (pMPStream->m_pMiniport->m_MPUInputBufferHead ==
  352. pMPStream->m_pMiniport->m_MPUInputBufferTail)
  353. {
  354. pMPStream->m_pMiniport->m_InputTimeStamp = 0;
  355. }
  356. return STATUS_SUCCESS;
  357. }
  358. /*****************************************************************************
  359. * CMiniportDMusUARTStream::SourceEvtsToPort()
  360. *****************************************************************************
  361. *
  362. * Reads incoming MIDI data, feeds into DMus events.
  363. * No need to touch the hardware, just read from our SW FIFO.
  364. *
  365. */
  366. STDMETHODIMP_(NTSTATUS)
  367. CMiniportDMusUARTStream::SourceEvtsToPort()
  368. {
  369. NTSTATUS ntStatus;
  370. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  371. _DbgPrintF(DEBUGLVL_BLAB, ("SourceEvtsToPort"));
  372. if (m_fCapture)
  373. {
  374. ntStatus = STATUS_SUCCESS;
  375. if (m_pMiniport->m_MPUInputBufferHead != m_pMiniport->m_MPUInputBufferTail)
  376. {
  377. PDMUS_KERNEL_EVENT aDMKEvt,eventTail,eventHead = NULL;
  378. while (m_pMiniport->m_MPUInputBufferHead != m_pMiniport->m_MPUInputBufferTail)
  379. {
  380. (void) m_AllocatorMXF->GetMessage(&aDMKEvt);
  381. if (!aDMKEvt)
  382. {
  383. _DbgPrintF(DEBUGLVL_TERSE, ("SourceEvtsToPort can't allocate DMKEvt"));
  384. return STATUS_INSUFFICIENT_RESOURCES;
  385. }
  386. // put this event at the end of the list
  387. if (!eventHead)
  388. {
  389. eventHead = aDMKEvt;
  390. }
  391. else
  392. {
  393. eventTail = eventHead;
  394. while (eventTail->pNextEvt)
  395. {
  396. eventTail = eventTail->pNextEvt;
  397. }
  398. eventTail->pNextEvt = aDMKEvt;
  399. }
  400. // read all the bytes out of the buffer, into event(s)
  401. for (aDMKEvt->cbEvent = 0; aDMKEvt->cbEvent < sizeof(PBYTE); aDMKEvt->cbEvent++)
  402. {
  403. if (m_pMiniport->m_MPUInputBufferHead == m_pMiniport->m_MPUInputBufferTail)
  404. {
  405. // _DbgPrintF(DEBUGLVL_TERSE, ("SourceEvtsToPort m_MPUInputBufferHead met m_MPUInputBufferTail, overrun"));
  406. break;
  407. }
  408. aDMKEvt->uData.abData[aDMKEvt->cbEvent] = m_pMiniport->m_MPUInputBuffer[m_pMiniport->m_MPUInputBufferHead];
  409. m_pMiniport->m_MPUInputBufferHead++;
  410. if (m_pMiniport->m_MPUInputBufferHead >= kMPUInputBufferSize)
  411. {
  412. m_pMiniport->m_MPUInputBufferHead = 0;
  413. }
  414. }
  415. }
  416. if (m_pMiniport->m_UseIRQ)
  417. {
  418. ntStatus = m_pMiniport->m_pInterruptSync->CallSynchronizedRoutine(SnapTimeStamp,PVOID(this));
  419. }
  420. else // !m_UseIRQ
  421. {
  422. ntStatus = SnapTimeStamp(NULL,PVOID(this));
  423. } // !m_UseIRQ
  424. aDMKEvt = eventHead;
  425. while (aDMKEvt)
  426. {
  427. aDMKEvt->ullPresTime100ns = m_SnapshotTimeStamp;
  428. aDMKEvt->usChannelGroup = 1;
  429. aDMKEvt->usFlags = DMUS_KEF_EVENT_INCOMPLETE;
  430. aDMKEvt = aDMKEvt->pNextEvt;
  431. }
  432. (void)m_sinkMXF->PutMessage(eventHead);
  433. }
  434. }
  435. else // render stream
  436. {
  437. _DbgPrintF(DEBUGLVL_TERSE, ("SourceEvtsToPort called on render stream"));
  438. ntStatus = STATUS_INVALID_DEVICE_REQUEST;
  439. }
  440. return ntStatus;
  441. }
  442. #pragma code_seg()
  443. /*****************************************************************************
  444. * DMusMPUInterruptServiceRoutine()
  445. *****************************************************************************
  446. * ISR.
  447. */
  448. NTSTATUS
  449. DMusMPUInterruptServiceRoutine
  450. (
  451. IN PINTERRUPTSYNC InterruptSync,
  452. IN PVOID DynamicContext
  453. )
  454. {
  455. _DbgPrintF(DEBUGLVL_BLAB, ("DMusMPUInterruptServiceRoutine"));
  456. ULONGLONG startTime;
  457. ASSERT(DynamicContext);
  458. NTSTATUS ntStatus;
  459. BOOL newBytesAvailable;
  460. CMiniportDMusUART *that;
  461. NTSTATUS clockStatus;
  462. that = (CMiniportDMusUART *) DynamicContext;
  463. newBytesAvailable = FALSE;
  464. ntStatus = STATUS_UNSUCCESSFUL;
  465. UCHAR portStatus = 0xff;
  466. //
  467. // Read the MPU status byte.
  468. //
  469. if (that->m_pPortBase)
  470. {
  471. portStatus =
  472. READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_STATUS);
  473. //
  474. // If there is outstanding work to do and there is a port-driver for
  475. // the MPU miniport...
  476. //
  477. if (UartFifoOkForRead(portStatus) && that->m_pPort)
  478. {
  479. startTime = PcGetTimeInterval(0);
  480. while ( (PcGetTimeInterval(startTime) < GTI_MILLISECONDS(50))
  481. && (UartFifoOkForRead(portStatus)) )
  482. {
  483. UCHAR uDest = READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_DATA);
  484. if ( (that->m_KSStateInput == KSSTATE_RUN)
  485. && (that->m_NumCaptureStreams)
  486. )
  487. {
  488. LONG buffHead = that->m_MPUInputBufferHead;
  489. if ( (that->m_MPUInputBufferTail + 1 == buffHead)
  490. || (that->m_MPUInputBufferTail + 1 - kMPUInputBufferSize == buffHead))
  491. {
  492. _DbgPrintF(DEBUGLVL_TERSE,("*****MPU Input Buffer Overflow*****"));
  493. }
  494. else
  495. {
  496. if (!that->m_InputTimeStamp)
  497. {
  498. clockStatus = that->m_MasterClock->GetTime(&that->m_InputTimeStamp);
  499. if (STATUS_SUCCESS != clockStatus)
  500. {
  501. _DbgPrintF(DEBUGLVL_TERSE,("GetTime failed for clock 0x%08x",that->m_MasterClock));
  502. }
  503. }
  504. newBytesAvailable = TRUE;
  505. // ...place the data in our FIFO...
  506. that->m_MPUInputBuffer[that->m_MPUInputBufferTail] = uDest;
  507. ASSERT(that->m_MPUInputBufferTail < kMPUInputBufferSize);
  508. that->m_MPUInputBufferTail++;
  509. if (that->m_MPUInputBufferTail >= kMPUInputBufferSize)
  510. {
  511. that->m_MPUInputBufferTail = 0;
  512. }
  513. }
  514. }
  515. //
  516. // Look for more MIDI data.
  517. //
  518. portStatus =
  519. READ_PORT_UCHAR(that->m_pPortBase + MPU401_REG_STATUS);
  520. } // either there's no data or we ran too long
  521. if (newBytesAvailable)
  522. {
  523. //
  524. // ...notify the MPU port driver that we have bytes.
  525. //
  526. that->m_pPort->Notify(that->m_pServiceGroup);
  527. }
  528. ntStatus = STATUS_SUCCESS;
  529. }
  530. }
  531. return ntStatus;
  532. }