NTSTATUS AmccPciEvtDeviceReleaseHardware ( _In_ WDFDEVICE hDevice, _In_ WDFCMRESLIST ResourcesTranslated ) /*++ Routine Description: EvtDeviceReleaseHardware is called by the framework whenever the PnP manager is revoking ownership of our resources. This may be in response to either IRP_MN_STOP_DEVICE or IRP_MN_REMOVE_DEVICE. The callback is made before passing down the IRP to the lower driver. In this callback, do anything necessary to free those resources. Arguments: Device - Handle to a framework device object. ResourcesTranslated - Handle to a collection of framework resource objects. This collection identifies the translated (system-physical) hardware resources that have been assigned to the device. The resources appear from the CPU's point of view. Use this list of resources to map I/O space and device-accessible memory into virtual address space Return Value: VOID --*/ { PAMCC_DEVICE_EXTENSION devExt = NULL; UNREFERENCED_PARAMETER(ResourcesTranslated); PAGED_CODE(); devExt = AmccPciGetDevExt(hDevice); // // Unmap the IO resource // if (devExt->PortBase) { if (devExt->PortMapped) { MmUnmapIoSpace( devExt->PortBase, devExt->PortCount ); } devExt->PortBase = NULL; } return STATUS_SUCCESS; }
NTSTATUS AmccPciAddDevice( _In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit ) /*++ Routine Description: EvtDeviceAdd is called by the framework in response to AddDevice call from the PnP manager. It is responsible for initializing and creating a WDFDEVICE object. Any work that should be done after the object is created should be deferred until EvtDeviceSoftwareInit, as that callback will be made with the device lock held (if there is one.) Arguments: Driver - Handle to a framework driver object created in DriverEntry DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; WDF_OBJECT_ATTRIBUTES fdoAttributes; WDF_INTERRUPT_CONFIG interruptConfig; WDF_OBJECT_ATTRIBUTES interruptAttributes; WDF_IO_QUEUE_CONFIG ioQueueConfig; PAMCC_DEVICE_EXTENSION devExt; WDFQUEUE hQueue; WDFDEVICE device; PAGED_CODE(); TraceEvents(TRACE_LEVEL_INFORMATION, AMCC_TRACE_INIT, "AmccPciAddDevice: 0x%p", Driver); // // Zero out the PnpPowerCallbacks structure. // WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); // // Set Callbacks for any of the functions we are interested in. // If no callback is set, Framework will take the default action // by itself. // pnpPowerCallbacks.EvtDevicePrepareHardware = AmccPciEvtDevicePrepareHardware; pnpPowerCallbacks.EvtDeviceReleaseHardware = AmccPciEvtDeviceReleaseHardware; pnpPowerCallbacks.EvtDeviceD0Entry = AmccPciEvtDeviceD0Entry; pnpPowerCallbacks.EvtDeviceD0Exit = AmccPciEvtDeviceD0Exit; // // Register the PnP Callbacks.. // WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); // // Set various attributes for this device // WdfDeviceInitSetIoType( DeviceInit, WdfDeviceIoDirect ); WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, AMCC_DEVICE_EXTENSION); fdoAttributes.EvtCleanupCallback = AmccPciContextCleanup; // // We want all the queue callbacks, cancel routine, and DpcForIsr to be serialized // at the device level, so we don't have worry about any synchronization issues. // fdoAttributes.SynchronizationScope = WdfSynchronizationScopeDevice; status = WdfDeviceCreate( &DeviceInit, &fdoAttributes, &device ); if ( !NT_SUCCESS(status) ) { TraceEvents(TRACE_LEVEL_ERROR, AMCC_TRACE_INIT, "WdfDeviceInitialize failed 0x%X", status); return status; } // // Device Initialization is complete. // Get the Device Extension and initialize it. // devExt = AmccPciGetDevExt(device); devExt->Device = device; TraceEvents(TRACE_LEVEL_INFORMATION, AMCC_TRACE_INIT, "PDO 0x%p, FDO 0x%p, DevExt 0x%p", WdfDeviceWdmGetPhysicalDevice(device), WdfDeviceWdmGetDeviceObject( device ), devExt); // // This device generates an interrupt. So create an interrupt object which // will later be associated with the devices resources and connected // by the Framework. // WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&interruptAttributes, INTERRUPT_DATA); // // Configure the Interrupt object // WDF_INTERRUPT_CONFIG_INIT(&interruptConfig, AmccPciEvtInterruptIsr, AmccPciEvtInterruptDpc); status = WdfInterruptCreate(device, &interruptConfig, &interruptAttributes, &devExt->WdfInterrupt); if (!NT_SUCCESS (status)) { TraceEvents(TRACE_LEVEL_ERROR, AMCC_TRACE_INIT, "WdfInterruptCreate failed: %!STATUS!\n", status); return status; } // // The S5933 requires DMA buffers be aligned on a 32-bit boundary // // NOTE: Read the existing alignment value. If it is greater than // or equal then keep it. If it is less then update the // alignment requirement field with this device's required // value. // // NOTE: See the MSDN section titled "Initializing a Device Object" // for details on how to specify this alignment value. // // NOTE: AMCC5933_ALIGNMENT__32BITS is equated to (4-1) for 32-bit // alignment. // { ULONG alignReq; alignReq = WdfDeviceGetAlignmentRequirement( device ); if (alignReq < AMCC5933_ALIGNMENT__32BITS) { // // Set the S5933 alignment requirement as new value. // WdfDeviceSetAlignmentRequirement( device, AMCC5933_ALIGNMENT__32BITS); } } // // Register I/O callbacks. // // Create a sequential IO Queue for serial operation. That means all the requests (Read/Write // & IOCTL) are serialized to the device. Until the driver completes the request presented to it, // the framework will not schedule another one. The requests held in the framework will be // cancelled automatically if the source of request (application) terminate or cancels it. // WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( &ioQueueConfig, WdfIoQueueDispatchSequential); ioQueueConfig.EvtIoDefault = AmccPciEvtIoDefault; // // By default, Static Driver Verifier (SDV) displays a warning if it // doesn't find the EvtIoStop callback on a power-managed queue. // The 'assume' below causes SDV to suppress this warning. If the driver // has not explicitly set PowerManaged to WdfFalse, the framework creates // power-managed queues when the device is not a filter driver. Normally // the EvtIoStop is required for power-managed queues, but for this driver // it is not needed b/c the driver doesn't hold on to the requests for // long time or forward them to other drivers. // If the EvtIoStop callback is not implemented, the framework waits for // all driver-owned requests to be done before moving in the Dx/sleep // states or before removing the device, which is the correct behavior // for this type of driver. If the requests were taking an indeterminate // amount of time to complete, or if the driver forwarded the requests // to a lower driver/another stack, the queue should have an // EvtIoStop/EvtIoResume. // __analysis_assume(ioQueueConfig.EvtIoStop != 0); status = WdfIoQueueCreate( device, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &hQueue ); __analysis_assume(ioQueueConfig.EvtIoStop == 0); if (!NT_SUCCESS (status)) { // // We don't have worry about deleting the device here because framework will automatically // cleanup that when the driver unloads. // TraceEvents(TRACE_LEVEL_ERROR, AMCC_TRACE_INIT, "WdfIoQueueCreate failed %!STATUS!", status); return status; } // // Register an interface so that application can find and talk to us. // NOTE: See the note in Public.h concerning this GUID value. // status = WdfDeviceCreateDeviceInterface( device, (LPGUID) &GUID_DEVINTERFACE_AMCC_PCI, NULL ); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, AMCC_TRACE_INIT, "<-- AMCCAddDevice: WdfDeviceCreateDeviceInterface failed %!STATUS!", status); return status; } devExt->MaximumTransferLength = MAXIMUM_REQUEST_CONTEXT_LENGTH; // // Set the maximum physical pages for now, but this value may change if // there aren't enough map registers // devExt->MaximumPhysicalPages = MAXIMUM_PHYSICAL_PAGES; return status; }
NTSTATUS AmccPciEvtDevicePrepareHardware( _In_ WDFDEVICE Device, _In_ WDFCMRESLIST Resources, _In_ WDFCMRESLIST ResourcesTranslated ) /*++ Routine Description: EvtDevicePrepareHardware event callback performs operations that are necessary to use the device's control registers. Arguments: Device - Handle to a framework device object. Resources - Handle to a collection of framework resource objects. This collection identifies the raw (bus-relative) hardware resources that have been assigned to the device. ResourcesTranslated - Handle to a collection of framework resource objects. This collection identifies the translated (system-physical) hardware resources that have been assigned to the device. The resources appear from the CPU's point of view. Use this list of resources to map I/O space and device-accessible memory into virtual address space Return Value: WDF status code. Let us not worry about cleaning up the resources here if we fail start, because the PNP manager will send a remove-request and we will free all the allocated resources in AmccPciEvtDeviceReleaseHardware. --*/ { ULONG i; NTSTATUS status = STATUS_SUCCESS; PAMCC_DEVICE_EXTENSION devExt = NULL; BOOLEAN foundPort = FALSE; PHYSICAL_ADDRESS portBasePA = {0}; ULONG portCount = 0; WDF_DMA_ENABLER_CONFIG dmaConfig; PCM_PARTIAL_RESOURCE_DESCRIPTOR desc; PAGED_CODE(); // // The Resources collection is not used for PCI devices, since the PCI // bus driver manages the device's PCI Base Address Registers. // UNREFERENCED_PARAMETER( Resources ); devExt = AmccPciGetDevExt(Device); // // Parse the resource list and save the resource information. // for (i=0; i < WdfCmResourceListGetCount(ResourcesTranslated); i++) { desc = WdfCmResourceListGetDescriptor(ResourcesTranslated, i); if(!desc) { TraceEvents(TRACE_LEVEL_ERROR, AMCC_TRACE_INIT, "WdfResourceCmGetDescriptor failed"); return STATUS_DEVICE_CONFIGURATION_ERROR; } switch (desc->Type) { case CmResourceTypePort: portBasePA = desc->u.Port.Start; portCount = desc->u.Port.Length; devExt->PortMapped = (desc->Flags & CM_RESOURCE_PORT_IO) ? FALSE : TRUE; foundPort = TRUE; // // Map in the single IO Space resource. // if (devExt->PortMapped) { devExt->PortBase = (PUCHAR) MmMapIoSpace( portBasePA, portCount, MmNonCached ); if (!devExt->PortBase) { TraceEvents(TRACE_LEVEL_ERROR, AMCC_TRACE_INIT, "Unable to map port range 0x%p, length %d", devExt->PortBase, devExt->PortCount); return STATUS_INSUFFICIENT_RESOURCES; } devExt->PortCount = portCount; } else { devExt->PortBase = (PUCHAR)(ULONG_PTR) portBasePA.QuadPart; devExt->PortCount = portCount; } devExt->Regs = (PREG5933) devExt->PortBase; break; default: TraceEvents(TRACE_LEVEL_WARNING, AMCC_TRACE_INIT, "Unknown resource type %d", desc->Type); break; } } if (!foundPort) { TraceEvents(TRACE_LEVEL_ERROR, AMCC_TRACE_INIT, "Missing hardware resources"); return STATUS_DEVICE_CONFIGURATION_ERROR; } // // Configure the DMA object // WDF_DMA_ENABLER_CONFIG_INIT( &dmaConfig, WdfDmaProfilePacket, devExt->MaximumTransferLength ); status = WdfDmaEnablerCreate( devExt->Device, &dmaConfig, WDF_NO_OBJECT_ATTRIBUTES, &devExt->DmaEnabler ); if (!NT_SUCCESS (status)) { TraceEvents(TRACE_LEVEL_ERROR, AMCC_TRACE_INIT, "WdfDmaEnablerCreate failed %08X", status); return status; } return STATUS_SUCCESS; }
BOOLEAN AmccPciProgramDma( __in WDFDMATRANSACTION Transaction, __in WDFDEVICE Device, __in PVOID Context, __in WDF_DMA_DIRECTION Direction, __in PSCATTER_GATHER_LIST SgList ) /*++ Routine Description: Arguments: Return Value: --*/ { AMCC_DEVICE_EXTENSION * devExt; ULONG address; ULONG length; union { ULONG ulong; MCSR_REG bits; } mcsr; union { ULONG ulong; INTCSR_REG bits; } intcsr; UNREFERENCED_PARAMETER( Transaction ); UNREFERENCED_PARAMETER( Context ); // // Reestablish working parameters. // devExt = AmccPciGetDevExt(Device); // // The S5933 used only 32-bit packet mode DMA operations. // ASSERT(SgList->NumberOfElements == 1); ASSERT(SgList->Elements[0].Address.HighPart == 0); // // Only the first Scatter/Gather element is relevant for packet mode. // S5933 only does 32-bit DMA transfer operations: only low part of // physical address is usable. // address = SgList->Elements[0].Address.LowPart; length = SgList->Elements[0].Length; TraceEvents(TRACE_LEVEL_INFORMATION, AMCC_TRACE_IO, "Address 0x%08X, Length %d", address, length); // // Read the Master Control/Status Register (MCSR) and // the Interrupt Control/Status Register (INTCSR). // mcsr.ulong = READ_PORT_ULONG((PULONG) &(devExt->Regs->MCSR)); intcsr.ulong = READ_PORT_ULONG((PULONG) &devExt->Regs->INTCSR); // // Setup read or write transfer registers. // // NOTE: The S5933 calls a transfer from memory to the device a "read". // if (Direction == WdfDmaDirectionWriteToDevice) { mcsr.bits.ReadFifoMgmtScheme = TRUE; mcsr.bits.ReadTransferEnable = TRUE; intcsr.bits.IntOnReadTransferComplete = TRUE; WRITE_PORT_ULONG((PULONG) &devExt->Regs->MRTC, length); WRITE_PORT_ULONG((PULONG) &devExt->Regs->MRAR, address); } else { mcsr.bits.WriteFifoMgmtScheme = TRUE; mcsr.bits.WriteTransferEnable = TRUE; intcsr.bits.IntOnWriteTransferComplete = TRUE; WRITE_PORT_ULONG((PULONG) &devExt->Regs->MWTC, length); WRITE_PORT_ULONG((PULONG) &devExt->Regs->MWAR, address); } // // Write modified INTCSR to enable the appropriate interrupt and // the MCSR to actually start the transfer. // WRITE_PORT_ULONG((PULONG) &devExt->Regs->INTCSR, intcsr.ulong); WRITE_PORT_ULONG((PULONG) &devExt->Regs->MCSR, mcsr.ulong); return TRUE; }
VOID AmccPciEvtInterruptDpc( __in WDFINTERRUPT WdfInterrupt, __in WDFOBJECT WdfDevice ) /*++ Routine Description: DPC callback for ISR. Arguments: WdfInterrupt - Handle to the framework interrupt object WdfDevice - Associated device object. Return Value: --*/ { PAMCC_DEVICE_EXTENSION devExt; WDFREQUEST request; REQUEST_CONTEXT * transfer; NTSTATUS status; size_t transferred; BOOLEAN transactionComplete; UNREFERENCED_PARAMETER( WdfInterrupt ); devExt = AmccPciGetDevExt(WdfDevice); // // Retreive request and transfer. // request = devExt->CurrentRequest; transfer = GetRequestContext(request); // // Check to see if the request is cancelled by the system. While // we are DMAing a large buffer into multiple transaction, // there is good possibilty for the request to get cancelled because // the originator of the request exited or cancelled the I/O explicitly. // if(WdfRequestIsCanceled(request)) { TraceEvents(TRACE_LEVEL_ERROR, AMCC_TRACE_IO, "Aborted DMA transaction 0x%p", request); WdfObjectDelete( transfer->DmaTransaction ); devExt->CurrentRequest = NULL; WdfRequestComplete(request, STATUS_CANCELLED); return; } // // The boolean transactionComplete indicates whether the transaction has // exited the transfer state, e.g. no further transfers are scheduled. // // If transactionComplete == FALSE, then the next DMA transfer has been // scheduled, e.g. the next interrrupt will drive the ISR again. // // If transactionComplete == TRUE, then status indicates the reason; // SUCCESS is the nomative case, while non-SUCCESS indicates the // DMA transaction failed for "status" reason. // transactionComplete = WdfDmaTransactionDmaCompleted( transfer->DmaTransaction, &status ); if (transactionComplete) { ASSERT(status != STATUS_MORE_PROCESSING_REQUIRED); // // No more data: request is complete // TraceEvents(TRACE_LEVEL_INFORMATION, AMCC_TRACE_IO, "Request %p completed: status %X", request, status); // // Get the final bytes transferred count. // transferred = WdfDmaTransactionGetBytesTransferred( transfer->DmaTransaction ); TraceEvents(TRACE_LEVEL_INFORMATION, AMCC_TRACE_IO, "Bytes transfered %d", (int) transferred ); // // Delete this DmaTransaction transaction. // WdfObjectDelete( transfer->DmaTransaction ); // // Clean-up for this request. // devExt->CurrentRequest = NULL; // // Complete this IO request. // WdfRequestCompleteWithInformation( request, status, (NT_SUCCESS(status)) ? transferred : 0 ); } }
VOID AmccPciEvtIoDefault( __in WDFQUEUE Queue, __in WDFREQUEST Request ) /*++ Routine Description: Start the IRP on the device. This driver allows only one I/O to be active on the adapter at any one time. If multiple I/Os are sent to the driver, they will be queued and completed as they complete on the adapter (one IRP per interrupt). Arguments: Queue - Default queue handle Request - Handle to the write request Parameters - Contains current stack location information from the IRP Return Value: None --*/ { PAMCC_DEVICE_EXTENSION devExt; REQUEST_CONTEXT * transfer; NTSTATUS status; size_t length; WDF_DMA_DIRECTION direction; WDFDMATRANSACTION dmaTransaction; WDF_REQUEST_PARAMETERS params; WDF_REQUEST_PARAMETERS_INIT(¶ms); WdfRequestGetParameters( Request, ¶ms ); // // Get the device extension. // devExt = AmccPciGetDevExt(WdfIoQueueGetDevice( Queue )); // // Validate and gather parameters. // switch (params.Type) { case WdfRequestTypeRead: length = params.Parameters.Read.Length; direction = WdfDmaDirectionReadFromDevice; break; case WdfRequestTypeWrite: length = params.Parameters.Write.Length; direction = WdfDmaDirectionWriteToDevice; break; default: TraceEvents(TRACE_LEVEL_WARNING, AMCC_TRACE_IO, "Request type not Read or Write\n"); WdfRequestComplete(Request, STATUS_INVALID_PARAMETER); return; } TraceEvents(TRACE_LEVEL_INFORMATION, AMCC_TRACE_IO, "Request %p: %s %d bytes", Request, (direction)?"Write":"Read", (ULONG)length); // // The length must be non-zero. // if (length == 0) { TraceEvents(TRACE_LEVEL_WARNING, AMCC_TRACE_IO, "Zero transfer length input to read/write"); WdfRequestComplete(Request, STATUS_INVALID_PARAMETER); return; } transfer = GetRequestContext(Request); // // Create new DmaRequst to conduct this DMA transaction. // status = WdfDmaTransactionCreate( devExt->DmaEnabler, WDF_NO_OBJECT_ATTRIBUTES, &dmaTransaction ); if(!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, AMCC_TRACE_IO, "WdfDmaRequestCreate failed: %X", status); WdfRequestComplete(Request, status); return; } // // Create new DmaTransaction. // status = WdfDmaTransactionInitializeUsingRequest( dmaTransaction, Request, AmccPciProgramDma, direction ); if(!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, AMCC_TRACE_IO, "WdfDmaRequestInitializeWithRequest failed: %X", status); WdfObjectDelete(dmaTransaction); WdfRequestComplete(Request, status); return; } // // Fill transfer context structure // transfer->Request = Request; transfer->DmaTransaction = dmaTransaction; // // Save the current Request as the "in-progress" request. // devExt->CurrentRequest = Request; // // Execute this dmaTransaction transaction. // status = WdfDmaTransactionExecute( dmaTransaction, WDF_NO_CONTEXT); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, AMCC_TRACE_IO, "WdfDmaTransactionExecute failed: %X", status); WdfObjectDelete(dmaTransaction); WdfRequestComplete(Request, status); return; } return; }
BOOLEAN AmccPciEvtInterruptIsr( __in WDFINTERRUPT Interrupt, __in ULONG MessageID ) /*++ Routine Description: This routine assumes that only a single I/O can be completed at a time on the hardware (i.e. at most one I/O completed per interrupt). Arguments: Interupt - Address of the framework interrupt object MessageID - Return Value: TRUE - Interrupt belongs to this device. --*/ { PAMCC_DEVICE_EXTENSION devExt = NULL; WDFDEVICE hDevice; union { ULONG ulong; INTCSR_REG bits; } intcsr; union { ULONG ulong; MCSR_REG bits; } mcsr; UNREFERENCED_PARAMETER( MessageID ); hDevice = WdfInterruptGetDevice(Interrupt); devExt = AmccPciGetDevExt(hDevice); // // Read interrupt control/status register and see if an interrupt is pending. // If not, return FALSE immediately. // intcsr.ulong = READ_PORT_ULONG((PULONG) &devExt->Regs->INTCSR); if (!intcsr.bits.InterruptAsserted) { return FALSE; } // // Disable bus-mastering // mcsr.ulong = READ_PORT_ULONG((PULONG) &devExt->Regs->MCSR); mcsr.bits.WriteTransferEnable = FALSE; mcsr.bits.ReadTransferEnable = FALSE; WRITE_PORT_ULONG((PULONG) &devExt->Regs->MCSR, mcsr.ulong ); // // This will take effect when INTCSR is rewritten later. // intcsr.bits.IntOnWriteTransferComplete = FALSE; intcsr.bits.IntOnReadTransferComplete = FALSE; // // Process pending interrupts. We're expecting an interrupt due // to a transfer count going to zero, but we might be getting a // master or target abort instead. // while (intcsr.bits.InterruptAsserted) { // // Merge new interrupts with old // _InterlockedOr((PLONG) &devExt->Intcsr, (LONG) intcsr.ulong ); // // Interrupt flags on the S5933 are cleared by writing a "1" bit // to them, so clear all the interrupts just examined. // WRITE_PORT_ULONG((PULONG) &devExt->Regs->INTCSR, intcsr.ulong); // // Check for additional interrupts // intcsr.ulong = READ_PORT_ULONG((PULONG) &devExt->Regs->INTCSR); } // // Check if there is a current Request. If not, then this interrupt cannot // do anything. This driver design requires an I/O to be pending in order // to queue the DPC. If there is no I/O current, then there is no need // to have a DPC queued. This driver also assumes one I/O per interrupt. // // IMPORTANT: Before returning TRUE, the interrupt must have been cleared // on the device or the system will hang trying to service this level // sensitive interrupt. // if (!devExt->CurrentRequest) { TraceEvents(TRACE_LEVEL_WARNING, AMCC_TRACE_IO, "Hardware generated interrupt with no request pending"); return TRUE; } // // Request the DPC to complete the transfer. // WdfInterruptQueueDpcForIsr( Interrupt ); // // Indicate that this adapter was interrupting. // return TRUE; }