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.

524 lines
15 KiB

  1. /*++
  2. Copyright (c) 1999, 2000 Microsoft Corporation
  3. Module Name:
  4. isoch.c
  5. Abstract:
  6. miniport transfer code for Isochronous
  7. Environment:
  8. kernel mode only
  9. Notes:
  10. THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  11. KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  12. IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  13. PURPOSE.
  14. Copyright (c) 1999, 2000 Microsoft Corporation. All Rights Reserved.
  15. Revision History:
  16. 8-1-00 : created, jsenior
  17. --*/
  18. #include "pch.h"
  19. //implements the following miniport functions:
  20. //non paged
  21. //UhciIsochTransfer
  22. //UhciProcessDoneIsochTd
  23. //UhciPollIsochEndpoint
  24. //UhciAbortIsochTransfer
  25. USB_MINIPORT_STATUS
  26. UhciIsochTransfer(
  27. IN PDEVICE_DATA DeviceData,
  28. IN PENDPOINT_DATA EndpointData,
  29. IN PTRANSFER_PARAMETERS TransferParameters,
  30. IN PTRANSFER_CONTEXT TransferContext,
  31. IN PMINIPORT_ISO_TRANSFER IsoTransfer
  32. )
  33. /*++
  34. Routine Description:
  35. Initialize all the TDs for an isochronous Transfer.
  36. Queue up whatever TDs we can in the current schedule.
  37. Whatever's left may get queued in the poll routine.
  38. Arguments:
  39. --*/
  40. {
  41. // indices and offsets
  42. ULONG i, dbCount;
  43. // lengths
  44. ULONG lengthThisTd, lengthMapped = 0;
  45. USHORT maxPacketSize = EndpointData->Parameters.MaxPacketSize;
  46. // structure pointers
  47. PTRANSFER_PARAMETERS tp;
  48. PISOCH_TRANSFER_BUFFER buffer = NULL;
  49. PHCD_TRANSFER_DESCRIPTOR firstTd, td; //, lastTd = NULL;
  50. HW_32BIT_PHYSICAL_ADDRESS address;
  51. PMINIPORT_ISO_PACKET packet;
  52. BOOLEAN pageCrossing = FALSE;
  53. USBD_STATUS insertResult;
  54. USB_MINIPORT_STATUS mpStatus;
  55. // Isoch pipes are uni-directional. Get the
  56. // direction from the endpoint address.
  57. UCHAR pid = GetPID(EndpointData->Parameters.EndpointAddress);
  58. //
  59. // Do we have enough free resources?
  60. //
  61. if (EndpointData->TdCount - EndpointData->TdsUsed <
  62. IsoTransfer->PacketCount) {
  63. // Not enough TDs to do this transfer yet.
  64. // Tell the port driver to wait.
  65. return USBMP_STATUS_BUSY;
  66. }
  67. // We may need DBs. Do we have enough?
  68. for (i = 0, dbCount = 0; i < IsoTransfer->PacketCount; i++) {
  69. if (IsoTransfer->Packets[i].BufferPointerCount == 2) {
  70. dbCount++;
  71. }
  72. }
  73. if (EndpointData->DbCount - EndpointData->DbsUsed <
  74. dbCount) {
  75. // Not enough DBs to do this transfer yet.
  76. // Tell the port driver to wait.
  77. return USBMP_STATUS_BUSY;
  78. }
  79. UhciCleanOutIsoch(DeviceData, FALSE);
  80. #if DBG
  81. {
  82. ULONG cf;
  83. cf = UhciGet32BitFrameNumber(DeviceData);
  84. LOGENTRY(DeviceData, G, '_iso', IsoTransfer->PacketCount, cf,
  85. IsoTransfer->Packets[0].FrameNumber);
  86. }
  87. #endif
  88. // UhciKdPrint((DeviceData, 2, "'First packet frame number = %x\n", IsoTransfer->Packets[0].FrameNumber));
  89. IncPendingTransfers(DeviceData, EndpointData);
  90. // init the context
  91. RtlZeroMemory(TransferContext, sizeof(*TransferContext));
  92. TransferContext->Sig = SIG_UHCI_TRANSFER;
  93. TransferContext->UsbdStatus = USBD_STATUS_SUCCESS;
  94. TransferContext->EndpointData = EndpointData;
  95. TransferContext->TransferParameters = tp = TransferParameters;
  96. TransferContext->IsoTransfer = IsoTransfer;
  97. UHCI_ASSERT(DeviceData,
  98. EndpointData->Parameters.TransferType == Isochronous);
  99. LOGENTRY(DeviceData, G, '_isT', EndpointData, TransferParameters, IsoTransfer->Packets[0].FrameNumber);
  100. //
  101. // One TD per transfer.
  102. //
  103. for (i = 0; i < IsoTransfer->PacketCount; i++) {
  104. packet = &IsoTransfer->Packets[i];
  105. address = packet->BufferPointer0.Hw32;
  106. UHCI_ASSERT(DeviceData, address);
  107. UHCI_ASSERT(DeviceData, packet->BufferPointerCount == 1 ||
  108. packet->BufferPointerCount == 2);
  109. //
  110. // Is this packet ok to transfer?
  111. //
  112. UhciCheckIsochTransferInsertion(DeviceData,
  113. insertResult,
  114. packet->FrameNumber);
  115. if (USBD_ERROR(insertResult)) {
  116. // Not ok to transfer. Try the next one.
  117. packet->UsbdStatus = insertResult;
  118. lengthMapped +=
  119. packet->BufferPointer0Length + packet->BufferPointer1Length;
  120. LOGENTRY(DeviceData, G, '_BSF', UhciGet32BitFrameNumber(DeviceData), IsoTransfer->Packets[i].FrameNumber, i);
  121. continue;
  122. }
  123. if (packet->BufferPointerCount == 1) {
  124. //
  125. // Normal, non-buffered case.
  126. //
  127. pageCrossing = FALSE;
  128. lengthThisTd = packet->BufferPointer0Length;
  129. } else {
  130. //
  131. // Page crossing. Must double buffer this transfer.
  132. //
  133. lengthThisTd = packet->BufferPointer0Length + packet->BufferPointer1Length;
  134. buffer = (PISOCH_TRANSFER_BUFFER)
  135. UHCI_ALLOC_DB(DeviceData, EndpointData, TRUE);
  136. UHCI_ASSERT(DeviceData, buffer);
  137. UHCI_ASSERT(DeviceData, buffer->Sig == SIG_HCD_IDB);
  138. UHCI_ASSERT(DeviceData, buffer->PhysicalAddress);
  139. buffer->SystemAddress = IsoTransfer->SystemAddress + lengthMapped;
  140. buffer->Size = lengthThisTd;
  141. UHCI_ASSERT(DeviceData, lengthThisTd <= MAX_ISOCH_PACKET_SIZE);
  142. if (OutPID == pid) {
  143. RtlCopyMemory(&buffer->Buffer[0],
  144. buffer->SystemAddress,
  145. lengthThisTd);
  146. }
  147. // Change the address for the TD
  148. pageCrossing = TRUE;
  149. address = buffer->PhysicalAddress;
  150. }
  151. TransferContext->PendingTds++;
  152. td = UHCI_ALLOC_TD(DeviceData, EndpointData);
  153. INITIALIZE_TD_FOR_TRANSFER(td, TransferContext);
  154. //
  155. // Initialize the TD fields
  156. //
  157. td->HwTD.Token.Pid = pid;
  158. td->HwTD.Token.MaximumLength = MAXIMUM_LENGTH(lengthThisTd);
  159. td->HwTD.Token.DataToggle = DataToggle0;
  160. td->HwTD.Control.IsochronousSelect = 1;
  161. td->HwTD.Control.ShortPacketDetect = 0; // Don't care about short packets
  162. td->HwTD.Control.ActualLength = MAXIMUM_LENGTH(0);
  163. td->HwTD.Control.ErrorCount = 0;
  164. td->HwTD.Buffer = address;
  165. td->IsoPacket = packet;
  166. if (pageCrossing) {
  167. SET_FLAG(td->Flags, TD_FLAG_DOUBLE_BUFFERED);
  168. td->DoubleBuffer = (PTRANSFER_BUFFER) buffer;
  169. }
  170. // countIOC = countIOC + 1 == 10 ? 0 : countIOC+1;
  171. //
  172. // Request some interrupts near the end of the
  173. // transfer
  174. td->HwTD.Control.InterruptOnComplete =
  175. (i+1 >= IsoTransfer->PacketCount) ? 1 : 0; //!countIOC;
  176. address += lengthThisTd;
  177. lengthMapped += lengthThisTd;
  178. if (USBD_STATUS_SUCCESS == insertResult) {
  179. //
  180. // Put the TD in the schedule
  181. //
  182. LOGENTRY(DeviceData, G, '_qi1', td, 0, packet->FrameNumber);
  183. INSERT_ISOCH_TD(DeviceData, td, packet->FrameNumber);
  184. }
  185. }
  186. if (!TransferContext->PendingTds) {
  187. // Nothing got queued. Complete the transfer.
  188. DecPendingTransfers(DeviceData, EndpointData);
  189. LOGENTRY(DeviceData, G, '_cpt',
  190. packet->UsbdStatus,
  191. TransferContext,
  192. TransferContext->BytesTransferred);
  193. USBPORT_INVALIDATE_ENDPOINT(DeviceData, EndpointData);
  194. UhciKdPrint((DeviceData, 2, "'No tds queued for isoch tx.\n", EndpointData));
  195. // return error and port will complete the transfer
  196. mpStatus = USBMP_STATUS_FAILURE;
  197. } else {
  198. mpStatus = USBMP_STATUS_SUCCESS;
  199. }
  200. UHCI_ASSERT(DeviceData, TransferContext->TransferParameters->TransferBufferLength == lengthMapped);
  201. return mpStatus;
  202. }
  203. VOID
  204. UhciProcessDoneIsochTd(
  205. PDEVICE_DATA DeviceData,
  206. PHCD_TRANSFER_DESCRIPTOR Td
  207. )
  208. /*++
  209. Routine Description:
  210. process a completed isoch TD
  211. Parameters
  212. --*/
  213. {
  214. PTRANSFER_CONTEXT transferContext;
  215. PENDPOINT_DATA endpointData;
  216. ULONG byteCount;
  217. PMINIPORT_ISO_PACKET packet;
  218. transferContext = Td->TransferContext;
  219. ASSERT_TRANSFER(DeviceData, transferContext);
  220. transferContext->PendingTds--;
  221. endpointData = transferContext->EndpointData;
  222. packet = Td->IsoPacket;
  223. UHCI_ASSERT(DeviceData, packet);
  224. if (!TEST_FLAG(Td->Flags, TD_FLAG_ISO_QUEUED)) {
  225. packet->UsbdStatus = USBD_STATUS_BAD_START_FRAME;
  226. } else if (Td->HwTD.Control.Active) {
  227. packet->UsbdStatus = USBD_STATUS_NOT_ACCESSED;
  228. } else {
  229. // completion status for this TD/packet?
  230. packet->UsbdStatus = UhciGetErrorFromTD(DeviceData, Td);
  231. }
  232. LOGENTRY(DeviceData, G, '_Dit', transferContext,
  233. packet->UsbdStatus,
  234. Td);
  235. byteCount = ACTUAL_LENGTH(Td->HwTD.Control.ActualLength);
  236. transferContext->BytesTransferred += byteCount;
  237. packet->LengthTransferred = byteCount;
  238. //
  239. // For double buffered transfers, we now have to copy back
  240. // if this was an IN transfer.
  241. //
  242. if (Td->HwTD.Token.Pid == InPID &&
  243. TEST_FLAG(Td->Flags, TD_FLAG_DOUBLE_BUFFERED)) {
  244. PISOCH_TRANSFER_BUFFER buffer = (PISOCH_TRANSFER_BUFFER)Td->DoubleBuffer;
  245. UHCI_ASSERT(DeviceData, TEST_FLAG(buffer->Flags, DB_FLAG_BUSY));
  246. RtlCopyMemory(buffer->SystemAddress,
  247. &buffer->Buffer[0],
  248. buffer->Size);
  249. }
  250. // mark the TD free
  251. // This also frees any double buffers.
  252. UHCI_FREE_TD(DeviceData, endpointData, Td);
  253. if (transferContext->PendingTds == 0) {
  254. // all TDs for this transfer are done
  255. // clear the HAVE_TRANSFER flag to indicate
  256. // we can take another
  257. DecPendingTransfers(DeviceData, endpointData);
  258. LOGENTRY(DeviceData, G, '_cit',
  259. packet->UsbdStatus,
  260. transferContext,
  261. transferContext->BytesTransferred);
  262. transferContext->TransferParameters->FrameCompleted =
  263. UhciGet32BitFrameNumber(DeviceData);
  264. USBPORT_COMPLETE_ISOCH_TRANSFER(
  265. DeviceData,
  266. endpointData,
  267. transferContext->TransferParameters,
  268. transferContext->IsoTransfer);
  269. }
  270. }
  271. VOID
  272. UhciPollIsochEndpoint(
  273. IN PDEVICE_DATA DeviceData,
  274. IN PENDPOINT_DATA EndpointData
  275. )
  276. /*++
  277. Routine Description:
  278. Called when the endpoint 'needs attention'
  279. The goal here is to determine which TDs, if any,
  280. have completed and complete any associated transfers.
  281. Arguments:
  282. Return Value:
  283. --*/
  284. {
  285. PHCD_TRANSFER_DESCRIPTOR td;
  286. ULONG i;
  287. PMINIPORT_ISO_PACKET packet;
  288. USBD_STATUS insertResult;
  289. LOGENTRY(DeviceData, G, '_PiE', EndpointData, 0, 0);
  290. //
  291. // Cleanup the isoch transfers that haven't completed yet.
  292. //
  293. UhciCleanOutIsoch(DeviceData, FALSE);
  294. //
  295. // Flush all completed TDs and
  296. // queue up TDs that were pended.
  297. //
  298. // Don't care about errors.
  299. // Just get 'em out of here.
  300. //
  301. for (i = 0; i<EndpointData->TdCount; i++) {
  302. td = &EndpointData->TdList->Td[i];
  303. if (TEST_FLAG(td->Flags, TD_FLAG_XFER)) {
  304. if (td->IsoPacket->FrameNumber < DeviceData->LastFrameProcessed ||
  305. td->IsoPacket->FrameNumber - DeviceData->LastFrameProcessed > UHCI_MAX_FRAME) {
  306. //
  307. // Done, whether we like it or not.
  308. //
  309. td->Flags |= TD_FLAG_DONE;
  310. } else if (!TEST_FLAG(td->Flags, TD_FLAG_ISO_QUEUED)) {
  311. packet = td->IsoPacket;
  312. UhciKdPrint((DeviceData, 0, "'Late TD\n"));
  313. UhciCheckIsochTransferInsertion(DeviceData,
  314. insertResult,
  315. packet->FrameNumber);
  316. if (USBD_STATUS_SUCCESS == insertResult) {
  317. //
  318. // Put the TD in the schedule
  319. //
  320. LOGENTRY(DeviceData, G, '_qi2', td, 0, packet->FrameNumber);
  321. INSERT_ISOCH_TD(DeviceData, td, packet->FrameNumber);
  322. }
  323. }
  324. if (TEST_FLAG(td->Flags, TD_FLAG_DONE)) {
  325. UhciProcessDoneIsochTd(DeviceData, td);
  326. }
  327. }
  328. }
  329. }
  330. VOID
  331. UhciCleanOutIsoch(
  332. IN PDEVICE_DATA DeviceData,
  333. IN BOOLEAN ForceClean
  334. )
  335. {
  336. ULONG i, currentFrame;
  337. if (1 != InterlockedIncrement(&DeviceData->SynchronizeIsoCleanup)) {
  338. InterlockedDecrement(&DeviceData->SynchronizeIsoCleanup);
  339. return;
  340. }
  341. //
  342. // Clean out the schedule, by pointing the frames
  343. // back to the interrupt QHs.
  344. //
  345. currentFrame = UhciGet32BitFrameNumber(DeviceData);
  346. if (currentFrame - DeviceData->LastFrameProcessed >= UHCI_MAX_FRAME ||
  347. ForceClean) {
  348. //
  349. // Schedule overrun.
  350. // Clean out all the frames.
  351. //
  352. UhciKdPrint((DeviceData, 2, "'Overrun L %x C %x\n", DeviceData->LastFrameProcessed, currentFrame));
  353. for (i = 0;
  354. i < UHCI_MAX_FRAME;
  355. i++) {
  356. UhciCleanFrameOfIsochTds (DeviceData, i);
  357. }
  358. } else {
  359. ULONG frameIndex;
  360. // normal cleanup of frames up to the current frame.
  361. frameIndex = ACTUAL_FRAME(currentFrame);
  362. UHCI_ASSERT(DeviceData, frameIndex < UHCI_MAX_FRAME);
  363. for (i = ACTUAL_FRAME(DeviceData->LastFrameProcessed);
  364. i != frameIndex;
  365. i = ACTUAL_FRAME(i+1)) {
  366. UhciCleanFrameOfIsochTds (DeviceData, i);
  367. }
  368. }
  369. DeviceData->LastFrameProcessed = currentFrame;
  370. InterlockedDecrement(&DeviceData->SynchronizeIsoCleanup);
  371. }
  372. VOID
  373. UhciAbortIsochTransfer(
  374. IN PDEVICE_DATA DeviceData,
  375. IN PENDPOINT_DATA EndpointData,
  376. IN PTRANSFER_CONTEXT TransferContext
  377. )
  378. /*++
  379. Routine Description:
  380. Aborts the specified Isoch transfer by freeing all
  381. the TDs associated with said transfer. The dequeuing
  382. of these transfers should have been done in the ISR
  383. where we clean out the schedule.
  384. Arguments:
  385. Return Value:
  386. --*/
  387. {
  388. PHCD_TRANSFER_DESCRIPTOR td;
  389. ULONG i;
  390. //
  391. // The endpoint should not be in the schedule
  392. //
  393. LOGENTRY(DeviceData, G, '_Ait', EndpointData, TransferContext, 0);
  394. UhciKdPrint((DeviceData, 2, "'Aborting isoch transfer %x\n", TransferContext));
  395. //
  396. // Cleanup the isoch transfers that haven't completed yet.
  397. //
  398. UhciCleanOutIsoch(DeviceData, FALSE);
  399. //
  400. // Free up all the tds in this transfer.
  401. //
  402. for (i=0; i<EndpointData->TdCount; i++) {
  403. td = &EndpointData->TdList->Td[i];
  404. if (td->TransferContext == TransferContext) {
  405. UHCI_FREE_TD(DeviceData, EndpointData, td);
  406. }
  407. }
  408. }
  409. VOID
  410. UhciSetIsochEndpointState(
  411. IN PDEVICE_DATA DeviceData,
  412. IN PENDPOINT_DATA EndpointData,
  413. IN MP_ENDPOINT_STATE State
  414. )
  415. /*++
  416. Routine Description:
  417. Arguments:
  418. Return Value:
  419. --*/
  420. {
  421. LOGENTRY(DeviceData, G, '_Sis', EndpointData, State, 0);
  422. }