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.

750 lines
21 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. xsplit.c
  5. Abstract:
  6. splits a DMA transfer into multiple smallest transfers
  7. that the miniport can handle.
  8. Environment:
  9. kernel mode only
  10. Notes:
  11. Revision History:
  12. 6-20-99 : created
  13. --*/
  14. #include "common.h"
  15. #ifdef ALLOC_PRAGMA
  16. #endif
  17. ULONG
  18. USBPORT_MakeSplitTransfer(
  19. PDEVICE_OBJECT FdoDeviceObject,
  20. PTRANSFER_SG_LIST SgList,
  21. PHCD_TRANSFER_CONTEXT SplitTransfer,
  22. ULONG MaxTransferLength,
  23. ULONG MaxPacketLength,
  24. PULONG Idx,
  25. PULONG Offset,
  26. ULONG BytesToMap,
  27. ULONG LengthMapped
  28. )
  29. /*++
  30. Routine Description:
  31. Arguments:
  32. Return Value:
  33. --*/
  34. {
  35. PTRANSFER_SG_ENTRY32 sgEntry;
  36. PTRANSFER_SG_LIST splitSgList;
  37. ULONG length;
  38. sgEntry = &SgList->SgEntry[*Idx];
  39. LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'splt',
  40. SplitTransfer,
  41. *Idx,
  42. *Offset);
  43. USBPORT_ASSERT(MaxTransferLength % MaxPacketLength == 0)
  44. if ((sgEntry->Length - *Offset) > MaxTransferLength) {
  45. // case 2, this transfer falls entirely within
  46. // this sg entry
  47. LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'spt1',
  48. MaxTransferLength,
  49. *Offset,
  50. sgEntry);
  51. // make one transfer from sg[Idx]
  52. // inc offset
  53. length = MaxTransferLength;
  54. splitSgList = &SplitTransfer->SgList;
  55. splitSgList->SgCount = 1;
  56. splitSgList->SgEntry[0].LogicalAddress.Hw32 =
  57. sgEntry->LogicalAddress.Hw32 + *Offset;
  58. splitSgList->SgEntry[0].SystemAddress =
  59. sgEntry->SystemAddress + *Offset;
  60. splitSgList->SgEntry[0].Length = length;
  61. // start offset is always 0 for the first element
  62. splitSgList->SgEntry[0].StartOffset = 0;
  63. SplitTransfer->Tp.TransferBufferLength =
  64. length;
  65. SplitTransfer->Tp.MiniportFlags = MPTX_SPLIT_TRANSFER;
  66. splitSgList->MdlVirtualAddress =
  67. SgList->MdlVirtualAddress + LengthMapped;
  68. splitSgList->MdlSystemAddress =
  69. SgList->MdlSystemAddress + LengthMapped;
  70. // indicate that this is a split child
  71. SET_FLAG(SplitTransfer->Flags, USBPORT_TXFLAG_SPLIT_CHILD);
  72. *Offset += length;
  73. BytesToMap -= length;
  74. } else {
  75. // make transfer from last part of sg[Idx]
  76. // and first part of sg[Idx+1] if necessary,
  77. // inc Idx
  78. // reset offset
  79. // case 2
  80. length = sgEntry->Length - *Offset;
  81. LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'spt2',
  82. MaxTransferLength,
  83. *Offset,
  84. length);
  85. USBPORT_ASSERT(length <= MaxTransferLength);
  86. // last part of sg1;
  87. splitSgList = &SplitTransfer->SgList;
  88. LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'sgE1',
  89. splitSgList,
  90. sgEntry,
  91. 0);
  92. splitSgList->SgCount = 1;
  93. splitSgList->SgEntry[0].LogicalAddress.Hw32 =
  94. sgEntry->LogicalAddress.Hw32 + *Offset;
  95. splitSgList->SgEntry[0].SystemAddress =
  96. sgEntry->SystemAddress + *Offset;
  97. splitSgList->SgEntry[0].Length = length;
  98. // start offset is always 0 for the first element
  99. splitSgList->SgEntry[0].StartOffset = 0;
  100. SplitTransfer->Tp.TransferBufferLength =
  101. length;
  102. SplitTransfer->Tp.MiniportFlags = MPTX_SPLIT_TRANSFER;
  103. splitSgList->MdlVirtualAddress =
  104. SgList->MdlVirtualAddress + LengthMapped;
  105. splitSgList->MdlSystemAddress =
  106. SgList->MdlSystemAddress + LengthMapped;
  107. // indicate that this is a split child
  108. SET_FLAG(SplitTransfer->Flags, USBPORT_TXFLAG_SPLIT_CHILD);
  109. *Offset += length;
  110. BytesToMap -= length;
  111. // calculate max size of second part
  112. length = MaxTransferLength - length;
  113. if (length > BytesToMap) {
  114. length = BytesToMap;
  115. }
  116. if (length == 0) {
  117. (*Idx)++;
  118. *Offset = 0;
  119. } else {
  120. LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'spt3',
  121. MaxTransferLength,
  122. *Offset,
  123. length);
  124. (*Idx)++;
  125. *Offset = 0;
  126. sgEntry = &SgList->SgEntry[*Idx];
  127. LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'sgE2',
  128. splitSgList,
  129. sgEntry,
  130. *Idx);
  131. splitSgList->SgCount++;
  132. splitSgList->SgEntry[1].LogicalAddress.Hw32 =
  133. sgEntry->LogicalAddress.Hw32 + *Offset;
  134. splitSgList->SgEntry[1].SystemAddress =
  135. sgEntry->SystemAddress + *Offset;
  136. splitSgList->SgEntry[1].Length = length;
  137. splitSgList->SgEntry[1].StartOffset =
  138. splitSgList->SgEntry[0].Length;
  139. SplitTransfer->Tp.TransferBufferLength += length;
  140. *Offset += length;
  141. BytesToMap -= length;
  142. }
  143. }
  144. LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'spt>',
  145. BytesToMap,
  146. 0,
  147. 0);
  148. return BytesToMap;
  149. }
  150. NTSTATUS
  151. USBPORT_SplitBulkInterruptTransfer(
  152. PDEVICE_OBJECT FdoDeviceObject,
  153. PHCD_ENDPOINT Endpoint,
  154. PHCD_TRANSFER_CONTEXT Transfer,
  155. PLIST_ENTRY TransferList
  156. )
  157. /*++
  158. Routine Description:
  159. Split a bulk or interrupt transfer
  160. Arguments:
  161. Return Value:
  162. nt status code
  163. --*/
  164. {
  165. ULONG maxTransferLength, maxPacketLength;
  166. PHCD_TRANSFER_CONTEXT splitTransfer;
  167. PTRANSFER_SG_LIST sgList;
  168. LIST_ENTRY tmpList;
  169. ULONG idx, i, offset, bytesToMap, lengthMapped;
  170. ULONG numberOfSplits;
  171. PLIST_ENTRY listEntry;
  172. sgList = &Transfer->SgList;
  173. maxPacketLength = EP_MAX_PACKET(Endpoint);
  174. USBPORT_ASSERT(EP_MAX_TRANSFER(Endpoint) >= maxPacketLength);
  175. // round to the smallest multiple of max packet size
  176. maxTransferLength =
  177. (EP_MAX_TRANSFER(Endpoint)/maxPacketLength) * maxPacketLength;
  178. // some notes:
  179. //
  180. //
  181. // The MAXTRANSFER is equal to USB_PAGE_SIZE (4k) and
  182. // the transfer sg list is broken into usb pages. In this
  183. // case we construct a transfer structure for each pair of
  184. // sg entries we round the length of the first sg entry down
  185. // to the highest multiple of MAXPACKET so we get a split
  186. // pattern like this:
  187. //
  188. // {Sg1}{.Sg2.}{.Sg3.}{.Sg4.} sg entries
  189. // |------|------|------|------| page breaks
  190. // |----------------------| original transfer
  191. // <...><>
  192. // <...><>
  193. // <...><><.>
  194. // {1 }{2 }{3 }{4} splits
  195. //
  196. // The MAXTRANSFER is less than USB_PAGE_SIZE (4k) and the
  197. // transfer sg list is broken into usb pages.
  198. //
  199. // the pattern will look like this:
  200. //
  201. // {...Sg1......}{......Sg2......}{......Sg3......}
  202. // |----------------|----------------|----------------|
  203. // |-----------------------------------|
  204. // <..>
  205. // <..>
  206. // <..>
  207. // <><>
  208. // <..>
  209. // <..>
  210. // <..>
  211. // <><>
  212. // <..>
  213. // {1 }{2 }{3 }{4 }{5 }{6 }{7 }{8 }{9 }
  214. // cases:
  215. // case 1 - transfer lies within the sg entry
  216. // case 2 - transfer overlaps the sg entry
  217. //
  218. // The MAXTRANSFER is greater than USB_PAGE_SIZE (4k)
  219. // ie etc, we currently don't handle this case
  220. //
  221. // Note: since the buffer is currently completely mapped
  222. // and locked it is better to tune the mniport to take the
  223. // larger transfers if possible
  224. if (EP_MAX_TRANSFER(Endpoint) > USB_PAGE_SIZE) {
  225. BUGCHECK(USBBUGCODE_INTERNAL_ERROR, 0, 0, 0);
  226. }
  227. // allocate the split elements
  228. // mark the parent transfer as a split
  229. SET_FLAG(Transfer->Flags, USBPORT_TXFLAG_SPLIT);
  230. numberOfSplits =
  231. Transfer->Tp.TransferBufferLength / maxTransferLength + 1;
  232. LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'sptC',
  233. numberOfSplits,
  234. 0,
  235. 0);
  236. InitializeListHead(&tmpList);
  237. for (i=0; i<numberOfSplits; i++) {
  238. ALLOC_POOL_Z(splitTransfer,
  239. NonPagedPool,
  240. Transfer->TotalLength);
  241. if (splitTransfer == NULL) {
  242. goto SplitBulkInterruptTransfer_Fail;
  243. }
  244. RtlCopyMemory(splitTransfer,
  245. Transfer,
  246. Transfer->TotalLength);
  247. splitTransfer->MiniportContext = (PUCHAR) splitTransfer;
  248. splitTransfer->MiniportContext += splitTransfer->PrivateLength;
  249. InitializeListHead(&splitTransfer->DoubleBufferList);
  250. InsertTailList(&tmpList,
  251. &splitTransfer->TransferLink);
  252. }
  253. idx = 0;
  254. offset = 0;
  255. bytesToMap = Transfer->Tp.TransferBufferLength;
  256. lengthMapped = 0;
  257. while (bytesToMap) {
  258. LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'sptM',
  259. bytesToMap,
  260. offset,
  261. idx);
  262. listEntry = RemoveHeadList(&tmpList);
  263. splitTransfer = (PHCD_TRANSFER_CONTEXT) CONTAINING_RECORD(
  264. listEntry,
  265. struct _HCD_TRANSFER_CONTEXT,
  266. TransferLink);
  267. ASSERT_TRANSFER(splitTransfer);
  268. bytesToMap = USBPORT_MakeSplitTransfer(FdoDeviceObject,
  269. sgList,
  270. splitTransfer,
  271. maxTransferLength,
  272. maxPacketLength,
  273. &idx,
  274. &offset,
  275. bytesToMap,
  276. lengthMapped);
  277. lengthMapped += splitTransfer->Tp.TransferBufferLength;
  278. InsertTailList(TransferList,
  279. &splitTransfer->TransferLink);
  280. InsertTailList(&Transfer->SplitTransferList,
  281. &splitTransfer->SplitLink);
  282. }
  283. // free extra splits we did not use
  284. while (!IsListEmpty(&tmpList)) {
  285. listEntry = RemoveHeadList(&tmpList);
  286. splitTransfer = (PHCD_TRANSFER_CONTEXT) CONTAINING_RECORD(
  287. listEntry,
  288. struct _HCD_TRANSFER_CONTEXT,
  289. TransferLink);
  290. ASSERT_TRANSFER(splitTransfer);
  291. UNSIG(splitTransfer);
  292. FREE_POOL(FdoDeviceObject, splitTransfer);
  293. }
  294. return STATUS_SUCCESS;
  295. SplitBulkInterruptTransfer_Fail:
  296. TEST_TRAP();
  297. // free the tmp list
  298. while (!IsListEmpty(&tmpList)) {
  299. listEntry = RemoveHeadList(&tmpList);
  300. splitTransfer = (PHCD_TRANSFER_CONTEXT) CONTAINING_RECORD(
  301. listEntry,
  302. struct _HCD_TRANSFER_CONTEXT,
  303. TransferLink);
  304. ASSERT_TRANSFER(splitTransfer);
  305. UNSIG(splitTransfer);
  306. FREE_POOL(FdoDeviceObject, splitTransfer);
  307. }
  308. return STATUS_INSUFFICIENT_RESOURCES;
  309. }
  310. #if 0
  311. NTSTATUS
  312. USBPORT_SplitIsochronousTransfer(
  313. PDEVICE_OBJECT FdoDeviceObject,
  314. PHCD_ENDPOINT Endpoint,
  315. PHCD_TRANSFER_CONTEXT Transfer,
  316. PLIST_ENTRY TransferList
  317. )
  318. /*++
  319. Routine Description:
  320. Split an iso transfer
  321. Arguments:
  322. Return Value:
  323. none
  324. --*/
  325. {
  326. PMINIPORT_ISO_TRANSFER isoTransfer, splitIsoTransfer;
  327. PHCD_TRANSFER_CONTEXT splitTransfer;
  328. LIST_ENTRY tmpList;
  329. // first figure out how many child transfer structures
  330. // we will need and allocate them, we do this based on
  331. // the most packets we can fit in to a request
  332. // we do not fixup the SG table for the child transfers
  333. // since this information is not passed to the miniport
  334. isoTransfer = Transfer->IsoTransfer;
  335. transferCount = 0;
  336. length = 0;
  337. maxSplitLength = EP_MAX_TRANSFER(Endpoint);
  338. for (i=0; i<isoTransfer->PacketCount; i++) {
  339. if (length + isoTransfer->Packets[i].Length > maxSplitLength) {
  340. length = 0;
  341. transferCount++;
  342. } else {
  343. length += isoTransfer->Packets[i].Length
  344. }
  345. }
  346. // transferCount is the number of child transfers,
  347. // allocate them the now and clone the parent
  348. InitializeListHead(tmpList);
  349. for (i=0; i<transferCount; i++) {
  350. TRANSFER_SG_LIST sgList;
  351. splitTransfer = ALLOC()
  352. if (splitTransfer == NULL) {
  353. // release resources and return an error
  354. TEST_TRAP();
  355. xxx;
  356. break;
  357. }
  358. RtlCopyMemory(splitTransfer,
  359. Transfer,
  360. Transfer->TotalLength);
  361. sgList = &splitTransfer->SgList;
  362. // zap the sg table since we don't use it for children
  363. for (j=0; j<sgList->SgCount; j++) {
  364. sgList->SgEntry[j].LogicalAddress = 0xffffffff;
  365. sgList->SgEntry[j].SystemAddress = USB_BAD_PTR;
  366. sgList->SgEntry[j].Length = 0xffffffff;
  367. sgList->SgEntry[j].StartOffset = 0xffffffff;
  368. }
  369. sgList->Flags = 0xFFFFFFFF;
  370. sgList->MdlVirtualAddress = USB_BAD_PTR;
  371. sgList->MdlSystemAddress = USB_BAD_PTR;
  372. sgList->SgCount = 0xFFFFFFFF;
  373. InsertTailList(&tmpList,
  374. &splitTransfer->TransferLink);
  375. }
  376. // we now have a list of child transfer structures.
  377. // intialize them
  378. pkt = 0;
  379. systemAddress = isoTransfer->SystemAddress;
  380. InitializeListHead(&Transfer->SplitTransferList);
  381. do {
  382. listEntry = RemoveHeadList(&tmpList);
  383. splitTransfer = (PHCD_TRANSFER_CONTEXT) CONTAINING_RECORD(
  384. listEntry,
  385. struct _HCD_TRANSFER_CONTEXT,
  386. TransferLink);
  387. ASSERT_TRANSFER(splitTransfer);
  388. SET_FLAG(transfer->Flags, USBPORT_TXFLAG_SPLIT_CHILD);
  389. splitIsoTransfer = splitTransfer->IsoTransfer;
  390. splitIsoTransfer->PacketCount = 0;
  391. splitIsoTransfer->SystemAdderess = systemAddress;
  392. splitLength = 0;
  393. i = 0;
  394. InsertTailList(TransferList,
  395. &splitTransfer->TransferLink);
  396. InsertTailList(Transfer->SplitTransferList,
  397. &splitTransfer->SplitLink);
  398. while (1) {
  399. if (splitLength + isoTransfer->Packets[pkt].Length > maxSplitLength) {
  400. // this transfer is filled, move to the next one
  401. systemAddress += splitLength;
  402. break;
  403. } else {
  404. splitIsoTransfer->Packets[i] = isoTransfer->Packets[pkt];
  405. splitLength += splitIsoTransfer->Packets[i].Length;
  406. splitIsoTransfer->PacketCount++;
  407. pkt++;
  408. i++;
  409. }
  410. }
  411. } while (pkt < isoTransfer->PacketCount);
  412. }
  413. #endif
  414. VOID
  415. USBPORT_SplitTransfer(
  416. PDEVICE_OBJECT FdoDeviceObject,
  417. PHCD_ENDPOINT Endpoint,
  418. PHCD_TRANSFER_CONTEXT Transfer,
  419. PLIST_ENTRY TransferList
  420. )
  421. /*++
  422. Routine Description:
  423. Splits a transfer into multiple transfers of the proper size
  424. for the miniport.
  425. Returns a list of transfer structures that need to be added to
  426. the active list. If the transfer does not need to be split the
  427. list will contain only the original transfer.
  428. Arguments:
  429. Return Value:
  430. none
  431. --*/
  432. {
  433. InitializeListHead(TransferList);
  434. InitializeListHead(&Transfer->SplitTransferList);
  435. Transfer->UsbdStatus = USBD_STATUS_SUCCESS;
  436. if (Transfer->Tp.TransferBufferLength <= EP_MAX_TRANSFER(Endpoint)) {
  437. // no split needed
  438. InsertTailList(TransferList,
  439. &Transfer->TransferLink);
  440. return;
  441. }
  442. switch(Endpoint->Parameters.TransferType) {
  443. case Interrupt:
  444. case Bulk:
  445. USBPORT_SplitBulkInterruptTransfer(FdoDeviceObject,
  446. Endpoint,
  447. Transfer,
  448. TransferList);
  449. break;
  450. case Control:
  451. // not supported yet
  452. // although currently not supported the USBD stack never
  453. // correctly implemented transfers > 4k so we fudge it here
  454. //
  455. BUGCHECK(USBBUGCODE_INTERNAL_ERROR, 0, 0, 0);
  456. break;
  457. case Isochronous:
  458. BUGCHECK(USBBUGCODE_INTERNAL_ERROR, 0, 0, 0);
  459. break;
  460. }
  461. }
  462. VOID
  463. USBPORT_DoneSplitTransfer(
  464. PHCD_TRANSFER_CONTEXT SplitTransfer
  465. )
  466. /*++
  467. Routine Description:
  468. Called when a split transfer is completed by hardware
  469. this function only completes active transfers
  470. Arguments:
  471. Return Value:
  472. --*/
  473. {
  474. PHCD_TRANSFER_CONTEXT transfer;
  475. PHCD_ENDPOINT endpoint;
  476. PDEVICE_OBJECT fdoDeviceObject;
  477. KIRQL tIrql;
  478. endpoint = SplitTransfer->Endpoint;
  479. ASSERT_ENDPOINT(endpoint);
  480. fdoDeviceObject = endpoint->FdoDeviceObject;
  481. LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'dnSP',
  482. SplitTransfer, 0, 0);
  483. // get the parent
  484. transfer = SplitTransfer->Transfer;
  485. ASSERT_TRANSFER(transfer);
  486. //
  487. // copy the child data to the parent transfer
  488. //
  489. transfer->MiniportBytesTransferred +=
  490. SplitTransfer->MiniportBytesTransferred;
  491. // error ?
  492. //
  493. if (SplitTransfer->UsbdStatus != USBD_STATUS_SUCCESS &&
  494. !TEST_FLAG(SplitTransfer->Flags, USBPORT_TXFLAG_KILL_SPLIT)) {
  495. transfer->UsbdStatus = SplitTransfer->UsbdStatus;
  496. }
  497. ACQUIRE_TRANSFER_LOCK(fdoDeviceObject, transfer, tIrql);
  498. // remove this transfer from the list
  499. LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'rmSP',
  500. transfer, 0, SplitTransfer);
  501. RemoveEntryList(&SplitTransfer->SplitLink);
  502. // flush any triple buffers
  503. USBPORT_FlushAdapterDBs(fdoDeviceObject,
  504. SplitTransfer);
  505. // free this child
  506. UNSIG(SplitTransfer);
  507. FREE_POOL(fdoDeviceObject, SplitTransfer);
  508. // is the transfer complete?
  509. if (IsListEmpty(&transfer->SplitTransferList)) {
  510. LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'cpSP',
  511. transfer, 0, 0);
  512. RELEASE_TRANSFER_LOCK(fdoDeviceObject, transfer, tIrql);
  513. USBPORT_DoneTransfer(transfer);
  514. } else {
  515. RELEASE_TRANSFER_LOCK(fdoDeviceObject, transfer, tIrql);
  516. }
  517. }
  518. VOID
  519. USBPORT_CancelSplitTransfer(
  520. PDEVICE_OBJECT FdoDeviceObject,
  521. PHCD_TRANSFER_CONTEXT SplitTransfer
  522. )
  523. /*++
  524. Routine Description:
  525. Arguments:
  526. Return Value:
  527. --*/
  528. {
  529. PHCD_TRANSFER_CONTEXT transfer;
  530. PHCD_ENDPOINT endpoint;
  531. PDEVICE_OBJECT fdoDeviceObject;
  532. KIRQL tIrql;
  533. endpoint = SplitTransfer->Endpoint;
  534. ASSERT_ENDPOINT(endpoint);
  535. fdoDeviceObject = endpoint->FdoDeviceObject;
  536. // remove the child, when all children are gone put the
  537. // parent on the cancel list
  538. LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'caSP',
  539. SplitTransfer, 0, 0);
  540. // get the parent
  541. transfer = SplitTransfer->Transfer;
  542. ASSERT_TRANSFER(transfer);
  543. //
  544. // copy the child data to the parent transfer
  545. //
  546. transfer->MiniportBytesTransferred +=
  547. SplitTransfer->MiniportBytesTransferred;
  548. ACQUIRE_TRANSFER_LOCK(fdoDeviceObject, transfer, tIrql);
  549. // remove this transfer from the list
  550. LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'rmSP',
  551. transfer, 0, SplitTransfer);
  552. RemoveEntryList(&SplitTransfer->SplitLink);
  553. RELEASE_TRANSFER_LOCK(fdoDeviceObject, transfer, tIrql);
  554. // free this child
  555. UNSIG(SplitTransfer);
  556. FREE_POOL(fdoDeviceObject, SplitTransfer);
  557. // is the transfer complete?
  558. if (IsListEmpty(&transfer->SplitTransferList)) {
  559. LOGENTRY(NULL, fdoDeviceObject, LOG_XFERS, 'cpSC',
  560. transfer, 0, 0);
  561. InsertTailList(&endpoint->CancelList, &transfer->TransferLink);
  562. }
  563. }