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.

1163 lines
33 KiB

  1. /*****************************************************************************
  2. * dma.cpp - dma channel
  3. *****************************************************************************
  4. * Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved.
  5. */
  6. #include "private.h"
  7. /*****************************************************************************
  8. * IDmaChannelInit
  9. *****************************************************************************
  10. * Interface for dma channel with Init.
  11. */
  12. DECLARE_INTERFACE_(IDmaChannelInit,IDmaChannelSlave)
  13. {
  14. DEFINE_ABSTRACT_UNKNOWN() // For IUnknown
  15. DEFINE_ABSTRACT_DMACHANNEL() // For IDmaChannel
  16. DEFINE_ABSTRACT_DMACHANNELSLAVE() // For IDmaChannelSlave
  17. STDMETHOD_(NTSTATUS,Init)
  18. ( THIS_
  19. IN PDEVICE_DESCRIPTION DeviceDescription,
  20. IN PDEVICE_OBJECT DeviceObject
  21. ) PURE;
  22. };
  23. typedef IDmaChannelInit *PDMACHANNELINIT;
  24. /*****************************************************************************
  25. * CDmaChannel
  26. *****************************************************************************
  27. * DMA channel implementation.
  28. */
  29. class CDmaChannel
  30. : public IDmaChannelInit,
  31. public CUnknown
  32. {
  33. private:
  34. PDEVICE_OBJECT m_DeviceObject;
  35. PDEVICE_OBJECT m_PhysicalDeviceObject;
  36. BOOLEAN m_Slave;
  37. BOOLEAN m_WriteToDevice;
  38. BOOLEAN m_ChannelActive;
  39. BOOLEAN m_TimedOut;
  40. PDMA_ADAPTER m_DmaAdapter;
  41. PMDL m_Mdl;
  42. PVOID m_MapRegisterBase;
  43. ULONG m_MaxBufferSize;
  44. ULONG m_AllocatedBufferSize;
  45. ULONG m_UsedBufferSize;
  46. ULONG m_MapSize;
  47. PVOID m_VirtualAddress;
  48. PHYSICAL_ADDRESS m_PhysicalAddress;
  49. PVOID m_UserAddress;
  50. ULONG m_TransferCount;
  51. KMUTEX m_DMALock;
  52. public:
  53. DECLARE_STD_UNKNOWN();
  54. DEFINE_STD_CONSTRUCTOR(CDmaChannel);
  55. ~CDmaChannel();
  56. IMP_IDmaChannelSlave;
  57. STDMETHODIMP_(NTSTATUS) Init
  58. (
  59. IN PDEVICE_DESCRIPTION DeviceDescription,
  60. IN PDEVICE_OBJECT DeviceObject
  61. );
  62. friend
  63. IO_ALLOCATION_ACTION
  64. AllocateAdapterCallback
  65. (
  66. IN PDEVICE_OBJECT DeviceObject,
  67. IN PIRP Irp,
  68. IN PVOID MapRegisterBase,
  69. IN PVOID Context
  70. );
  71. };
  72. /*****************************************************************************
  73. * Factory
  74. */
  75. #pragma code_seg("PAGE")
  76. /*****************************************************************************
  77. * CreateDmaChannel()
  78. *****************************************************************************
  79. * Creates a DMA channel.
  80. */
  81. NTSTATUS
  82. CreateDmaChannel
  83. (
  84. OUT PUNKNOWN * Unknown,
  85. IN REFCLSID,
  86. IN PUNKNOWN UnknownOuter OPTIONAL,
  87. IN POOL_TYPE PoolType
  88. )
  89. {
  90. PAGED_CODE();
  91. ASSERT(Unknown);
  92. _DbgPrintF(DEBUGLVL_LIFETIME,("Creating DMA"));
  93. STD_CREATE_BODY_
  94. (
  95. CDmaChannel,
  96. Unknown,
  97. UnknownOuter,
  98. PoolType,
  99. PDMACHANNEL
  100. );
  101. }
  102. /*****************************************************************************
  103. * PcNewDmaChannel()
  104. *****************************************************************************
  105. * Creates a DMA channel.
  106. */
  107. PORTCLASSAPI
  108. NTSTATUS
  109. NTAPI
  110. PcNewDmaChannel
  111. (
  112. OUT PDMACHANNEL * OutDmaChannel,
  113. IN PUNKNOWN OuterUnknown OPTIONAL,
  114. IN POOL_TYPE PoolType,
  115. IN PDEVICE_DESCRIPTION DeviceDescription,
  116. IN PDEVICE_OBJECT DeviceObject OPTIONAL
  117. )
  118. {
  119. PAGED_CODE();
  120. ASSERT(OutDmaChannel);
  121. ASSERT(DeviceDescription);
  122. PUNKNOWN unknown;
  123. NTSTATUS ntStatus =
  124. CreateDmaChannel
  125. (
  126. &unknown,
  127. GUID_NULL,
  128. OuterUnknown,
  129. PoolType
  130. );
  131. if (NT_SUCCESS(ntStatus))
  132. {
  133. PDMACHANNELINIT dmaChannel;
  134. ntStatus =
  135. unknown->QueryInterface
  136. (
  137. IID_IDmaChannel,
  138. (PVOID *) &dmaChannel
  139. );
  140. if (NT_SUCCESS(ntStatus))
  141. {
  142. ntStatus =
  143. dmaChannel->Init
  144. (
  145. DeviceDescription,
  146. DeviceObject
  147. );
  148. if (NT_SUCCESS(ntStatus))
  149. {
  150. *OutDmaChannel = dmaChannel;
  151. }
  152. else
  153. {
  154. dmaChannel->Release();
  155. }
  156. }
  157. unknown->Release();
  158. }
  159. return ntStatus;
  160. }
  161. /*****************************************************************************
  162. * Member functions
  163. */
  164. #pragma code_seg()
  165. /*****************************************************************************
  166. * CDmaChannel::~CDmaChannel()
  167. *****************************************************************************
  168. * Destructor.
  169. * Must put in non-paged code for raising IRQL for calling put adapter.
  170. */
  171. CDmaChannel::~CDmaChannel()
  172. {
  173. ASSERT((KeGetCurrentIrql() < DISPATCH_LEVEL));
  174. KIRQL irqlOld;
  175. _DbgPrintF(DEBUGLVL_LIFETIME,("Destroying DMA (0x%08x)",this));
  176. FreeBuffer();
  177. if (m_DmaAdapter)
  178. {
  179. KeRaiseIrql(DISPATCH_LEVEL,&irqlOld);
  180. m_DmaAdapter->DmaOperations->PutDmaAdapter(m_DmaAdapter);
  181. KeLowerIrql(irqlOld);
  182. m_DmaAdapter = NULL;
  183. }
  184. }
  185. #pragma code_seg("PAGE")
  186. /*****************************************************************************
  187. * CDmaChannel::NonDelegatingQueryInterface()
  188. *****************************************************************************
  189. * Obtains an interface.
  190. */
  191. STDMETHODIMP_(NTSTATUS)
  192. CDmaChannel::
  193. NonDelegatingQueryInterface
  194. (
  195. REFIID Interface,
  196. PVOID * Object
  197. )
  198. {
  199. PAGED_CODE();
  200. ASSERT(Object);
  201. if (IsEqualGUIDAligned(Interface,IID_IUnknown))
  202. {
  203. *Object = PVOID(PUNKNOWN(this));
  204. }
  205. else
  206. if (IsEqualGUIDAligned(Interface,IID_IDmaChannel))
  207. {
  208. *Object = PVOID(PDMACHANNELINIT(this));
  209. }
  210. else
  211. if (IsEqualGUIDAligned(Interface,IID_IDmaChannelSlave) && m_Slave)
  212. {
  213. *Object = PVOID(PDMACHANNELINIT(this));
  214. }
  215. else
  216. {
  217. *Object = NULL;
  218. }
  219. if (*Object)
  220. {
  221. PUNKNOWN(*Object)->AddRef();
  222. return STATUS_SUCCESS;
  223. }
  224. return STATUS_INVALID_PARAMETER;
  225. }
  226. /*****************************************************************************
  227. * PcDmaSlaveDescription()
  228. *****************************************************************************
  229. * Fills in a DMA device description for a slave device based on a resource.
  230. */
  231. PORTCLASSAPI
  232. NTSTATUS
  233. NTAPI
  234. PcDmaSlaveDescription
  235. (
  236. IN PRESOURCELIST ResourceList,
  237. IN ULONG ResourceIndex,
  238. IN BOOLEAN DemandMode,
  239. IN BOOLEAN AutoInitialize,
  240. IN DMA_SPEED DmaSpeed,
  241. IN ULONG MaximumLength,
  242. IN ULONG DmaPort,
  243. OUT PDEVICE_DESCRIPTION DeviceDescription
  244. )
  245. {
  246. PAGED_CODE();
  247. ASSERT(ResourceList);
  248. ASSERT(DeviceDescription);
  249. _DbgPrintF(DEBUGLVL_BLAB,("DmaSlaveDescription"));
  250. NTSTATUS ntStatus = STATUS_SUCCESS;
  251. PCM_PARTIAL_RESOURCE_DESCRIPTOR dmaDescriptor =
  252. ResourceList->FindTranslatedDma(ResourceIndex);
  253. if (! dmaDescriptor)
  254. {
  255. ntStatus = STATUS_INVALID_PARAMETER;
  256. }
  257. else
  258. {
  259. RtlZeroMemory(DeviceDescription,sizeof(DEVICE_DESCRIPTION));
  260. DeviceDescription->Version = DEVICE_DESCRIPTION_VERSION;
  261. DeviceDescription->DmaChannel = dmaDescriptor->u.Dma.Channel;
  262. DeviceDescription->DmaWidth =
  263. (DeviceDescription->DmaChannel > 3) ? Width16Bits : Width8Bits;
  264. DeviceDescription->DemandMode = DemandMode;
  265. DeviceDescription->AutoInitialize = AutoInitialize;
  266. DeviceDescription->DmaSpeed = DmaSpeed;
  267. DeviceDescription->MaximumLength = MaximumLength;
  268. DeviceDescription->DmaPort = DmaPort;
  269. // fill in default interface bus type, Init() will query PnP
  270. DeviceDescription->InterfaceType = Isa;
  271. }
  272. return ntStatus;
  273. }
  274. /*****************************************************************************
  275. * PcDmaMasterDescription()
  276. *****************************************************************************
  277. * Fills in a DMA device description for a master device based on a resource
  278. * list.
  279. */
  280. PORTCLASSAPI
  281. void
  282. NTAPI
  283. PcDmaMasterDescription
  284. (
  285. IN PRESOURCELIST ResourceList OPTIONAL,
  286. IN BOOLEAN ScatterGather,
  287. IN BOOLEAN Dma32BitAddresses,
  288. IN BOOLEAN IgnoreCount,
  289. IN BOOLEAN Dma64BitAddresses,
  290. IN DMA_WIDTH DmaWidth,
  291. IN DMA_SPEED DmaSpeed,
  292. IN ULONG MaximumLength,
  293. IN ULONG DmaPort,
  294. OUT PDEVICE_DESCRIPTION DeviceDescription
  295. )
  296. {
  297. PAGED_CODE();
  298. ASSERT(DeviceDescription);
  299. _DbgPrintF(DEBUGLVL_BLAB,("DmaMasterDescription"));
  300. ASSERT(DeviceDescription);
  301. RtlZeroMemory(DeviceDescription,sizeof(DEVICE_DESCRIPTION));
  302. DeviceDescription->Version =
  303. IgnoreCount ? DEVICE_DESCRIPTION_VERSION1 : DEVICE_DESCRIPTION_VERSION;
  304. DeviceDescription->Master = TRUE;
  305. DeviceDescription->ScatterGather = ScatterGather;
  306. DeviceDescription->Dma32BitAddresses = Dma32BitAddresses;
  307. DeviceDescription->IgnoreCount = IgnoreCount;
  308. DeviceDescription->Dma64BitAddresses = Dma64BitAddresses;
  309. DeviceDescription->DmaWidth = DmaWidth;
  310. DeviceDescription->DmaSpeed = DmaSpeed;
  311. DeviceDescription->MaximumLength = MaximumLength;
  312. DeviceDescription->DmaPort = DmaPort;
  313. // fill in default interface bus type, Init() will query PnP
  314. DeviceDescription->InterfaceType = PCIBus;
  315. }
  316. /*****************************************************************************
  317. * CDmaChannel::Init()
  318. *****************************************************************************
  319. * Initializes the dma channel.
  320. */
  321. STDMETHODIMP_(NTSTATUS)
  322. CDmaChannel::
  323. Init
  324. (
  325. IN PDEVICE_DESCRIPTION DeviceDescription,
  326. IN PDEVICE_OBJECT DeviceObject
  327. )
  328. {
  329. PAGED_CODE();
  330. ASSERT(DeviceDescription);
  331. ASSERT(DeviceObject);
  332. _DbgPrintF(DEBUGLVL_LIFETIME,("Initializing DMA (0x%08x)",this));
  333. PDEVICE_CONTEXT deviceContext =
  334. PDEVICE_CONTEXT(DeviceObject->DeviceExtension);
  335. PDEVICE_OBJECT PhysicalDeviceObject =
  336. deviceContext->PhysicalDeviceObject;
  337. m_DeviceObject = DeviceObject;
  338. m_PhysicalDeviceObject = PhysicalDeviceObject;
  339. m_Slave = !DeviceDescription->ScatterGather;
  340. m_ChannelActive = FALSE;
  341. KeInitializeMutex(&m_DMALock,0);
  342. // determine bus interface type
  343. INTERFACE_TYPE InterfaceType;
  344. ULONG BytesReturned;
  345. NTSTATUS ntStatus = IoGetDeviceProperty( m_PhysicalDeviceObject,
  346. DevicePropertyLegacyBusType,
  347. sizeof(INTERFACE_TYPE),
  348. &InterfaceType,
  349. &BytesReturned );
  350. if(NT_SUCCESS(ntStatus))
  351. {
  352. DeviceDescription->InterfaceType = InterfaceType;
  353. } else
  354. {
  355. // default values were already filled in by PcDmaSlaveDescription (Isa)
  356. // and PcDmaMasterDescription (PCIBus), so we'll just use those.
  357. ntStatus = STATUS_SUCCESS;
  358. }
  359. ULONG mapRegisters = DeviceDescription->MaximumLength / PAGE_SIZE + 1;
  360. m_DmaAdapter = IoGetDmaAdapter( PhysicalDeviceObject,
  361. DeviceDescription,
  362. &mapRegisters );
  363. if (! m_DmaAdapter)
  364. {
  365. ntStatus = STATUS_DEVICE_CONFIGURATION_ERROR;
  366. }
  367. else
  368. if (! mapRegisters)
  369. {
  370. _DbgPrintF(DEBUGLVL_TERSE, ("zero map registers"));
  371. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  372. }
  373. else
  374. {
  375. if (mapRegisters * PAGE_SIZE < DeviceDescription->MaximumLength)
  376. {
  377. m_MaxBufferSize = mapRegisters * PAGE_SIZE;
  378. }
  379. else
  380. {
  381. m_MaxBufferSize = DeviceDescription->MaximumLength;
  382. }
  383. }
  384. return ntStatus;
  385. }
  386. /*****************************************************************************
  387. * CDmaChannel::AllocateBuffer()
  388. *****************************************************************************
  389. * Allocate a buffer for this DMA channel.
  390. */
  391. STDMETHODIMP_(NTSTATUS)
  392. CDmaChannel::
  393. AllocateBuffer
  394. (
  395. IN ULONG BufferSize,
  396. IN PPHYSICAL_ADDRESS PhysicalAddressConstraint OPTIONAL
  397. )
  398. {
  399. PAGED_CODE();
  400. if (!BufferSize)
  401. {
  402. _DbgPrintF(DEBUGLVL_TERSE,("AllocateBuffer: NULL BufferSize!"));
  403. return STATUS_INVALID_PARAMETER;
  404. }
  405. if (PhysicalAddressConstraint)
  406. {
  407. DebugLog((ULONG_PTR)0x02,(ULONG_PTR)BufferSize,(ULONG_PTR)PhysicalAddressConstraint->HighPart,(ULONG_PTR)PhysicalAddressConstraint->LowPart);
  408. if ( (BufferSize > PhysicalAddressConstraint->QuadPart + 1)
  409. || (PhysicalAddressConstraint->QuadPart & (PhysicalAddressConstraint->QuadPart + 1)))
  410. {
  411. ASSERT(BufferSize <= PhysicalAddressConstraint->QuadPart + 1);
  412. // Physical address contraint should be power of 2 (minus 1)
  413. ASSERT(0 == (PhysicalAddressConstraint->QuadPart & (PhysicalAddressConstraint->QuadPart + 1)));
  414. return STATUS_INVALID_PARAMETER;
  415. }
  416. }
  417. else
  418. {
  419. DebugLog((ULONG_PTR)0x03,(ULONG_PTR)BufferSize,0,0);
  420. }
  421. DebugLog((ULONG_PTR)0x04,(ULONG_PTR)m_DmaAdapter,(ULONG_PTR)m_VirtualAddress,(ULONG_PTR)m_MaxBufferSize);
  422. ASSERT(! m_VirtualAddress);
  423. NTSTATUS ntStatus = STATUS_SUCCESS;
  424. if (BufferSize > m_MaxBufferSize)
  425. {
  426. ntStatus = STATUS_INVALID_PARAMETER;
  427. }
  428. else
  429. {
  430. m_UsedBufferSize = m_AllocatedBufferSize = BufferSize;
  431. #define MAX_REJECTED 40
  432. ULONG rejected = 0;
  433. PHYSICAL_ADDRESS rejectedPA[MAX_REJECTED];
  434. PVOID rejectedVA[MAX_REJECTED];
  435. ULONG rejectedSize[MAX_REJECTED];
  436. ULONG paBuffsize = BufferSize;
  437. ULONG rumpAmount;
  438. BOOLEAN matchingAllocation,alignmentFixup;
  439. matchingAllocation = TRUE;
  440. alignmentFixup = FALSE;
  441. while (! m_VirtualAddress)
  442. {
  443. PVOID virtualAddress =
  444. HalAllocateCommonBuffer
  445. (
  446. m_DmaAdapter,
  447. paBuffsize,
  448. &m_PhysicalAddress,
  449. FALSE
  450. );
  451. DebugLog((ULONG_PTR)0x11111111,(ULONG_PTR)virtualAddress,(ULONG_PTR)paBuffsize,(ULONG_PTR)m_PhysicalAddress.LowPart);
  452. if (! virtualAddress)
  453. {
  454. break;
  455. }
  456. if (PhysicalAddressConstraint)
  457. {
  458. PHYSICAL_ADDRESS beginConstraint,endConstraint,endBuffer,nextConstraint;
  459. endBuffer.QuadPart = m_PhysicalAddress.QuadPart + paBuffsize;
  460. beginConstraint.QuadPart = m_PhysicalAddress.QuadPart & ~PhysicalAddressConstraint->QuadPart;
  461. endConstraint.QuadPart = (m_PhysicalAddress.QuadPart + m_AllocatedBufferSize - 1)
  462. & ~PhysicalAddressConstraint->QuadPart;
  463. matchingAllocation = (paBuffsize == m_AllocatedBufferSize)
  464. && (beginConstraint.QuadPart == endConstraint.QuadPart);
  465. DebugLog((ULONG_PTR)m_PhysicalAddress.LowPart,(ULONG_PTR)endBuffer.LowPart,
  466. (ULONG_PTR)beginConstraint.LowPart, (ULONG_PTR)endConstraint.LowPart);
  467. nextConstraint.QuadPart = endConstraint.QuadPart + PhysicalAddressConstraint->QuadPart + 1;
  468. rumpAmount = (ULONG)((nextConstraint.QuadPart - endBuffer.QuadPart) & ~(PAGE_SIZE-1));
  469. DebugLog((ULONG_PTR)m_AllocatedBufferSize,(ULONG_PTR)rumpAmount,0,(ULONG_PTR)nextConstraint.LowPart);
  470. if (rumpAmount > m_AllocatedBufferSize)
  471. {
  472. rumpAmount = m_AllocatedBufferSize;
  473. }
  474. }
  475. if (matchingAllocation)
  476. {
  477. m_VirtualAddress = virtualAddress;
  478. }
  479. else
  480. {
  481. if (rejected == MAX_REJECTED)
  482. {
  483. HalFreeCommonBuffer
  484. (
  485. m_DmaAdapter,
  486. paBuffsize,
  487. m_PhysicalAddress,
  488. virtualAddress,
  489. FALSE
  490. );
  491. DebugLog((ULONG_PTR)0x01111,(ULONG_PTR)m_VirtualAddress,(ULONG_PTR)m_PhysicalAddress.HighPart,(ULONG_PTR)m_PhysicalAddress.LowPart);
  492. break;
  493. }
  494. rejectedPA[rejected] = m_PhysicalAddress;
  495. rejectedVA[rejected] = virtualAddress;
  496. rejectedSize[rejected] = paBuffsize;
  497. rejected++;
  498. alignmentFixup = (!alignmentFixup); // get ready for next time, when we
  499. if (alignmentFixup) // either fill the rest of this zone...
  500. {
  501. paBuffsize = rumpAmount;
  502. }
  503. else // ... or go back to being truthful
  504. {
  505. paBuffsize = m_AllocatedBufferSize;
  506. }
  507. }
  508. }
  509. while (rejected--)
  510. {
  511. HalFreeCommonBuffer
  512. (
  513. m_DmaAdapter,
  514. rejectedSize[rejected],
  515. rejectedPA[rejected],
  516. rejectedVA[rejected],
  517. FALSE
  518. );
  519. DebugLog((ULONG_PTR)0x02222,(ULONG_PTR)rejectedVA[rejected],(ULONG_PTR)rejectedSize[rejected],(ULONG_PTR)rejectedPA[rejected].LowPart);
  520. }
  521. if (! m_VirtualAddress)
  522. {
  523. _DbgPrintF(DEBUGLVL_TERSE,("unable to allocate common buffer"));
  524. m_AllocatedBufferSize = 0;
  525. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  526. }
  527. }
  528. DebugLog((ULONG_PTR)0x0b,(ULONG_PTR)ntStatus,(ULONG_PTR)m_PhysicalAddress.HighPart,(ULONG_PTR)m_PhysicalAddress.LowPart);
  529. DebugLog((ULONG_PTR)0x0c,(ULONG_PTR)m_VirtualAddress,(ULONG_PTR)m_DmaAdapter,(ULONG_PTR)m_AllocatedBufferSize);
  530. return ntStatus;
  531. }
  532. /*****************************************************************************
  533. * CDmaChannel::FreeBuffer()
  534. *****************************************************************************
  535. * Free the buffer for this DMA channel.
  536. */
  537. STDMETHODIMP_(void)
  538. CDmaChannel::
  539. FreeBuffer
  540. ( void
  541. )
  542. {
  543. PAGED_CODE();
  544. if (m_VirtualAddress)
  545. {
  546. if (m_Mdl)
  547. {
  548. IoFreeMdl(m_Mdl);
  549. m_Mdl = NULL;
  550. }
  551. HalFreeCommonBuffer
  552. (
  553. m_DmaAdapter,
  554. m_AllocatedBufferSize,
  555. m_PhysicalAddress,
  556. m_VirtualAddress,
  557. FALSE
  558. );
  559. DebugLog((ULONG_PTR)0x03333,(ULONG_PTR)m_VirtualAddress,(ULONG_PTR)m_PhysicalAddress.HighPart,(ULONG_PTR)m_PhysicalAddress.LowPart);
  560. m_VirtualAddress = NULL;
  561. m_PhysicalAddress.HighPart = 0;
  562. m_PhysicalAddress.LowPart = 0;
  563. }
  564. }
  565. #pragma code_seg()
  566. /*****************************************************************************
  567. * AllocateAdapterCallback()
  568. *****************************************************************************
  569. * Fixed by MartinP 1/29/00 on suggestions from ForrestF.
  570. * Removed spinlock and event.
  571. *
  572. */
  573. static
  574. IO_ALLOCATION_ACTION
  575. AllocateAdapterCallback
  576. (
  577. IN PDEVICE_OBJECT DeviceObject,
  578. IN PIRP Irp OPTIONAL,
  579. IN PVOID MapRegisterBase,
  580. IN PVOID Context
  581. )
  582. {
  583. ASSERT(DeviceObject);
  584. ASSERT(MapRegisterBase);
  585. ASSERT(Context);
  586. CDmaChannel *that = (CDmaChannel *)Context;
  587. if( FALSE == that->m_TimedOut )
  588. {
  589. ULONG MapLength = that->m_MapSize;
  590. that->m_MapRegisterBase = MapRegisterBase;
  591. IoMapTransfer( that->m_DmaAdapter,
  592. that->m_Mdl,
  593. that->m_MapRegisterBase,
  594. MmGetMdlVirtualAddress(that->m_Mdl),
  595. &MapLength,
  596. that->m_WriteToDevice );
  597. if (that->m_MapSize == MapLength)
  598. {
  599. that->m_ChannelActive = TRUE;
  600. }
  601. else
  602. {
  603. _DbgPrintF(DEBUGLVL_TERSE,("***** MapSize Requested (0x%x) != MapLength (0x%x)",that->m_MapSize,MapLength));
  604. that->m_TransferCount = 0;
  605. }
  606. }
  607. return KeepObject;
  608. }
  609. /*****************************************************************************
  610. * CDmaChannel::Start()
  611. *****************************************************************************
  612. * Fixed by MartinP 1/29/00 on suggestions from ForrestF. Removed spinlock
  613. * and event, replaced by single mutex. Must be in non-pageable code, since
  614. * IRQL is raised to DISPATCH_LEVEL.
  615. *
  616. */
  617. STDMETHODIMP_(NTSTATUS)
  618. CDmaChannel::
  619. Start
  620. (
  621. IN ULONG MapSize,
  622. IN BOOLEAN WriteToDevice
  623. )
  624. {
  625. ASSERT((KeGetCurrentIrql() < DISPATCH_LEVEL));
  626. ASSERT(MapSize <= m_AllocatedBufferSize);
  627. NTSTATUS ntStatus = STATUS_SUCCESS;
  628. // don't try to start a channel that is already started
  629. if( TRUE == m_ChannelActive )
  630. {
  631. ASSERT(!"Nested DMA Starts");
  632. return STATUS_UNSUCCESSFUL;
  633. }
  634. if (! m_Mdl)
  635. {
  636. if (m_VirtualAddress)
  637. {
  638. m_Mdl =
  639. IoAllocateMdl
  640. (
  641. m_VirtualAddress,
  642. m_MaxBufferSize,
  643. FALSE,
  644. FALSE,
  645. NULL
  646. );
  647. if (m_Mdl)
  648. {
  649. MmBuildMdlForNonPagedPool(m_Mdl);
  650. }
  651. else
  652. {
  653. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  654. _DbgPrintF(DEBUGLVL_TERSE, ("CDmaChannel::Start, IoAllocateMdl() == NULL"));
  655. }
  656. }
  657. else
  658. {
  659. ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  660. _DbgPrintF(DEBUGLVL_TERSE, ("CDmaChannel::Start, m_VirtualAddress == NULL"));
  661. }
  662. }
  663. if (NT_SUCCESS(ntStatus))
  664. {
  665. m_WriteToDevice = WriteToDevice;
  666. m_MapSize = MapSize;
  667. #if DBG
  668. if (m_TransferCount)
  669. {
  670. _DbgPrintF(DEBUGLVL_TERSE, ("m_TransferCount == 0x%x in CDmaChannel::Start()",m_TransferCount));
  671. }
  672. #endif
  673. m_TransferCount = MapSize;
  674. m_TimedOut = FALSE;
  675. //
  676. // Allocate an adapter channel. When the system is ready,
  677. // we'll process in the callback and then continue after
  678. // the event is signalled.
  679. //
  680. // grab the global DMA lock that serializes IoAllocateAdapterChannel calls
  681. // setup for 10 second timeout (PASSIVE_LEVEL only!!)
  682. LARGE_INTEGER Timeout = RtlConvertLongToLargeInteger( -10L * 10000000L );
  683. ntStatus = KeWaitForMutexObject( &m_DMALock,
  684. Executive,
  685. KernelMode,
  686. FALSE,
  687. &Timeout);
  688. if (STATUS_SUCCESS == ntStatus) // STATUS_TIMEOUT is a success code
  689. {
  690. _DbgPrintF(DEBUGLVL_VERBOSE, ("allocating adapter channel"));
  691. //
  692. // IoAllocateAdapterChannel must be called at DISPATCH_LEVEL
  693. //
  694. KIRQL irqlOld;
  695. KeRaiseIrql(DISPATCH_LEVEL,&irqlOld);
  696. ntStatus = IoAllocateAdapterChannel( m_DmaAdapter,
  697. m_DeviceObject,
  698. BYTES_TO_PAGES(m_AllocatedBufferSize),
  699. AllocateAdapterCallback,
  700. PVOID(this) );
  701. KeLowerIrql(irqlOld);
  702. // OK to continue on our merry way
  703. KeReleaseMutex(&m_DMALock,FALSE);
  704. if (!NT_SUCCESS(ntStatus))
  705. {
  706. _DbgPrintF(DEBUGLVL_TERSE,("Cannot allocate DMA adapter channel"));
  707. m_TransferCount = 0;
  708. }
  709. }
  710. else
  711. {
  712. ASSERT(ntStatus == STATUS_TIMEOUT);
  713. ntStatus = STATUS_UNSUCCESSFUL;
  714. _DbgPrintF(DEBUGLVL_VERBOSE, ("DMA lock timeout, can't allocate DMA channel"));
  715. }
  716. }
  717. return ntStatus;
  718. }
  719. /*****************************************************************************
  720. * CDmaChannel::Stop()
  721. *****************************************************************************
  722. * TODO
  723. */
  724. STDMETHODIMP_(NTSTATUS)
  725. CDmaChannel::
  726. Stop
  727. ( void
  728. )
  729. {
  730. ASSERT((KeGetCurrentIrql() <= DISPATCH_LEVEL));
  731. if (InterlockedExchange(PLONG(&m_TransferCount),0))
  732. {
  733. (void) IoFlushAdapterBuffers
  734. (
  735. m_DmaAdapter,
  736. m_Mdl,
  737. m_MapRegisterBase,
  738. m_VirtualAddress,
  739. m_AllocatedBufferSize,
  740. m_WriteToDevice
  741. );
  742. KIRQL irqlOld;
  743. KeRaiseIrql(DISPATCH_LEVEL,&irqlOld);
  744. IoFreeAdapterChannel(m_DmaAdapter);
  745. m_ChannelActive = FALSE;
  746. KeLowerIrql(irqlOld);
  747. }
  748. return STATUS_SUCCESS;
  749. }
  750. /*****************************************************************************
  751. * CDmaChannel::ReadCounter()
  752. *****************************************************************************
  753. * TODO
  754. */
  755. STDMETHODIMP_(ULONG)
  756. CDmaChannel::
  757. ReadCounter
  758. ( void
  759. )
  760. {
  761. ULONG ulResult = HalReadDmaCounter(m_DmaAdapter);
  762. if ( !m_ChannelActive )
  763. {
  764. ulResult = 0;
  765. }
  766. else
  767. {
  768. if (ulResult == m_TransferCount)
  769. {
  770. ulResult = 0;
  771. }
  772. else if (ulResult > m_TransferCount)
  773. {
  774. _DbgPrintF(DEBUGLVL_TERSE,("HalReadDmaCounter returned value out of range (0x%x >= 0x%x)",ulResult,m_TransferCount));
  775. ulResult = 0;
  776. }
  777. }
  778. return ulResult;
  779. }
  780. /*****************************************************************************
  781. * CDmaChannel::TransferCount()
  782. *****************************************************************************
  783. * Return the amount of data to be transfered via DMA.
  784. */
  785. STDMETHODIMP_(ULONG)
  786. CDmaChannel::
  787. TransferCount
  788. ( void
  789. )
  790. {
  791. return m_TransferCount;
  792. }
  793. /*****************************************************************************
  794. * CDmaChannel::MaximumBufferSize()
  795. *****************************************************************************
  796. * Return the maximum size that can be allocated to this DMA buffer.
  797. */
  798. STDMETHODIMP_(ULONG)
  799. CDmaChannel::
  800. MaximumBufferSize
  801. ( void
  802. )
  803. {
  804. return m_MaxBufferSize;
  805. }
  806. /*****************************************************************************
  807. * CDmaChannel::AllocatedBufferSize()
  808. *****************************************************************************
  809. * Return the original size allocated to this DMA buffer -- the maximum value
  810. * that can be sent to SetBufferSize().
  811. */
  812. STDMETHODIMP_(ULONG)
  813. CDmaChannel::
  814. AllocatedBufferSize
  815. ( void
  816. )
  817. {
  818. return m_AllocatedBufferSize;
  819. }
  820. /*****************************************************************************
  821. * CDmaChannel::BufferSize()
  822. *****************************************************************************
  823. * Return the current size of the DMA buffer.
  824. */
  825. STDMETHODIMP_(ULONG)
  826. CDmaChannel::
  827. BufferSize
  828. ( void
  829. )
  830. {
  831. return m_UsedBufferSize;
  832. }
  833. /*****************************************************************************
  834. * CDmaChannel::SetBufferSize()
  835. *****************************************************************************
  836. * Change the size of the DMA buffer. This cannot exceed the initial
  837. * buffer size returned by AllocatedBufferSize().
  838. */
  839. STDMETHODIMP_(void)
  840. CDmaChannel::
  841. SetBufferSize
  842. (
  843. IN ULONG BufferSize
  844. )
  845. {
  846. ASSERT(BufferSize <= m_AllocatedBufferSize);
  847. m_UsedBufferSize = BufferSize;
  848. }
  849. /*****************************************************************************
  850. * CDmaChannel::SystemAddress()
  851. *****************************************************************************
  852. * Return the virtual address of this DMA buffer.
  853. */
  854. STDMETHODIMP_(PVOID)
  855. CDmaChannel::
  856. SystemAddress
  857. ( void
  858. )
  859. {
  860. return m_VirtualAddress;
  861. }
  862. /*****************************************************************************
  863. * CDmaChannel::PhysicalAddress()
  864. *****************************************************************************
  865. * Return the actual physical address of this DMA buffer.
  866. */
  867. STDMETHODIMP_(PHYSICAL_ADDRESS)
  868. CDmaChannel::
  869. PhysicalAddress
  870. ( void
  871. )
  872. {
  873. ASSERT(m_VirtualAddress);
  874. return m_PhysicalAddress;
  875. }
  876. /*****************************************************************************
  877. * CDmaChannel::GetAdapterObject()
  878. *****************************************************************************
  879. * Return the DMA adapter object (defined in wdm.h).
  880. */
  881. STDMETHODIMP_(PADAPTER_OBJECT)
  882. CDmaChannel::
  883. GetAdapterObject
  884. ( void
  885. )
  886. {
  887. return m_DmaAdapter;
  888. }
  889. STDMETHODIMP_(NTSTATUS)
  890. CDmaChannel::WaitForTC(
  891. ULONG Timeout
  892. )
  893. /*++
  894. Routine Description:
  895. Waits for the DMA transfer to complete, else times out.
  896. Arguments:
  897. Timeout - Specifies the timeout in microseconds to wait for the
  898. transfer to complete. This is rounded down to the nearest 10
  899. microsecond increment.
  900. Return:
  901. STATUS_SUCCESS if the transfer completed, else an error code.
  902. --*/
  903. {
  904. ULONG Count;
  905. if (Count = HalReadDmaCounter(m_DmaAdapter))
  906. {
  907. ULONG LastCount = Count;
  908. Timeout /= 10;
  909. while ((LastCount !=
  910. (Count = HalReadDmaCounter( m_DmaAdapter ))) && Timeout)
  911. {
  912. LastCount = Count;
  913. KeStallExecutionProcessor( 10 );
  914. Timeout--;
  915. }
  916. return (Timeout > 0) ? STATUS_SUCCESS : STATUS_IO_TIMEOUT;
  917. }
  918. else
  919. {
  920. return STATUS_SUCCESS;
  921. }
  922. }
  923. /*****************************************************************************
  924. * CDmaChannel::CopyTo()
  925. *****************************************************************************
  926. * Copy data into the DMA buffer. This can be overridden if a client needs
  927. * to massage the data on output.
  928. */
  929. STDMETHODIMP_(void)
  930. CDmaChannel::
  931. CopyTo
  932. ( IN PVOID Destination,
  933. IN PVOID Source,
  934. IN ULONG ByteCount
  935. )
  936. {
  937. #ifndef _X86_
  938. RtlCopyMemory(Destination,Source,ByteCount);
  939. #else
  940. //
  941. // Jeff says this is the way to go.
  942. //
  943. _asm {
  944. mov esi, Source
  945. mov ecx, ByteCount
  946. mov edi, Destination
  947. add edi, ecx
  948. neg ecx
  949. sub edi, 16
  950. jmp Next16
  951. Loop16:
  952. mov eax, DWORD PTR [esi]
  953. mov ebx, DWORD PTR [esi+4]
  954. mov DWORD PTR [edi+ecx], eax
  955. mov DWORD PTR [edi+ecx+4], ebx
  956. mov eax, DWORD PTR [esi+8]
  957. mov ebx, DWORD PTR [esi+12]
  958. mov DWORD PTR [edi+ecx+8], eax
  959. mov DWORD PTR [edi+ecx+12], ebx
  960. add esi, 16
  961. Next16:
  962. add ecx, 16
  963. jle Loop16
  964. sub ecx, 16
  965. jmp Next4
  966. Loop4:
  967. mov eax, DWORD PTR [esi]
  968. add esi, 4
  969. mov DWORD PTR [edi+ecx+12], eax
  970. Next4:
  971. add ecx, 4
  972. jle Loop4
  973. sub ecx, 4
  974. jz Done1
  975. Final1:
  976. mov al, BYTE PTR [esi]
  977. inc esi
  978. mov BYTE PTR [edi+ecx+16], al
  979. inc ecx
  980. jnz Final1
  981. Done1:
  982. }
  983. #endif
  984. }
  985. /*****************************************************************************
  986. * CDmaChannel::CopyFrom()
  987. *****************************************************************************
  988. * Copy data out of the DMA buffer. This can be overridden if a client needs
  989. * to massage the data on input.
  990. */
  991. STDMETHODIMP_(void)
  992. CDmaChannel::
  993. CopyFrom
  994. ( IN PVOID Destination,
  995. IN PVOID Source,
  996. IN ULONG ByteCount
  997. )
  998. {
  999. CopyTo(Destination,Source,ByteCount);
  1000. }