387 lines
8.4 KiB
C
387 lines
8.4 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1990 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
smbcsrv.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
SMBus class driver service functions
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Ken Reneris
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "smbc.h"
|
|||
|
|
|||
|
VOID
|
|||
|
SmbCCheckAlarmDelete (
|
|||
|
IN PSMBDATA Smb,
|
|||
|
IN PSMB_ALARM SmbAlarm
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE,SmbCCheckAlarmDelete)
|
|||
|
#pragma alloc_text(PAGE,SmbCRegisterAlarm)
|
|||
|
#pragma alloc_text(PAGE,SmbCDeregisterAlarm)
|
|||
|
#endif
|
|||
|
|
|||
|
UCHAR gHexDigits [] = "0123456789ABCDEF";
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SmbCRunAlarmMethodCompletionRoutine (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
{
|
|||
|
SmbPrint(SMB_ALARMS, ("SmbCRunAlarmMethodCompletionRoutine: Done running Control Method. Status=0x%08x\n", Irp->IoStatus.Status));
|
|||
|
|
|||
|
ExFreePool (Irp->AssociatedIrp.SystemBuffer);
|
|||
|
IoFreeIrp(Irp);
|
|||
|
|
|||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
SmbCRunAlarmMethod (
|
|||
|
IN PSMB_CLASS SmbClass,
|
|||
|
IN UCHAR Address,
|
|||
|
IN USHORT Data
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Run _Rxx for the alarm
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PIRP irp;
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
PACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER inputBuffer;
|
|||
|
|
|||
|
SmbPrint(SMB_ALARMS, ("SmbCRunAlarmMethod: Running Control method _R%02x\n", Address));
|
|||
|
|
|||
|
inputBuffer = ExAllocatePoolWithTag (
|
|||
|
NonPagedPool,
|
|||
|
sizeof (ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER),
|
|||
|
'AbmS'
|
|||
|
);
|
|||
|
RtlZeroMemory( inputBuffer, sizeof(ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER) );
|
|||
|
inputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER_SIGNATURE;
|
|||
|
inputBuffer->MethodNameAsUlong = '00Q_';
|
|||
|
inputBuffer->MethodName[2] = gHexDigits[ Address / 16];
|
|||
|
inputBuffer->MethodName[3] = gHexDigits[ Address % 16];
|
|||
|
inputBuffer->IntegerArgument = Data;
|
|||
|
|
|||
|
irp = IoAllocateIrp (SmbClass->LowerDeviceObject->StackSize, FALSE);
|
|||
|
if (!irp) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
irp->AssociatedIrp.SystemBuffer = inputBuffer;
|
|||
|
|
|||
|
ASSERT ((IOCTL_ACPI_ASYNC_EVAL_METHOD & 0x3) == METHOD_BUFFERED);
|
|||
|
irp->Flags = IRP_BUFFERED_IO | IRP_INPUT_OPERATION;
|
|||
|
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|||
|
|
|||
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|||
|
|
|||
|
irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
|||
|
irpSp->Parameters.DeviceIoControl.OutputBufferLength = 0;
|
|||
|
irpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ACPI_EVAL_INPUT_BUFFER_SIMPLE_INTEGER);
|
|||
|
irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_ACPI_ASYNC_EVAL_METHOD;
|
|||
|
|
|||
|
irp->UserBuffer = NULL;
|
|||
|
|
|||
|
IoSetCompletionRoutine(
|
|||
|
irp,
|
|||
|
SmbCRunAlarmMethodCompletionRoutine,
|
|||
|
NULL, // No Context This just frees the IRP
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
TRUE
|
|||
|
);
|
|||
|
|
|||
|
IoCallDriver(SmbClass->LowerDeviceObject, irp);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SmbClassAlarm (
|
|||
|
IN PSMB_CLASS SmbClass,
|
|||
|
IN UCHAR Address,
|
|||
|
IN USHORT Data
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Miniport has an alarm input
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PSMBDATA Smb;
|
|||
|
PSMB_ALARM SmbAlarm;
|
|||
|
PLIST_ENTRY Entry, NextEntry;
|
|||
|
BOOLEAN AlarmRegistered = FALSE;
|
|||
|
|
|||
|
Smb = CONTAINING_RECORD (SmbClass, SMBDATA, Class);
|
|||
|
ASSERT_DEVICE_LOCKED (Smb);
|
|||
|
|
|||
|
Entry = Smb->Alarms.Flink;
|
|||
|
while (Entry != &Smb->Alarms) {
|
|||
|
SmbAlarm = CONTAINING_RECORD (Entry, SMB_ALARM, Link);
|
|||
|
|
|||
|
//
|
|||
|
// If notification is for this address, issue it
|
|||
|
//
|
|||
|
|
|||
|
if (Address >= SmbAlarm->MinAddress && Address <= SmbAlarm->MaxAddress) {
|
|||
|
|
|||
|
//
|
|||
|
// A driver has registered for this notification. Don't call the BIOS.
|
|||
|
//
|
|||
|
AlarmRegistered = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Raise reference count before calling notifcation function
|
|||
|
//
|
|||
|
|
|||
|
SmbAlarm->Reference += 1;
|
|||
|
ASSERT (SmbAlarm->Reference != 0);
|
|||
|
SmbClassUnlockDevice (SmbClass);
|
|||
|
|
|||
|
//
|
|||
|
// Issue notification
|
|||
|
//
|
|||
|
|
|||
|
SmbAlarm->NotifyFunction (SmbAlarm->NotifyContext, Address, Data);
|
|||
|
|
|||
|
//
|
|||
|
// Continue
|
|||
|
//
|
|||
|
|
|||
|
SmbClassLockDevice (SmbClass);
|
|||
|
SmbAlarm->Reference -= 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get next entry
|
|||
|
//
|
|||
|
|
|||
|
NextEntry = Entry->Flink;
|
|||
|
|
|||
|
//
|
|||
|
// If entry is pending delete, hand it to deleting thread
|
|||
|
//
|
|||
|
|
|||
|
if (SmbAlarm->Flag & SMBC_ALARM_DELETE_PENDING) {
|
|||
|
SmbCCheckAlarmDelete (Smb, SmbAlarm);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Move on
|
|||
|
//
|
|||
|
|
|||
|
Entry = NextEntry;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If no one registered for this alarm, call the _Rxx control method
|
|||
|
//
|
|||
|
if (!AlarmRegistered) {
|
|||
|
|
|||
|
SmbCRunAlarmMethod (SmbClass, Address, Data);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
SmbCCheckAlarmDelete (
|
|||
|
IN PSMBDATA Smb,
|
|||
|
IN PSMB_ALARM SmbAlarm
|
|||
|
)
|
|||
|
{
|
|||
|
//
|
|||
|
// If alarm structure is referenced, wait somemore
|
|||
|
//
|
|||
|
|
|||
|
if (SmbAlarm->Reference) {
|
|||
|
return ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Time to free it. Remove it from the notification list, clear
|
|||
|
// the pending flag and set the event to let waiting threads know
|
|||
|
// that some entry was removed
|
|||
|
//
|
|||
|
|
|||
|
RemoveEntryList (&SmbAlarm->Link);
|
|||
|
SmbAlarm->Flag &= ~SMBC_ALARM_DELETE_PENDING;
|
|||
|
KeSetEvent (&Smb->AlarmEvent, 0, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SmbCRegisterAlarm (
|
|||
|
PSMBDATA Smb,
|
|||
|
PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Called to register for an alarm event
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PVOID LockPtr;
|
|||
|
PSMB_ALARM SmbAlarm, *Result;
|
|||
|
PSMB_REGISTER_ALARM RegAlarm;
|
|||
|
PIO_STACK_LOCATION IrpSp;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
if (ExGetPreviousMode() != KernelMode ||
|
|||
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(SMB_REGISTER_ALARM) ||
|
|||
|
IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(PSMB_ALARM) ) {
|
|||
|
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
RegAlarm = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|||
|
SmbAlarm = ExAllocatePoolWithTag (
|
|||
|
NonPagedPool,
|
|||
|
sizeof (SMB_ALARM),
|
|||
|
'AbmS'
|
|||
|
);
|
|||
|
|
|||
|
if (!SmbAlarm) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
SmbAlarm->Flag = 0;
|
|||
|
SmbAlarm->Reference = 0;
|
|||
|
SmbAlarm->MinAddress = RegAlarm->MinAddress;
|
|||
|
SmbAlarm->MaxAddress = RegAlarm->MaxAddress;
|
|||
|
SmbAlarm->NotifyFunction = RegAlarm->NotifyFunction;
|
|||
|
SmbAlarm->NotifyContext = RegAlarm->NotifyContext;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Add it to the alarm notification list
|
|||
|
//
|
|||
|
|
|||
|
LockPtr = MmLockPagableCodeSection(SmbCRegisterAlarm);
|
|||
|
SmbClassLockDevice (&Smb->Class);
|
|||
|
InsertTailList (&Smb->Alarms, &SmbAlarm->Link);
|
|||
|
SmbClassUnlockDevice (&Smb->Class);
|
|||
|
MmUnlockPagableImageSection(LockPtr);
|
|||
|
|
|||
|
//
|
|||
|
// Return value caller needs to deregister with
|
|||
|
//
|
|||
|
|
|||
|
Result = (PSMB_ALARM *) Irp->UserBuffer;
|
|||
|
*Result = SmbAlarm;
|
|||
|
Irp->IoStatus.Information = sizeof(PSMB_ALARM);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SmbCDeregisterAlarm (
|
|||
|
PSMBDATA Smb,
|
|||
|
PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Called to register for an alarm event
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PVOID LockPtr;
|
|||
|
PSMB_ALARM SmbAlarm;
|
|||
|
PIO_STACK_LOCATION IrpSp;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
if (ExGetPreviousMode() != KernelMode ||
|
|||
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(PSMB_ALARM) ) {
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
SmbAlarm = * (PSMB_ALARM *) IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|||
|
LockPtr = MmLockPagableCodeSection(SmbCDeregisterAlarm);
|
|||
|
SmbClassLockDevice (&Smb->Class);
|
|||
|
|
|||
|
//
|
|||
|
// Flag alarm structure as delete pending
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
SmbAlarm->Flag |= SMBC_ALARM_DELETE_PENDING;
|
|||
|
|
|||
|
//
|
|||
|
// While delete is pending wait
|
|||
|
//
|
|||
|
|
|||
|
while (SmbAlarm->Flag & SMBC_ALARM_DELETE_PENDING) {
|
|||
|
|
|||
|
//
|
|||
|
// Issue bogus alarm to generate freeing
|
|||
|
//
|
|||
|
|
|||
|
KeResetEvent (&Smb->AlarmEvent);
|
|||
|
SmbClassAlarm (&Smb->Class, 0xFF, 0);
|
|||
|
|
|||
|
//
|
|||
|
// Wait for alarm structure to get freed, then check if it
|
|||
|
// was ours
|
|||
|
//
|
|||
|
|
|||
|
SmbClassUnlockDevice (&Smb->Class);
|
|||
|
KeWaitForSingleObject (
|
|||
|
&Smb->AlarmEvent,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
SmbClassLockDevice (&Smb->Class);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// It's been removed, free the memory
|
|||
|
//
|
|||
|
|
|||
|
SmbClassUnlockDevice (&Smb->Class);
|
|||
|
MmUnlockPagableImageSection(LockPtr);
|
|||
|
|
|||
|
ExFreePool (SmbAlarm);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|