summaryrefslogtreecommitdiff
path: root/general/PLX9x5x/sys/Write.c
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /general/PLX9x5x/sys/Write.c
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'general/PLX9x5x/sys/Write.c')
-rw-r--r--general/PLX9x5x/sys/Write.c525
1 files changed, 525 insertions, 0 deletions
diff --git a/general/PLX9x5x/sys/Write.c b/general/PLX9x5x/sys/Write.c
new file mode 100644
index 00000000..3e4d8b53
--- /dev/null
+++ b/general/PLX9x5x/sys/Write.c
@@ -0,0 +1,525 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ Write.c
+
+Abstract:
+
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#include "Write.tmh"
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+VOID
+PLxEvtIoWrite(
+ IN WDFQUEUE Queue,
+ IN WDFREQUEST Request,
+ IN size_t Length
+ )
+/*++
+
+Routine Description:
+
+ Called by the framework as soon as it receives a write request.
+ If the device is not ready, fail the request.
+ Otherwise get scatter-gather list for this request and send the
+ packet to the hardware for DMA.
+
+Arguments:
+
+ Queue - Handle to the framework queue object that is associated
+ with the I/O request.
+ Request - Handle to a framework request object.
+
+ Length - Length of the IO operation
+ The default property of the queue is to not dispatch
+ zero lenght read & write requests to the driver and
+ complete is with status success. So we will never get
+ a zero length request.
+
+Return Value:
+
+--*/
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PDEVICE_EXTENSION devExt = NULL;
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ "--> PLxEvtIoWrite: Request %p", Request);
+
+ //
+ // Get the DevExt from the Queue handle
+ //
+ devExt = PLxGetDeviceContext(WdfIoQueueGetDevice(Queue));
+
+ //
+ // Validate the Length parameter.
+ //
+ if (Length > PCI9656_SRAM_SIZE) {
+ status = STATUS_INVALID_BUFFER_SIZE;
+ goto CleanUp;
+ }
+
+ //
+ // Following code illustrates two different ways of initializing a DMA
+ // transaction object. If ASSOC_WRITE_REQUEST_WITH_DMA_TRANSACTION is
+ // defined in the sources file, the first section will be used.
+ //
+#ifdef ASSOC_WRITE_REQUEST_WITH_DMA_TRANSACTION
+
+ //
+ // This section illustrates how to create and initialize
+ // a DmaTransaction using a WDF Request.
+ // This type of coding pattern would probably be the most commonly used
+ // for handling client Requests.
+ //
+ status = WdfDmaTransactionInitializeUsingRequest(
+ devExt->WriteDmaTransaction,
+ Request,
+ PLxEvtProgramWriteDma,
+ WdfDmaDirectionWriteToDevice );
+
+ if(!NT_SUCCESS(status)) {
+ TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE,
+ "WdfDmaTransactionInitializeUsingRequest failed: "
+ "%!STATUS!", status);
+ goto CleanUp;
+ }
+#else
+ //
+ // This section illustrates how to create and initialize
+ // a DmaTransaction via direct parameters (e.g. not using a WDF Request).
+ // This type of coding pattern might be used for driver-initiated DMA
+ // operations (e.g. DMA operations not based on driver client requests.)
+ //
+ // NOTE: This example unpacks the WDF Request in order to get a set of
+ // parameters for the call to WdfDmaTransactionInitialize. While
+ // this is completely legimate, the represenative usage pattern
+ // for WdfDmaTransactionIniitalize would have the driver create/
+ // initialized a DmaTransactin without a WDF Request. A simple
+ // example might be where the driver needs to DMA the devices's
+ // firmware to it during device initialization. There would be
+ // no WDF Request; the driver would supply the parameters for
+ // WdfDmaTransactionInitialize directly.
+ //
+ {
+ PTRANSACTION_CONTEXT transContext;
+ PMDL mdl;
+ PVOID virtualAddress;
+ ULONG length;
+
+ //
+ // Initialize this new DmaTransaction with direct parameters.
+ //
+ status = WdfRequestRetrieveInputWdmMdl(Request, &mdl);
+ if (!NT_SUCCESS(status)) {
+ TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE,
+ "WdfRequestRetrieveInputWdmMdl failed: %!STATUS!", status);
+ goto CleanUp;
+ }
+
+ virtualAddress = MmGetMdlVirtualAddress(mdl);
+ length = MmGetMdlByteCount(mdl);
+
+ _Analysis_assume_(length > 0);
+ status = WdfDmaTransactionInitialize( devExt->WriteDmaTransaction,
+ PLxEvtProgramWriteDma,
+ WdfDmaDirectionWriteToDevice,
+ mdl,
+ virtualAddress,
+ length );
+
+ if(!NT_SUCCESS(status)) {
+ TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE,
+ "WdfDmaTransactionInitialize failed: %!STATUS!", status);
+ goto CleanUp;
+ }
+
+ //
+ // Retreive this DmaTransaction's context ptr (aka TRANSACTION_CONTEXT)
+ // and fill it in with info.
+ //
+ transContext = PLxGetTransactionContext( devExt->WriteDmaTransaction );
+ transContext->Request = Request;
+ }
+#endif
+
+#if 0 //FYI
+ //
+ // Modify the MaximumLength for this DmaTransaction only.
+ //
+ // Note: The new length must be less than or equal to that set when
+ // the DmaEnabler was created.
+ //
+ {
+ ULONG length = devExt->MaximumTransferLength / 2;
+
+ //TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ // "Setting a new MaxLen %d", length);
+
+ WdfDmaTransactionSetMaximumLength( devExt->WriteDmaTransaction, length );
+ }
+#endif
+
+ //
+ // Execute this DmaTransaction transaction.
+ //
+ status = WdfDmaTransactionExecute( devExt->WriteDmaTransaction,
+ WDF_NO_CONTEXT);
+
+ if(!NT_SUCCESS(status)) {
+
+ //
+ // Couldn't execute this DmaTransaction, so fail Request.
+ //
+ TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE,
+ "WdfDmaTransactionExecute failed: %!STATUS!", status);
+ goto CleanUp;
+ }
+
+ //
+ // Indicate that Dma transaction has been started successfully. The request
+ // will be complete by the Dpc routine when the DMA transaction completes.
+ //
+ status = STATUS_SUCCESS;
+
+CleanUp:
+
+ //
+ // If there are errors, then clean up and complete the Request.
+ //
+ if (!NT_SUCCESS(status)) {
+ WdfDmaTransactionRelease(devExt->WriteDmaTransaction);
+ WdfRequestComplete(Request, status);
+ }
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ "<-- PLxEvtIoWrite: %!STATUS!", status);
+
+ return;
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+BOOLEAN
+PLxEvtProgramWriteDma(
+ IN WDFDMATRANSACTION Transaction,
+ IN WDFDEVICE Device,
+ IN PVOID Context,
+ IN WDF_DMA_DIRECTION Direction,
+ IN PSCATTER_GATHER_LIST SgList
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ PDEVICE_EXTENSION devExt;
+ size_t offset;
+ PDMA_TRANSFER_ELEMENT dteVA;
+ ULONG_PTR dteLA;
+ BOOLEAN errors;
+ ULONG i;
+
+ UNREFERENCED_PARAMETER( Context );
+ UNREFERENCED_PARAMETER( Direction );
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ "--> PLxEvtProgramWriteDma");
+
+ //
+ // Initialize locals
+ //
+ devExt = PLxGetDeviceContext(Device);
+ errors = FALSE;
+
+ //
+ // Get the number of bytes as the offset to the beginning of this
+ // Dma operations transfer location in the buffer.
+ //
+ offset = WdfDmaTransactionGetBytesTransferred(Transaction);
+
+ //
+ // Setup the pointer to the next DMA_TRANSFER_ELEMENT
+ // for both virtual and physical address references.
+ //
+ dteVA = (PDMA_TRANSFER_ELEMENT) devExt->WriteCommonBufferBase;
+ dteLA = (devExt->WriteCommonBufferBaseLA.LowPart +
+ sizeof(DMA_TRANSFER_ELEMENT));
+
+ //
+ // Translate the System's SCATTER_GATHER_LIST elements
+ // into the device's DMA_TRANSFER_ELEMENT elements.
+ //
+ for (i=0; i < SgList->NumberOfElements; i++) {
+
+ //
+ // Construct this DTE.
+ //
+ // NOTE: The LocalAddress is the offset into the SRAM from
+ // where this Write will start.
+ //
+ dteVA->PciAddressLow = SgList->Elements[i].Address.LowPart;
+ dteVA->PciAddressHigh = SgList->Elements[i].Address.HighPart;
+ dteVA->TransferSize = SgList->Elements[i].Length;
+
+ dteVA->LocalAddress = (ULONG) offset;
+
+ dteVA->DescPtr.DescLocation = DESC_PTR_DESC_LOCATION__PCI;
+ dteVA->DescPtr.TermCountInt = FALSE;
+ dteVA->DescPtr.LastElement = FALSE;
+ dteVA->DescPtr.DirOfTransfer = DESC_PTR_DIRECTION__TO_DEVICE;
+ dteVA->DescPtr.Address = DESC_PTR_ADDR( dteLA );
+
+ //
+ // Increment the DmaTransaction length by this element length
+ //
+ offset += SgList->Elements[i].Length;
+
+ //
+ // If at end of SgList, then set LastElement bit in final NTE.
+ //
+ if (i == SgList->NumberOfElements - 1) {
+
+ dteVA->DescPtr.LastElement = TRUE;
+
+#if 0 // set to 1 for recording the details
+ TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ "\tDTE[%d] : Addr #%X%08X Len %5d, Local %08X, "
+ "Loc(%d), Last(%d), TermInt(%d), ToPci(%d)\n",
+ i,
+ dteVA->PciAddressHigh,
+ dteVA->PciAddressLow,
+ dteVA->TransferSize,
+ dteVA->LocalAddress,
+ dteVA->DescPtr.DescLocation,
+ dteVA->DescPtr.LastElement,
+ dteVA->DescPtr.TermCountInt,
+ dteVA->DescPtr.DirOfTransfer );
+#endif
+ break;
+ }
+
+#if 0 // set to 1 for recording the details
+ TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ "\tDTE[%d] : Addr #%X%08X Len %5d, Local %08X, "
+ "Loc(%d), Last(%d), TermInt(%d), ToPci(%d)\n",
+ i,
+ dteVA->PciAddressHigh,
+ dteVA->PciAddressLow,
+ dteVA->TransferSize,
+ dteVA->LocalAddress,
+ dteVA->DescPtr.DescLocation,
+ dteVA->DescPtr.LastElement,
+ dteVA->DescPtr.TermCountInt,
+ dteVA->DescPtr.DirOfTransfer );
+#endif
+
+ //
+ // Adjust the next DMA_TRANSFER_ELEMEMT
+ //
+ dteVA++;
+ dteLA += sizeof(DMA_TRANSFER_ELEMENT);
+ }
+
+ //
+ // Start the DMA operation.
+ // Acquire this device's InterruptSpinLock.
+ //
+ WdfInterruptAcquireLock( devExt->Interrupt );
+
+ //
+ // DMA 0 Mode Register - (DMAMODE0)
+ // Enable Scatter/Gather Mode, Interrupt On Done,
+ // and route Ints to PCI.
+ //
+ {
+ union {
+ DMA_MODE bits;
+ ULONG ulong;
+ } dmaMode;
+
+ dmaMode.ulong =
+ READ_REGISTER_ULONG( (PULONG) &devExt->Regs->Dma0_Mode );
+
+ dmaMode.bits.SgModeEnable = TRUE;
+ dmaMode.bits.DoneIntEnable = TRUE;
+ dmaMode.bits.IntToPci = TRUE;
+
+ WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Dma0_Mode,
+ dmaMode.ulong );
+ }
+
+ //
+ // Interrupt CSR Register - (INTCSR)
+ // Enable PCI Ints and DMA Channel 0 Ints.
+ //
+ {
+ union {
+ INT_CSR bits;
+ ULONG ulong;
+ } intCSR;
+
+ intCSR.ulong =
+ READ_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr );
+
+ intCSR.bits.PciIntEnable = TRUE;
+ intCSR.bits.DmaChan0IntEnable = TRUE;
+
+ WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr,
+ intCSR.ulong );
+ }
+
+ //
+ // DMA 0 Descriptor Pointer Register - (DMADPR0)
+ // Write the base LOGICAL address of the DMA_TRANSFER_ELEMENT list.
+ //
+ {
+ union {
+ DESC_PTR bits;
+ ULONG ulong;
+ } ptr;
+
+ ptr.bits.DescLocation = DESC_PTR_DESC_LOCATION__PCI;
+ ptr.bits.TermCountInt = TRUE;
+ ptr.bits.Address =
+ DESC_PTR_ADDR( devExt->WriteCommonBufferBaseLA.LowPart );
+
+ WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Dma0_Desc_Ptr,
+ ptr.ulong );
+ }
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ " PLxEvtProgramWriteDma: Start a Write DMA operation");
+
+ //
+ // DMA 0 CSR Register - (DMACSR0)
+ // Start the DMA operation: Set Enable and Start bits.
+ //
+ {
+ union {
+ DMA_CSR bits;
+ UCHAR uchar;
+ } dmaCSR;
+
+ dmaCSR.uchar =
+ READ_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma0_Csr );
+
+ dmaCSR.bits.Enable = TRUE;
+ dmaCSR.bits.Start = TRUE;
+
+ WRITE_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma0_Csr,
+ dmaCSR.uchar );
+ }
+
+ //
+ // Release our interrupt spinlock
+ //
+ WdfInterruptReleaseLock( devExt->Interrupt );
+
+ //
+ // NOTE: This shows how to process errors which occur in the
+ // PFN_WDF_PROGRAM_DMA function in general.
+ // Basically the DmaTransaction must be deleted and
+ // the Request must be completed.
+ //
+ if (errors) {
+ //
+ // Must abort the transaction before deleting it.
+ //
+ NTSTATUS status;
+
+ (VOID) WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &status);
+ ASSERT(NT_SUCCESS(status));
+ PLxWriteRequestComplete( Transaction, STATUS_INVALID_DEVICE_STATE );
+ TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE,
+ "<-- PLxEvtProgramWriteDma: error ****");
+ return FALSE;
+ }
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE,
+ "<-- PLxEvtProgramWriteDma");
+
+ return TRUE;
+}
+
+
+VOID
+PLxWriteRequestComplete(
+ IN WDFDMATRANSACTION DmaTransaction,
+ IN NTSTATUS Status
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ WDFREQUEST request;
+ size_t bytesTransferred;
+
+ //
+ // Initialize locals
+ //
+
+#ifdef ASSOC_WRITE_REQUEST_WITH_DMA_TRANSACTION
+
+ request = WdfDmaTransactionGetRequest(DmaTransaction);
+
+#else
+ //
+ // If CreateDirect was used then there will be no assoc. Request.
+ //
+ {
+ PTRANSACTION_CONTEXT transContext = PLxGetTransactionContext(DmaTransaction);
+
+ request = transContext->Request;
+ transContext->Request = NULL;
+
+ }
+#endif
+
+ //
+ // Get the final bytes transferred count.
+ //
+ bytesTransferred = WdfDmaTransactionGetBytesTransferred( DmaTransaction );
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC,
+ "PLxWriteRequestComplete: Request %p, Status %!STATUS!, "
+ "bytes transferred %d\n",
+ request, Status, (int) bytesTransferred );
+
+ WdfDmaTransactionRelease(DmaTransaction);
+
+ WdfRequestCompleteWithInformation( request, Status, bytesTransferred);
+
+}
+