|
|
/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
smbmisc.c
Abstract:
SMBus handler functions
Author:
Ken Reneris
Environment:
Notes:
Revision History:
Chris Windle 1/27/98 Bug Fixes
--*/
#include "smbbattp.h"
//
// Make the SelectorBit table pageable
//
//#ifdef ALLOC_DATA_PRAGMA
//#pragma data_seg("PAGE")
//#endif
//
// Lookup table for the battery that corresponds to bit positions and
// whether or not reverse logic is being used (to indicate charging or
// discharging).
//
// NOTE: To support Simultaneous Charging and Powering, this table
// has been modified to account for multiple bits. Also, it can't be
// used for battery index lookup since it assumes one bit set maximum.
// Instead, use special indexes for multiple batteries as follows:
//
// 1st Battery = Index & 0x03
// 2nd Battery = (Index >> 2) & 0x03 (Battery A not allowed)
// 3rd Battery = (Index >> 4) & 0x03 (Battery A not allowed)
//
// In < 4 battery systems the Battery D bit can be used to determine
// the nibbles that are inverted, and it allows the following combinations:
//
// Battery A & B
// Battery A & C
// Battery B & C
// Battery A, B, & C
//
const SELECTOR_STATE_LOOKUP SelectorBits [16] = { {BATTERY_NONE, FALSE}, // Bit Pattern: 0000
{BATTERY_A, FALSE}, // 0001
{BATTERY_B, FALSE}, // 0010
{MULTIBATT_AB, FALSE}, // 0011
{BATTERY_C, FALSE}, // 0100
{MULTIBATT_AC, FALSE}, // 0101
{MULTIBATT_BC, FALSE}, // 0110
{MULTIBATT_ABC, FALSE}, // 0111
{MULTIBATT_ABC, TRUE}, // 1000
{MULTIBATT_BC, TRUE}, // 1001
{MULTIBATT_AC, TRUE}, // 1010
{BATTERY_C, TRUE}, // 1011
{MULTIBATT_AB, TRUE}, // 1100
{BATTERY_B, TRUE}, // 1101
{BATTERY_A, TRUE}, // 1110
{BATTERY_NONE, TRUE} // 1111
};
//
// Note: For 4-Battery Systems to support Simultaneous Capability
// properly, the following two assumptions must be made:
// - Battery D can never be used simultaneously.
// - Three batteries can not be used simultaneously.
//
// This allows for only the following possible battery combinations:
//
// Battery A & B
// Battery A & C
// Battery B & C
//
// The following table is used for 4-battery lookup
//
const SELECTOR_STATE_LOOKUP SelectorBits4 [16] = { {BATTERY_NONE, FALSE}, // Bit Pattern: 0000
{BATTERY_A, FALSE}, // 0001
{BATTERY_B, FALSE}, // 0010
{MULTIBATT_AB, FALSE}, // 0011
{BATTERY_C, FALSE}, // 0100
{MULTIBATT_AC, FALSE}, // 0101
{MULTIBATT_BC, FALSE}, // 0110
{BATTERY_D, TRUE}, // 0111
{BATTERY_D, FALSE}, // 1000
{MULTIBATT_BC, TRUE}, // 1001
{MULTIBATT_AC, TRUE}, // 1010
{BATTERY_C, TRUE}, // 1011
{MULTIBATT_AB, TRUE}, // 1100
{BATTERY_B, TRUE}, // 1101
{BATTERY_A, TRUE}, // 1110
{BATTERY_NONE, TRUE} // 1111
};
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,SmbBattLockDevice)
#pragma alloc_text(PAGE,SmbBattUnlockDevice)
#pragma alloc_text(PAGE,SmbBattLockSelector)
#pragma alloc_text(PAGE,SmbBattUnlockSelector)
#pragma alloc_text(PAGE,SmbBattRequest)
#pragma alloc_text(PAGE,SmbBattRB)
#pragma alloc_text(PAGE,SmbBattRW)
#pragma alloc_text(PAGE,SmbBattRSW)
#pragma alloc_text(PAGE,SmbBattWW)
#pragma alloc_text(PAGE,SmbBattGenericRW)
#pragma alloc_text(PAGE,SmbBattGenericWW)
#pragma alloc_text(PAGE,SmbBattGenericRequest)
#pragma alloc_text(PAGE,SmbBattSetSelectorComm)
#pragma alloc_text(PAGE,SmbBattResetSelectorComm)
#pragma alloc_text(PAGE,SmbBattDirectDataAccess)
#pragma alloc_text(PAGE,SmbBattIndex)
#pragma alloc_text(PAGE,SmbBattReverseLogic)
#pragma alloc_text(PAGE,SmbBattAcquireGlobalLock)
#pragma alloc_text(PAGE,SmbBattReleaseGlobalLock)
#endif
VOID SmbBattLockDevice ( IN PSMB_BATT SmbBatt ) { PAGED_CODE();
//
// Get device lock on the battery
//
ExAcquireFastMutex (&SmbBatt->NP->Mutex); }
VOID SmbBattUnlockDevice ( IN PSMB_BATT SmbBatt ) { PAGED_CODE();
//
// Release device lock on the battery
//
ExReleaseFastMutex (&SmbBatt->NP->Mutex); }
VOID SmbBattLockSelector ( IN PBATTERY_SELECTOR Selector ) { PAGED_CODE();
//
// Get device lock on the selector
//
if (Selector) { ExAcquireFastMutex (&Selector->Mutex); } }
VOID SmbBattUnlockSelector ( IN PBATTERY_SELECTOR Selector ) { PAGED_CODE();
//
// Release device lock on the selector
//
if (Selector) { ExReleaseFastMutex (&Selector->Mutex); } }
NTSTATUS SmbBattSynchronousRequest ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++
Routine Description:
Completion function for synchronous IRPs sent to this driver. Context is the event to set
--*/ { PKEVENT Event;
Event = (PKEVENT) Context; KeSetEvent (Event, IO_NO_INCREMENT, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; }
VOID SmbBattRequest ( IN PSMB_BATT SmbBatt, IN PSMB_REQUEST SmbReq ) // function to issue SMBus request
{ KEVENT Event; PIRP Irp; PIO_STACK_LOCATION IrpSp; NTSTATUS Status; BOOLEAN useLock = SmbBattUseGlobalLock; ACPI_MANIPULATE_GLOBAL_LOCK_BUFFER globalLock;
PAGED_CODE();
//
// Build Io Control for SMB bus driver for this request
//
KeInitializeEvent (&Event, NotificationEvent, FALSE);
if (!SmbBatt->SmbHcFdo) { //
// The SMB host controller either hasn't been opened yet (in start device) or
// there was an error opening it and we did not get deleted somehow.
//
BattPrint(BAT_ERROR, ("SmbBattRequest: SmbHc hasn't been opened yet \n")); SmbReq->Status = SMB_UNKNOWN_FAILURE; return ; }
Irp = IoAllocateIrp (SmbBatt->SmbHcFdo->StackSize, FALSE); if (!Irp) { SmbReq->Status = SMB_UNKNOWN_FAILURE; return ; }
IrpSp = IoGetNextIrpStackLocation(Irp); IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; IrpSp->Parameters.DeviceIoControl.IoControlCode = SMB_BUS_REQUEST; IrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(SMB_REQUEST); IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = SmbReq; IoSetCompletionRoutine (Irp, SmbBattSynchronousRequest, &Event, TRUE, TRUE, TRUE);
//
// Issue it
//
//
// Note: uselock is a cached value of the global variable, so in case the
// value changes, we won't aquire and not release etc.
//
if (useLock) { if (!NT_SUCCESS (SmbBattAcquireGlobalLock (SmbBatt->SmbHcFdo, &globalLock))) { useLock = FALSE; } }
IoCallDriver (SmbBatt->SmbHcFdo, Irp); KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = Irp->IoStatus.Status; IoFreeIrp (Irp);
if (useLock) { SmbBattReleaseGlobalLock (SmbBatt->SmbHcFdo, &globalLock); }
//
// Check result code
//
if (!NT_SUCCESS(Status)) { BattPrint(BAT_ERROR, ("SmbBattRequest: error in SmbHc request - %x\n", Status)); SmbReq->Status = SMB_UNKNOWN_FAILURE; } }
VOID SmbBattRB( IN PSMB_BATT SmbBatt, IN UCHAR SmbCmd, OUT PUCHAR Buffer, OUT PUCHAR BufferLength ) // function to read-block from the battery
{ SMB_REQUEST SmbReq;
PAGED_CODE();
SmbReq.Protocol = SMB_READ_BLOCK; SmbReq.Address = SMB_BATTERY_ADDRESS; SmbReq.Command = SmbCmd; SmbBattRequest (SmbBatt, &SmbReq);
if (SmbReq.Status == SMB_STATUS_OK) { ASSERT (SmbReq.BlockLength < SMB_MAX_DATA_SIZE); memcpy (Buffer, SmbReq.Data, SmbReq.BlockLength); *BufferLength = SmbReq.BlockLength; } else { // some sort of failure, check tag data for cache validity
SmbBatt->Info.Valid &= ~VALID_TAG_DATA; } }
VOID SmbBattRW( IN PSMB_BATT SmbBatt, IN UCHAR SmbCmd, OUT PULONG Result ) // function to read-word from the battery
// N.B. word is returned as a ULONG
{ SMB_REQUEST SmbReq;
PAGED_CODE();
SmbReq.Protocol = SMB_READ_WORD; SmbReq.Address = SMB_BATTERY_ADDRESS; SmbReq.Command = SmbCmd; SmbBattRequest (SmbBatt, &SmbReq);
if (SmbReq.Status != SMB_STATUS_OK) { // some sort of failure, check tag data for cache validity
SmbBatt->Info.Valid &= ~VALID_TAG_DATA; }
*Result = SmbReq.Data[0] | SmbReq.Data[1] << WORD_MSB_SHIFT; BattPrint(BAT_IO, ("SmbBattRW: Command: %02x == %04x\n", SmbCmd, *Result)); }
VOID SmbBattRSW( IN PSMB_BATT SmbBatt, IN UCHAR SmbCmd, OUT PLONG Result ) // function to read-signed-word from the battery
// N.B. word is returned as a LONG
{ ULONG i;
PAGED_CODE();
SmbBattRW(SmbBatt, SmbCmd, &i); *Result = ((SHORT) i); }
VOID SmbBattWW( IN PSMB_BATT SmbBatt, IN UCHAR SmbCmd, IN ULONG Data ) // function to write-word to the battery
{ SMB_REQUEST SmbReq;
PAGED_CODE();
SmbReq.Protocol = SMB_WRITE_WORD; SmbReq.Address = SMB_BATTERY_ADDRESS; SmbReq.Command = SmbCmd; SmbReq.Data[0] = (UCHAR) (Data & WORD_LSB_MASK); SmbReq.Data[1] = (UCHAR) (Data >> WORD_MSB_SHIFT) & WORD_LSB_MASK; BattPrint(BAT_IO, ("SmbBattWW: Command: %02x = %04x\n", SmbCmd, Data)); SmbBattRequest (SmbBatt, &SmbReq);
if (SmbReq.Status != SMB_STATUS_OK) { // some sort of failure, check tag data for cache validity
SmbBatt->Info.Valid &= ~VALID_TAG_DATA; } }
UCHAR SmbBattGenericRW( IN PDEVICE_OBJECT SmbHcFdo, IN UCHAR Address, IN UCHAR SmbCmd, OUT PULONG Result ) // function to read-word from the SMB device (charger or selector)
// N.B. word is returned as a ULONG
{ SMB_REQUEST SmbReq;
PAGED_CODE();
SmbReq.Protocol = SMB_READ_WORD; SmbReq.Address = Address; SmbReq.Command = SmbCmd; SmbBattGenericRequest (SmbHcFdo, &SmbReq);
*Result = SmbReq.Data[0] | (SmbReq.Data[1] << WORD_MSB_SHIFT); BattPrint(BAT_IO, ("SmbBattGenericRW: Address: %02x:%02x == %04x\n", Address, SmbCmd, *Result)); return SmbReq.Status; }
UCHAR SmbBattGenericWW( IN PDEVICE_OBJECT SmbHcFdo, IN UCHAR Address, IN UCHAR SmbCmd, IN ULONG Data ) // function to write-word to SMB device (charger or selector)
{ SMB_REQUEST SmbReq;
PAGED_CODE();
SmbReq.Protocol = SMB_WRITE_WORD; SmbReq.Address = Address; SmbReq.Command = SmbCmd; SmbReq.Data[0] = (UCHAR) (Data & WORD_LSB_MASK); SmbReq.Data[1] = (UCHAR) (Data >> WORD_MSB_SHIFT) & WORD_LSB_MASK;
BattPrint(BAT_IO, ("SmbBattGenericWW: Address: %02x:%02x = %04x\n", Address, SmbCmd, Data)); SmbBattGenericRequest (SmbHcFdo, &SmbReq); return SmbReq.Status;
}
VOID SmbBattGenericRequest ( IN PDEVICE_OBJECT SmbHcFdo, IN PSMB_REQUEST SmbReq ) // function to issue SMBus request
{ KEVENT Event; PIRP Irp; PIO_STACK_LOCATION IrpSp; NTSTATUS Status; BOOLEAN useLock = SmbBattUseGlobalLock; ACPI_MANIPULATE_GLOBAL_LOCK_BUFFER globalLock;
PAGED_CODE();
//
// Build Io Control for SMB bus driver for this request
//
KeInitializeEvent (&Event, NotificationEvent, FALSE);
if (!SmbHcFdo) { //
// The SMB host controller either hasn't been opened yet (in start device) or
// there was an error opening it and we did not get deleted somehow.
//
BattPrint(BAT_ERROR, ("SmbBattGenericRequest: SmbHc hasn't been opened yet \n")); SmbReq->Status = SMB_UNKNOWN_FAILURE; return ; }
Irp = IoAllocateIrp (SmbHcFdo->StackSize, FALSE); if (!Irp) { SmbReq->Status = SMB_UNKNOWN_FAILURE; return ; }
IrpSp = IoGetNextIrpStackLocation(Irp); IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; IrpSp->Parameters.DeviceIoControl.IoControlCode = SMB_BUS_REQUEST; IrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(SMB_REQUEST); IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = SmbReq; IoSetCompletionRoutine (Irp, SmbBattSynchronousRequest, &Event, TRUE, TRUE, TRUE);
//
// Issue it
//
//
// Note: uselock is a cached value of the global variable, so in case the
// value changes, we won't acquire and not release etc.
//
if (useLock) { if (!NT_SUCCESS (SmbBattAcquireGlobalLock (SmbHcFdo, &globalLock))) { useLock = FALSE; } }
IoCallDriver (SmbHcFdo, Irp); KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = Irp->IoStatus.Status; IoFreeIrp (Irp);
if (useLock) { SmbBattReleaseGlobalLock (SmbHcFdo, &globalLock); }
//
// Check result code
//
if (!NT_SUCCESS(Status)) { BattPrint(BAT_ERROR, ("SmbBattGenericRequest: error in SmbHc request - %x\n", Status)); SmbReq->Status = SMB_UNKNOWN_FAILURE; } }
NTSTATUS SmbBattSetSelectorComm ( IN PSMB_BATT SmbBatt, OUT PULONG OldSelectorState ) /*++
Routine Description:
This routine sets the communication path through the selector to the calling battery. It returns the original selector state in the variable provided.
NOTE: It is assumed that the caller already has acquired the device lock on the selector before calling us.
NOTE: This function should always be called in a pair with SmbBattResetSelectorComm
Arguments:
SmbBatt - Nonpaged extension for current battery
OldSelectorState - Original selector state at start of this function
Return Value:
NTSTATUS
--*/ { PBATTERY_SELECTOR selector; UCHAR smbStatus; ULONG requestData;
PAGED_CODE();
BattPrint(BAT_TRACE, ("SmbBattSetSelectorComm: ENTERING\n"));
//
// We only need to do this if there is a selector in the system.
//
if (SmbBatt->SelectorPresent) {
selector = SmbBatt->Selector; *OldSelectorState = selector->SelectorState;
//
// If the battery isn't present, fail the request.
//
if (!(selector->SelectorState & SmbBatt->SelectorBitPosition)) { return STATUS_NO_SUCH_DEVICE; }
//
// See if we are already set up to talk with the requesting battery.
// We will check against the cached information in the selector struct.
//
if (selector->SelectorState & (SmbBatt->SelectorBitPosition << SELECTOR_SHIFT_COM)) { return STATUS_SUCCESS; }
//
// Build the data word to change the selector communications. This will
// look like the following:
//
// PRESENT field 0xf we don't want to change anything here
// CHARGE field 0xf we don't want to change anything here
// POWER BY field 0xf we don't want to change anything here
// SMB field 0x_ the bit set according to the battery number
//
requestData = (SmbBatt->SelectorBitPosition << SELECTOR_SHIFT_COM) | SELECTOR_SET_COM_MASK;
smbStatus = SmbBattGenericWW ( SmbBatt->SmbHcFdo, selector->SelectorAddress, selector->SelectorStateCommand, requestData );
if (smbStatus != SMB_STATUS_OK) { BattPrint (BAT_ERROR, ("SmbBattSetSelectorComm: couldn't write selector state - %x\n", smbStatus)); return STATUS_UNSUCCESSFUL; } else { selector->SelectorState |= SELECTOR_STATE_SMB_MASK; selector->SelectorState &= requestData;
BattPrint (BAT_IO, ("SmbBattSetSelectorComm: state after write - %x\n", selector->SelectorState)); }
} // if (subsystemExt->SelectorPresent)
BattPrint(BAT_TRACE, ("SmbBattSetSelectorComm: EXITING\n")); return STATUS_SUCCESS; }
NTSTATUS SmbBattResetSelectorComm ( IN PSMB_BATT SmbBatt, IN ULONG OldSelectorState ) /*++
Routine Description:
This routine resets the communication path through the selector to the its original state. It returns the original selector state in the variable provided.
NOTE: It is assumed that the caller already has acquired the device lock on the selector before calling us.
NOTE: This function should always be called in a pair with SmbBattSetSelectorComm
Arguments:
SmbBatt - Nonpaged extension for current battery
OldSelectorState - Original selector state to be restored
Return Value:
NTSTATUS
--*/ { PBATTERY_SELECTOR selector; UCHAR smbStatus; ULONG tmpState;
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
BattPrint(BAT_TRACE, ("SmbBattResetSelectorComm: ENTERING\n"));
//
// We only need to do this if there is a selector in the system.
//
if (SmbBatt->SelectorPresent) {
selector = SmbBatt->Selector;
//
// See if we were already set up to talk with the requesting battery.
// We will check against the cached information in the selector struct.
//
if ((OldSelectorState & selector->SelectorState) & SELECTOR_STATE_SMB_MASK) { return STATUS_SUCCESS; }
//
// Change the selector communications back. The SMB field is the only
// that we will write.
//
tmpState = SELECTOR_SET_COM_MASK; tmpState |= OldSelectorState & SELECTOR_STATE_SMB_MASK;
smbStatus = SmbBattGenericWW ( SmbBatt->SmbHcFdo, selector->SelectorAddress, selector->SelectorStateCommand, tmpState );
if (smbStatus != SMB_STATUS_OK) { BattPrint ( BAT_ERROR, ("SmbBattResetSelectorComm: couldn't write selector state - %x\n", smbStatus) ); status = STATUS_UNSUCCESSFUL; } else { selector->SelectorState |= SELECTOR_STATE_SMB_MASK; selector->SelectorState &= tmpState; BattPrint ( BAT_IO, ("SmbBattResetSelectorComm: state after write - %x\n", selector->SelectorState) ); }
} // if (subsystemExt->SelectorPresent)
BattPrint(BAT_TRACE, ("SmbBattResetSelectorComm: EXITING\n")); return status; }
NTSTATUS SmbBattDirectDataAccess ( IN PSMB_NP_BATT DeviceExtension, IN PSMBBATT_DATA_STRUCT IoBuffer, IN ULONG InputLen, IN ULONG OutputLen ) /*++
Routine Description:
This routine is used to handle IOCTLs acessing the SMBBatt commands directly.
Arguments:
DeviceExtension - Device extension for the smart battery subsystem
IoBuffer - Buffer that contains the input structure and will contain the results of the read.
Return Value:
NTSTATUS --*/ { PSMB_BATT_SUBSYSTEM SubsystemExt; PSMB_BATT SmbBatt;
UCHAR address; UCHAR command; UCHAR smbStatus; ULONG oldSelectorState; ULONG ReturnBufferLength; UCHAR strLength; UCHAR strBuffer[SMB_MAX_DATA_SIZE+1]; // +1 extra char to hold NULL
UCHAR strBuffer2[SMB_MAX_DATA_SIZE+1]; UNICODE_STRING unicodeString; ANSI_STRING ansiString; UCHAR tempFlags;
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
if ((DeviceExtension->SmbBattFdoType == SmbTypeBattery) && (IoBuffer->Address == SMB_BATTERY_ADDRESS)) { // This is a battery data request
SmbBatt = DeviceExtension->Batt; SmbBattLockSelector (SmbBatt->Selector); SmbBattLockDevice (SmbBatt); status = SmbBattSetSelectorComm (SmbBatt, &oldSelectorState); if (NT_SUCCESS (status)) { if ((InputLen >= sizeof(SMBBATT_DATA_STRUCT)) && (OutputLen == 0)) { // This is a write command
status = STATUS_NOT_IMPLEMENTED; } else if ((InputLen == sizeof(SMBBATT_DATA_STRUCT)) && (OutputLen > 0)){ // This is a Read command
if ((IoBuffer->Command >= BAT_REMAINING_CAPACITY_ALARM) && (IoBuffer->Command <= BAT_SERIAL_NUMBER)) {
// ReadWord Commands
if (OutputLen == sizeof(SMBBATT_DATA_STRUCT)) { tempFlags = SmbBatt->Info.Valid; SmbBatt->Info.Valid |= VALID_TAG_DATA; SmbBattRW(SmbBatt, IoBuffer->Command, &IoBuffer->Data.Ulong); if (SmbBatt->Info.Valid & VALID_TAG_DATA) { ReturnBufferLength = sizeof(ULONG); } else { status = STATUS_DATA_ERROR; } SmbBatt->Info.Valid = tempFlags; } else { status = STATUS_INVALID_BUFFER_SIZE; }
} else if ((IoBuffer->Command >= BAT_MANUFACTURER_NAME) && (IoBuffer->Command <= BAT_MANUFACTURER_DATA)) {
// ReadBlock Commands
if (OutputLen == (SMBBATT_DATA_STRUCT_SIZE)+(SMB_MAX_DATA_SIZE*2)) { memset (&IoBuffer->Data.Block[0], 0, (SMB_MAX_DATA_SIZE*2)); unicodeString.Buffer = &IoBuffer->Data.Block[0]; unicodeString.MaximumLength = SMB_MAX_DATA_SIZE*2; unicodeString.Length = 0;
memset (strBuffer, 0, sizeof(strBuffer)); memset (strBuffer2, 0, sizeof(strBuffer2)); do { SmbBattRB ( SmbBatt, IoBuffer->Command, strBuffer, &strLength );
SmbBattRB ( SmbBatt, IoBuffer->Command, strBuffer2, &strLength ); } while (strcmp (strBuffer, strBuffer2));
RtlInitAnsiString (&ansiString, strBuffer); RtlAnsiStringToUnicodeString (&unicodeString, &ansiString, FALSE);
ReturnBufferLength = unicodeString.Length;
} else { status = STATUS_INVALID_BUFFER_SIZE; } } else { // Unsupported Commands
status = STATUS_INVALID_PARAMETER; } }
}
SmbBattResetSelectorComm (SmbBatt, oldSelectorState); SmbBattUnlockDevice (SmbBatt); SmbBattUnlockSelector (SmbBatt->Selector); } else if (DeviceExtension->SmbBattFdoType == SmbTypeSubsystem) { // This is a battery subsystem
SubsystemExt = (PSMB_BATT_SUBSYSTEM) DeviceExtension; SmbBattLockSelector (SubsystemExt->Selector);
if ((InputLen >= sizeof(SMBBATT_DATA_STRUCT)) && (OutputLen == 0)) { // This is a write command
status = STATUS_NOT_IMPLEMENTED; } else if ((InputLen == sizeof(SMBBATT_DATA_STRUCT)) && (OutputLen > 0)){ // This is a Read command
switch (IoBuffer->Address) {
case SMB_SELECTOR_ADDRESS:
//
// We have to do some translation for selector requests depending
// on whether the selector is stand alone or implemented in the
// charger.
//
if ((SubsystemExt->SelectorPresent) && (SubsystemExt->Selector)) {
address = SubsystemExt->Selector->SelectorAddress; command = IoBuffer->Command;
// Map to Charger if Selector is implemented in the Charger
if (address == SMB_CHARGER_ADDRESS) { switch (command) { case SELECTOR_SELECTOR_STATE: case SELECTOR_SELECTOR_PRESETS: case SELECTOR_SELECTOR_INFO: command |= CHARGER_SELECTOR_COMMANDS; break;
default: status = STATUS_NOT_SUPPORTED; break; } }
} else { status = STATUS_NO_SUCH_DEVICE; }
break;
case SMB_CHARGER_ADDRESS:
//
// For this one we currently only support the ChargerStatus and
// ChargerSpecInfo commands.
//
// Other commands are not currently supported.
//
address = IoBuffer->Address;
switch (IoBuffer->Command) { case CHARGER_SPEC_INFO: case CHARGER_STATUS:
command = IoBuffer->Command; break;
default: status = STATUS_NOT_SUPPORTED; break;
}
break;
default: status = STATUS_NOT_SUPPORTED; break;
} // switch (readStruct->Address)
if (status == STATUS_SUCCESS) { //
// Do the read command
//
smbStatus = SmbBattGenericRW ( SubsystemExt->SmbHcFdo, address, command, &IoBuffer->Data.Ulong );
if (smbStatus != SMB_STATUS_OK) { BattPrint ( BAT_ERROR, ("SmbBattDirectDataAccess: Couldn't read from - %x, status - %x\n", address, smbStatus) );
status = STATUS_UNSUCCESSFUL;
} }
}
SmbBattUnlockSelector (SubsystemExt->Selector); } else { status=STATUS_INVALID_DEVICE_REQUEST; BattPrint ( BAT_ERROR, ("SmbBattDirectDataAccess: Invalid SmbBattFdoType") ); }
return status; }
UCHAR SmbBattIndex ( IN PBATTERY_SELECTOR Selector, IN ULONG SelectorNibble, IN UCHAR SimultaneousIndex ) /*++
Routine Description:
This routine is provided as a helper routine to determine which battery is selected in a given selector nibble, based on the number of batteries supported in the system.
Arguments:
Selector - Structure defining selector address and commands
SelectorNibble - The nibble of the SelectorState, moved to the low order 4 bits, to check reverse logic on.
SimultaneousIndex - Which batteryindex is requested in simultaneous- battery situations (0, 1, or 2)
Return Value:
BatteryIndex = 0 - Battery A 1 - Battery B 2 - Battery C 3 - Battery D FF - No Battery
--*/ { UCHAR batteryIndex;
PAGED_CODE();
// Assume if SelectorInfo supports 4 batteries, use SelectorBits4 table
if (Selector->SelectorInfo & BATTERY_D_PRESENT) { batteryIndex = SelectorBits4[SelectorNibble].BatteryIndex; } else { batteryIndex = SelectorBits[SelectorNibble].BatteryIndex; }
// If it's valid
if (batteryIndex != BATTERY_NONE) {
// return index for First Battery
if (SimultaneousIndex == 0) { return (batteryIndex & 3);
// return index for Second Battery
} else if (SimultaneousIndex == 1) { batteryIndex = (batteryIndex >> 2) & 3; if (batteryIndex != BATTERY_A) { return (batteryIndex); }
// return index for Third Battery
} else if (SimultaneousIndex == 2) { batteryIndex = (batteryIndex >> 2) & 3; if (batteryIndex != BATTERY_A) { return (batteryIndex); } } }
// return no battery index
return (BATTERY_NONE); }
BOOLEAN SmbBattReverseLogic ( IN PBATTERY_SELECTOR Selector, IN ULONG SelectorNibble ) /*++
Routine Description:
This routine is provided as a helper routine to determine the reverse logic on a given selector nibble, based on the number of batteries supported in the system.
Arguments:
Selector - Structure defining selector address and commands
SelectorNibble - The nibble of the SelectorState, moved to the low order 4 bits, to check reverse logic on.
Return Value:
FALSE if the nibble is normal TRUE if the nibble is inverted
--*/ {
PAGED_CODE();
// Assume if SelectorInfo supports 4 batteries, use SelectorBits4 table
if (Selector->SelectorInfo & BATTERY_D_PRESENT) { return (SelectorBits4[SelectorNibble].ReverseLogic); } else { return (SelectorBits[SelectorNibble].ReverseLogic); } }
NTSTATUS SmbBattAcquireGlobalLock ( IN PDEVICE_OBJECT LowerDeviceObject, OUT PACPI_MANIPULATE_GLOBAL_LOCK_BUFFER GlobalLock ) /*++
Routine Description:
Call ACPI driver to obtain the global lock
Note: This routine can be called at dispatch level
Arguments:
LowerDeviceObject - The FDO to pass the request to.
Return Value:
Return Value from IOCTL.
--*/ { NTSTATUS status; PIRP irp; PIO_STACK_LOCATION irpSp; KEVENT event;
BattPrint (BAT_TRACE, ("SmbBattAcquireGlobalLock: Entering\n"));
//
// We wish to acquire the lock
//
GlobalLock->Signature = ACPI_ACQUIRE_GLOBAL_LOCK_SIGNATURE; GlobalLock->LockObject = NULL;
//
// setup the irp
//
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoAllocateIrp (LowerDeviceObject->StackSize, FALSE); if (!irp) { return STATUS_INSUFFICIENT_RESOURCES; }
irpSp = IoGetNextIrpStackLocation(irp); irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_ACPI_ACQUIRE_GLOBAL_LOCK; irpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ACPI_MANIPULATE_GLOBAL_LOCK_BUFFER); irpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ACPI_MANIPULATE_GLOBAL_LOCK_BUFFER); irp->AssociatedIrp.SystemBuffer = GlobalLock; IoSetCompletionRoutine (irp, SmbBattSynchronousRequest, &event, TRUE, TRUE, TRUE);
//
// Send to ACPI driver
//
IoCallDriver (LowerDeviceObject, irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = irp->IoStatus.Status; IoFreeIrp (irp);
if (!NT_SUCCESS(status)) { BattPrint( BAT_ERROR, ("SmbBattAcquireGlobalLock: Acquire Lock failed, status = %08x\n", status ) ); DbgBreakPoint (); }
BattPrint (BAT_TRACE, ("SmbBattAcquireGlobalLock: Returning %x\n", status));
return status; }
NTSTATUS SmbBattReleaseGlobalLock ( IN PDEVICE_OBJECT LowerDeviceObject, IN PACPI_MANIPULATE_GLOBAL_LOCK_BUFFER GlobalLock ) /*++
Routine Description:
Call ACPI driver to release the global lock
Arguments:
LowerDeviceObject - The FDO to pass the request to.
Return Value:
Return Value from IOCTL.
--*/ { NTSTATUS status; PIRP irp; PIO_STACK_LOCATION irpSp; KEVENT event;
BattPrint (BAT_TRACE, ("SmbBattReleaseGlobalLock: Entering\n"));
//
// We wish to acquire the lock
//
GlobalLock->Signature = ACPI_RELEASE_GLOBAL_LOCK_SIGNATURE;
//
// setup the irp
//
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoAllocateIrp (LowerDeviceObject->StackSize, FALSE); if (!irp) { return STATUS_INSUFFICIENT_RESOURCES; }
irpSp = IoGetNextIrpStackLocation(irp); irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_ACPI_RELEASE_GLOBAL_LOCK; irpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ACPI_MANIPULATE_GLOBAL_LOCK_BUFFER); irpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ACPI_MANIPULATE_GLOBAL_LOCK_BUFFER); irp->AssociatedIrp.SystemBuffer = GlobalLock; IoSetCompletionRoutine (irp, SmbBattSynchronousRequest, &event, TRUE, TRUE, TRUE);
//
// Send to ACPI driver
//
IoCallDriver (LowerDeviceObject, irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = irp->IoStatus.Status; IoFreeIrp (irp);
if (!NT_SUCCESS(status)) { BattPrint( BAT_ERROR, ("SmbBattReleaseGlobalLock: Acquire Lock failed, status = %08x\n", status ) ); }
BattPrint (BAT_TRACE, ("SmbBattReleaseGlobalLock: Returning %x\n", status));
return status; }
|