/*++ Copyright (c) 1991 - 2001 Microsoft Corporation Module Name: ### ## # ## ## # ## ## ##### ### ## ## #### ##### ##### ## # ## ### ## ### # ## ## ## ## ### ### ### ## # ## ## ## ## ### ## ### ## #### # ## ## ## ## ## ## ######## ## ## ## ## ## ### ## # # ## # #### #### ##### ## ## # ### ## ## ## ## ## ## ### ### ### # ### #### #### ####### # # ## ## ##### ##### # ## ### ### # ## ## ## ## ## ## # ## ## ## # ## ## ### ## ## # # ## ## ## ## ## # ## ## #### ## ## Abstract: This module contains the entire implementation of the NVRAM miniport for the ServerWorks CSB5 server chip set. Author: Wesley Witt (wesw) 1-Oct-2001 Environment: Kernel mode only. Notes: --*/ #include "swnvram.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,DriverEntry) #endif #define CLEARBITS(_val,_mask) ((_val) &= ~(_mask)) #define SETBITS(_val,_mask) ((_val) |= (_mask)) BOOLEAN SaNvramStartNextIo( IN PDEVICE_EXTENSION DeviceExtension ) /*++ Routine Description: This function is called by the ISR and the StartIo functions. The purpose is to modify the device's hardware registers such that the next I/O is started on the device. Arguments: DeviceExtension - A pointer to the mini-port's device extension Context: IRQL: DISPATCH_LEVEL or DIRQL, arbitrary thread context Return Value: A value of TRUE is returned if the I/O is started successfully. Otherwise a value of FALSE is returned. --*/ { USHORT Control; // // This function executes at DIRQL and it synchronized // with the ISR. // // // Read the current value of the NVRAM address register // Control = READ_REGISTER_USHORT( (PUSHORT)DeviceExtension->NvramMemBase ); if (Control & NVRAM_CONTROL_BUSY) { // // This should never happen, but the device us telling // us that it is busy. // return FALSE; } // // Setup the device for the I/O // // // Clear the Function code // CLEARBITS( Control, NVRAM_CONTROL_FUNCTION_CODE ); switch (DeviceExtension->IoFunction) { case IRP_MJ_WRITE: // // Stuff the next dword into the data register // WRITE_REGISTER_ULONG( (PULONG)(DeviceExtension->NvramMemBase+4), DeviceExtension->IoBuffer[DeviceExtension->IoIndex] ); // // Set the bit to indicate a write // SETBITS( Control, NVRAM_CONTROL_FUNCTION_WRITE ); break; case IRP_MJ_READ: // // Set the bit to indicate a read // SETBITS( Control, NVRAM_CONTROL_FUNCTION_READ ); break; default: return FALSE; } // // Clear the I/O address // CLEARBITS( Control, NVRAM_CONTROL_ADDRESS ); // // Set the requested I/O address // Control = Control | (USHORT) (DeviceExtension->IoOffset + DeviceExtension->IoIndex); // // Write the new control value to the address register // WRITE_REGISTER_USHORT( (PUSHORT)DeviceExtension->NvramMemBase, Control ); return TRUE; } BOOLEAN SaNvramInterruptService( IN PKINTERRUPT InterruptObject, IN PVOID ServiceContext ) /*++ Routine Description: This function is the device's interrupt service routine and is called by the OS to service the interrupt. The interrupt spin lock is held so work here is kept to a minimum. Arguments: InterruptObject - Pointer to an interrupt object. DeviceExtension - Pointer to the mini-port's device extension Context: IRQL: DIRQL, arbitrary thread context Return Value: A value of TRUE is returned if the interrupt is serviced by this function. Otherwise a value of FALSE is returned. --*/ { PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) ServiceContext; USHORT Control; // // First thing is to check the DONE bit in the NVRAM address register. // This bit indicates that the device has completed a previous // command request. This tells the ISR that the interrupt was // likely generated by the NVRAM device. // Control = READ_REGISTER_USHORT( (PUSHORT)DeviceExtension->NvramMemBase ); if ((Control & NVRAM_CONTROL_DONE) == 0) { return FALSE; } // // Now the interrupt must be stopped. // This is accomplished by setting the done // bit and clearing the function code. // CLEARBITS( Control, NVRAM_CONTROL_FUNCTION_CODE ); SETBITS( Control, NVRAM_CONTROL_DONE ); WRITE_REGISTER_USHORT( (PUSHORT)DeviceExtension->NvramMemBase, Control ); // // Check to see if this is a bogus interrupt // if (DeviceExtension->IoIndex >= DeviceExtension->IoLength) { return FALSE; } // // Process the I/O // if (DeviceExtension->IoFunction == IRP_MJ_READ) { // // Fetch the double word value from the NVRAM data register // DeviceExtension->IoBuffer[DeviceExtension->IoIndex] = READ_REGISTER_ULONG( (PULONG)(DeviceExtension->NvramMemBase+4) ); } DeviceExtension->IoIndex += 1; DeviceExtension->CompletedIoSize += sizeof(ULONG); if (DeviceExtension->IoIndex >= DeviceExtension->IoLength) { // // We're done so we need to have the DPC start the next I/O // SaPortRequestDpc( DeviceExtension, NULL ); } else { // // More I/O necessary so request the device start the next I/O // if (!SaNvramStartNextIo( DeviceExtension )) { // // Something failed so be sure that the next I/O is started // SaPortRequestDpc( DeviceExtension, NULL ); } } return TRUE; } NTSTATUS SaNvramRead( IN PVOID DeviceExtensionIn, IN PIRP Irp, IN PVOID FsContext, IN LONGLONG StartingOffset, IN PVOID DataBuffer, IN ULONG DataBufferLength ) /*++ Routine Description: This routine processes the read requests for the local display miniport. Arguments: DeviceExtensionIn - Miniport's device extension StartingOffset - Starting offset for the I/O DataBuffer - Pointer to the data buffer DataBufferLength - Length of the data buffer in bytes Return Value: NT status code. --*/ { PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) DeviceExtensionIn; // // Validate the I/O parameters // if (((StartingOffset + DataBufferLength) / sizeof(ULONG)) > MAX_NVRAM_SIZE) { REPORT_ERROR( SA_DEVICE_NVRAM, "Starting offset is too large\n", STATUS_INVALID_PARAMETER_1 ); return STATUS_INVALID_PARAMETER_1; } // // Save the I/O parameters // DeviceExtension->IoBuffer = (PULONG)DataBuffer; DeviceExtension->IoLength = DataBufferLength / sizeof(ULONG); DeviceExtension->IoFunction = IRP_MJ_READ; DeviceExtension->IoOffset = (ULONG) (StartingOffset / sizeof(ULONG)); DeviceExtension->IoIndex = 0; DeviceExtension->CompletedIoSize = 0; // // Start the I/O if (!SaNvramStartNextIo( DeviceExtension )) { REPORT_ERROR( SA_DEVICE_NVRAM, "SaNvramStartNextIo failed\n", STATUS_UNSUCCESSFUL ); return STATUS_UNSUCCESSFUL; } return STATUS_PENDING; } NTSTATUS SaNvramWrite( IN PVOID DeviceExtensionIn, IN PIRP Irp, IN PVOID FsContext, IN LONGLONG StartingOffset, IN PVOID DataBuffer, IN ULONG DataBufferLength ) /*++ Routine Description: This routine processes the write requests for the local display miniport. Arguments: DeviceExtensionIn - Miniport's device extension StartingOffset - Starting offset for the I/O DataBuffer - Pointer to the data buffer DataBufferLength - Length of the data buffer in bytes Return Value: NT status code. --*/ { PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) DeviceExtensionIn; // // Validate the I/O parameters // if (((StartingOffset + DataBufferLength) / sizeof(ULONG)) > MAX_NVRAM_SIZE) { REPORT_ERROR( SA_DEVICE_NVRAM, "Starting offset is too large\n", STATUS_INVALID_PARAMETER_1 ); return STATUS_INVALID_PARAMETER_1; } // // Save the I/O parameters // DeviceExtension->IoBuffer = (PULONG)DataBuffer; DeviceExtension->IoLength = DataBufferLength / sizeof(ULONG); DeviceExtension->IoFunction = IRP_MJ_WRITE; DeviceExtension->IoOffset = (ULONG) (StartingOffset / sizeof(ULONG)); DeviceExtension->IoIndex = 0; DeviceExtension->CompletedIoSize = 0; // // Start the I/O if (!SaNvramStartNextIo( DeviceExtension )) { REPORT_ERROR( SA_DEVICE_NVRAM, "SaNvramStartNextIo failed\n", STATUS_UNSUCCESSFUL ); return STATUS_UNSUCCESSFUL; } return STATUS_PENDING; } VOID SaNvramDpcRoutine( IN PKDPC Dpc, IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: This function is the device's DPC-for-ISR function. It is called only by the ISR function and it's only function is to start the next I/O. Arguments: DeviceObject - Pointer to the target device object. DeviceExtension - Pointer to the mini-port's device extension. Context - Mini-port supplied context pointer. Context: IRQL: DISPATCH_LEVEL, DPC context Return Value: None. --*/ { PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) Irp; NTSTATUS Status; // // Set the status based on the data // that was transfered. // if (DeviceExtension->CompletedIoSize == (DeviceExtension->IoIndex * sizeof(ULONG))) { Status = STATUS_SUCCESS; } else { Status = STATUS_UNEXPECTED_IO_ERROR; } // // Reset the current I/O paramaters to // prevent the ISR from reacting to a // bogus interrupt. // DeviceExtension->IoBuffer = NULL; DeviceExtension->IoLength = 0; DeviceExtension->IoFunction = 0; DeviceExtension->IoOffset = 0; DeviceExtension->IoIndex = 0; // // Complete this IRP and start the next // IRP is there is one in the queue // SaPortCompleteRequest( DeviceExtension, NULL, DeviceExtension->CompletedIoSize, Status, FALSE ); } NTSTATUS SaNvramDeviceIoctl( IN PVOID DeviceExtension, IN PIRP Irp, IN PVOID FsContext, IN ULONG FunctionCode, IN PVOID InputBuffer, IN ULONG InputBufferLength, IN PVOID OutputBuffer, IN ULONG OutputBufferLength ) /*++ Routine Description: This function is called by the SAPORT driver so that the mini-port driver can service an IOCTL call. Arguments: DeviceExtension - A pointer to the mini-port's device extension FunctionCode - The IOCTL function code InputBuffer - Pointer to the input buffer, contains the data sent down by the I/O InputBufferLength - Length in bytes of the InputBuffer OutputBuffer - Pointer to the output buffer, contains the data generated by this call OutputBufferLength - Length in bytes of the OutputBuffer Context: IRQL: IRQL PASSIVE_LEVEL, arbitrary thread context Return Value: If the function succeeds, it must return STATUS_SUCCESS. Otherwise, it must return one of the error status values defined in ntstatus.h. --*/ { NTSTATUS Status = STATUS_SUCCESS; PSA_NVRAM_CAPS NvramCaps = NULL; switch (FunctionCode) { case FUNC_SA_GET_VERSION: *((PULONG)OutputBuffer) = SA_INTERFACE_VERSION; break; case FUNC_SA_GET_CAPABILITIES: NvramCaps = (PSA_NVRAM_CAPS)OutputBuffer; NvramCaps->SizeOfStruct = sizeof(SA_NVRAM_CAPS); NvramCaps->NvramSize = MAX_NVRAM_SIZE; break; default: Status = STATUS_NOT_SUPPORTED; REPORT_ERROR( SA_DEVICE_NVRAM, "Unsupported device control", Status ); break; } return Status; } NTSTATUS SaNvramHwInitialize( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID DeviceExtensionIn, IN PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResources, IN ULONG PartialResourceCount ) /*++ Routine Description: This function is called by the SAPORT driver so that the mini-port driver can initialize it's hardware resources. Arguments: DeviceObject - Pointer to the target device object. Irp - Pointer to an IRP structure that describes the requested I/O operation. DeviceExtension - A pointer to the mini-port's device extension. PartialResources - Pointer to the translated resources alloacted by the system. PartialResourceCount - The number of resources in the PartialResources array. Context: IRQL: IRQL PASSIVE_LEVEL, system thread context Return Value: If the function succeeds, it must return STATUS_SUCCESS. Otherwise, it must return one of the error status values defined in ntstatus.h. --*/ { PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) DeviceExtensionIn; PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceMemory = NULL; ULONG i; for (i=0; iNvramMemBase = (PUCHAR) SaPortGetVirtualAddress( DeviceExtension, ResourceMemory->u.Memory.Start, ResourceMemory->u.Memory.Length ); if (DeviceExtension->NvramMemBase == NULL) { REPORT_ERROR( SA_DEVICE_NVRAM, "SaPortGetVirtualAddress failed", STATUS_NO_MEMORY ); return STATUS_NO_MEMORY; } // // Enable interrupts on the hardware // WRITE_REGISTER_USHORT( (PUSHORT)DeviceExtension->NvramMemBase, NVRAM_CONTROL_INTERRUPT_ENABLE ); return STATUS_SUCCESS; } NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: This routine is the driver's entry point, called by the I/O system to load the driver. The driver's entry points are initialized and a mutex to control paging is initialized. In DBG mode, this routine also examines the registry for special debug parameters. Arguments: DriverObject - a pointer to the object that represents this device driver. RegistryPath - a pointer to this driver's key in the Services tree. Return Value: STATUS_SUCCESS --*/ { NTSTATUS Status; SAPORT_INITIALIZATION_DATA SaPortInitData; RtlZeroMemory( &SaPortInitData, sizeof(SAPORT_INITIALIZATION_DATA) ); SaPortInitData.StructSize = sizeof(SAPORT_INITIALIZATION_DATA); SaPortInitData.DeviceType = SA_DEVICE_NVRAM; SaPortInitData.HwInitialize = SaNvramHwInitialize; SaPortInitData.DeviceIoctl = SaNvramDeviceIoctl; SaPortInitData.Read = SaNvramRead; SaPortInitData.Write = SaNvramWrite; SaPortInitData.InterruptServiceRoutine = SaNvramInterruptService; SaPortInitData.IsrForDpcRoutine = SaNvramDpcRoutine; SaPortInitData.DeviceExtensionSize = sizeof(DEVICE_EXTENSION); Status = SaPortInitialize( DriverObject, RegistryPath, &SaPortInitData ); if (!NT_SUCCESS(Status)) { REPORT_ERROR( SA_DEVICE_NVRAM, "SaPortInitialize failed\n", Status ); return Status; } return STATUS_SUCCESS; }