You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
664 lines
16 KiB
664 lines
16 KiB
/*++
|
|
|
|
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; i<PartialResourceCount; i++) {
|
|
if (PartialResources[i].Type == CmResourceTypeMemory) {
|
|
ResourceMemory = &PartialResources[i];
|
|
}
|
|
}
|
|
|
|
if (ResourceMemory == NULL) {
|
|
REPORT_ERROR( SA_DEVICE_NVRAM, "Missing memory resource", STATUS_UNSUCCESSFUL );
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Setup the memory base address
|
|
//
|
|
|
|
DeviceExtension->NvramMemBase = (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;
|
|
}
|