diff options
Diffstat (limited to 'general/PLX9x5x/sys/Read.c')
| -rw-r--r-- | general/PLX9x5x/sys/Read.c | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/general/PLX9x5x/sys/Read.c b/general/PLX9x5x/sys/Read.c new file mode 100644 index 00000000..319cf094 --- /dev/null +++ b/general/PLX9x5x/sys/Read.c @@ -0,0 +1,453 @@ +/*++ + +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: + + Read.c + +Abstract: + + +Environment: + + Kernel mode + +--*/ + +#include "precomp.h" + +#include "Read.tmh" + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +VOID +PLxEvtIoRead( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ + +Routine Description: + + Called by the framework as soon as it receives a read 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 - Default queue handle + Request - Handle to the write request + Lenght - Length of the data buffer associated with the request. + 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; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + "--> PLxEvtIoRead: Request %p", Request); + + // + // Get the DevExt from the Queue handle + // + devExt = PLxGetDeviceContext(WdfIoQueueGetDevice(Queue)); + + do { + // + // Validate the Length parameter. + // + if (Length > PCI9656_SRAM_SIZE) { + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + // + // Initialize this new DmaTransaction. + // + status = WdfDmaTransactionInitializeUsingRequest( + devExt->ReadDmaTransaction, + Request, + PLxEvtProgramReadDma, + WdfDmaDirectionReadFromDevice ); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, + "WdfDmaTransactionInitializeUsingRequest " + "failed: %!STATUS!", status); + break; + } + +#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_READ, + // "Setting a new MaxLen %d\n", length); + + WdfDmaTransactionSetMaximumLength( devExt->ReadDmaTransaction, + length ); + } +#endif + + // + // Execute this DmaTransaction. + // + status = WdfDmaTransactionExecute( devExt->ReadDmaTransaction, + WDF_NO_CONTEXT); + + if(!NT_SUCCESS(status)) { + // + // Couldn't execute this DmaTransaction, so fail Request. + // + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, + "WdfDmaTransactionExecute failed: %!STATUS!", status); + break; + } + + // + // 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; + + } while (0); + + // + // If there are errors, then clean up and complete the Request. + // + if (!NT_SUCCESS(status )) { + WdfDmaTransactionRelease(devExt->ReadDmaTransaction); + WdfRequestComplete(Request, status); + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + "<-- PLxEvtIoRead: status %!STATUS!", status); + + return; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +BOOLEAN +PLxEvtProgramReadDma( + IN WDFDMATRANSACTION Transaction, + IN WDFDEVICE Device, + IN WDFCONTEXT Context, + IN WDF_DMA_DIRECTION Direction, + IN PSCATTER_GATHER_LIST SgList + ) +/*++ + +Routine Description: + + The framework calls a driver's EvtProgramDma event callback function + when the driver calls WdfDmaTransactionExecute and the system has + enough map registers to do the transfer. The callback function must + program the hardware to start the transfer. A single transaction + initiated by calling WdfDmaTransactionExecute may result in multiple + calls to this function if the buffer is too large and there aren't + enough map registers to do the whole transfer. + + +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_READ, + "--> PLxEvtProgramReadDma"); + + // + // 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->ReadCommonBufferBase; + dteLA = (devExt->ReadCommonBufferBaseLA.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 Read 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__FROM_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; + + //TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + // "\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 ); + break; + } + + //TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + // "\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 ); + + // + // 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 1 Mode Register - (DMAMODE1) + // 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->Dma1_Mode ); + + dmaMode.bits.SgModeEnable = TRUE; + dmaMode.bits.DoneIntEnable = TRUE; + dmaMode.bits.IntToPci = TRUE; + + dmaMode.bits.ClearCountMode = TRUE; + + WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Dma1_Mode, + dmaMode.ulong ); + } + + // + // Interrupt CSR Register - (INTCSR) + // Enable PCI Ints and DMA Channel 1 Ints. + // + { + union { + INT_CSR bits; + ULONG ulong; + } intCSR; + + intCSR.ulong = + READ_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr ); + + intCSR.bits.PciIntEnable = TRUE; + intCSR.bits.DmaChan1IntEnable = TRUE; + + WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr, + intCSR.ulong ); + } + + // + // DMA 1 Descriptor Pointer Register - (DMADPR1) + // 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->ReadCommonBufferBaseLA.LowPart ); + + WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Dma1_Desc_Ptr, + ptr.ulong ); + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + " PLxEvtProgramReadDma: Start a Read DMA operation"); + + // + // DMA 1 CSR Register - (DMACSR1) + // Start the DMA operation: Set Enable and Start bits. + // + { + union { + DMA_CSR bits; + UCHAR uchar; + } dmaCSR; + + dmaCSR.uchar = + READ_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma1_Csr ); + + dmaCSR.bits.Enable = TRUE; + dmaCSR.bits.Start = TRUE; + + WRITE_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma1_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) { + NTSTATUS status; + + // + // Must abort the transaction before deleting. + // + (VOID) WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &status); + ASSERT(NT_SUCCESS(status)); + + PLxReadRequestComplete( Transaction, STATUS_INVALID_DEVICE_STATE ); + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, + "<-- PLxEvtProgramReadDma: errors ****"); + return FALSE; + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + "<-- PLxEvtProgramReadDma"); + + return TRUE; +} + +VOID +PLxReadRequestComplete( + IN WDFDMATRANSACTION DmaTransaction, + IN NTSTATUS Status + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + WDFREQUEST request; + size_t bytesTransferred; + + // + // Get the associated request from the transaction. + // + request = WdfDmaTransactionGetRequest(DmaTransaction); + + ASSERT(request); + + // + // Get the final bytes transferred count. + // + bytesTransferred = WdfDmaTransactionGetBytesTransferred( DmaTransaction ); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, + "PLxReadRequestComplete: Request %p, Status %!STATUS!, " + "bytes transferred %d\n", + request, Status, (int) bytesTransferred ); + + WdfDmaTransactionRelease(DmaTransaction); + + // + // Complete this Request. + // + WdfRequestCompleteWithInformation( request, Status, bytesTransferred); + +} + |
