diff options
| author | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
|---|---|---|
| committer | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
| commit | 97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch) | |
| tree | 46f3701832d70b420eb0fc0eb93261f9da45db3f /general/PLX9x5x/sys/Write.c | |
| parent | ef1905bf1e8825bb31120dfb27e0daf3154d859a (diff) | |
Initial publish
Diffstat (limited to 'general/PLX9x5x/sys/Write.c')
| -rw-r--r-- | general/PLX9x5x/sys/Write.c | 525 |
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); + +} + |
