KTIMER CmBattWakeDpcTimerObject;
KDPC CmBattWakeDpcObject;
PDEVICE_OBJECT AcAdapterPdo;
+LARGE_INTEGER CmBattWakeDpcDelay;
/* FUNCTIONS ******************************************************************/
VOID
NTAPI
-CmBattPowerCallBack(PCMBATT_DEVICE_EXTENSION DeviceExtension,
- PVOID Argument1,
- PVOID Argument2)
+CmBattPowerCallBack(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
+ IN ULONG Action,
+ IN ULONG Value)
{
- UNIMPLEMENTED;
+ BOOLEAN Cancelled;
+ PDEVICE_OBJECT DeviceObject;
+ if (CmBattDebug & 0x10)
+ DbgPrint("CmBattPowerCallBack: action: %d, value: %d \n", Action, Value);
+
+ /* Check if a transition is going to happen */
+ if (Action == PO_CB_SYSTEM_STATE_LOCK)
+ {
+ /* We have just re-entered S0: call the wake DPC in 10 seconds */
+ if (Value == 1)
+ {
+ if (CmBattDebug & 0x10)
+ DbgPrint("CmBattPowerCallBack: Calling CmBattWakeDpc after 10 seconds.\n");
+ Cancelled = KeSetTimer(&CmBattWakeDpcTimerObject, CmBattWakeDpcDelay, &CmBattWakeDpcObject);
+ if (CmBattDebug & 0x10)
+ DbgPrint("CmBattPowerCallBack: timerCanceled = %d.\n", Cancelled);
+ }
+ else if (Value == 0)
+ {
+ /* We are exiting the S0 state: loop all devices to set the delay flag */
+ if (CmBattDebug & 0x10)
+ DbgPrint("CmBattPowerCallBack: Delaying Notifications\n");
+ for (DeviceObject = DeviceExtension->DeviceObject;
+ DeviceObject;
+ DeviceObject = DeviceObject->NextDevice)
+ {
+ /* Set the delay flag */
+ DeviceExtension = DeviceObject->DeviceExtension;
+ DeviceExtension->DelayNotification = TRUE;
+ }
+ }
+ else if (CmBattDebug & 0x10)
+ {
+ /* Unknown value */
+ DbgPrint("CmBattPowerCallBack: unknown argument2 = %08x\n");
+ }
+ }
}
VOID
NTAPI
-CmBattWakeDpc(PKDPC Dpc,
- PCMBATT_DEVICE_EXTENSION FdoExtension,
- PVOID SystemArgument1,
- PVOID SystemArgument2)
+CmBattWakeDpc(IN PKDPC Dpc,
+ IN PCMBATT_DEVICE_EXTENSION FdoExtension,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2)
{
+ PDEVICE_OBJECT CurrentObject;
+ BOOLEAN AcNotify = FALSE;
+ PCMBATT_DEVICE_EXTENSION DeviceExtension;
+ ULONG ArFlag;
+ if (CmBattDebug & 2) DbgPrint("CmBattWakeDpc: Entered.\n");
+
+ /* Loop all device objects */
+ for (CurrentObject = FdoExtension->DeviceObject;
+ CurrentObject;
+ CurrentObject = CurrentObject->NextDevice)
+ {
+ /* Turn delay flag off, we're back in S0 */
+ DeviceExtension = CurrentObject->DeviceExtension;
+ DeviceExtension->DelayNotification = 0;
+
+ /* Check if this is an AC adapter */
+ if (DeviceExtension->FdoType == CmBattAcAdapter)
+ {
+ /* Was there a pending notify? */
+ if (DeviceExtension->ArFlag & CMBATT_AR_NOTIFY)
+ {
+ /* We'll send a notify on the next pass */
+ AcNotify = TRUE;
+ DeviceExtension->ArFlag = 0;
+ if (CmBattDebug & 0x20)
+ DbgPrint("CmBattWakeDpc: AC adapter notified\n");
+ }
+ }
+ }
+
+ /* Loop the device objects again */
+ for (CurrentObject = FdoExtension->DeviceObject;
+ CurrentObject;
+ CurrentObject = CurrentObject->NextDevice)
+ {
+ /* Check if this is a battery */
+ DeviceExtension = CurrentObject->DeviceExtension;
+ if (DeviceExtension->FdoType == CmBattBattery)
+ {
+ /* Check what ARs are pending */
+ ArFlag = DeviceExtension->ArFlag;
+ if (CmBattDebug & 0x20)
+ DbgPrint("CmBattWakeDpc: Performing delayed ARs: %01x\n", ArFlag);
+
+ /* Insert notification, clear the lock value */
+ if (ArFlag & CMBATT_AR_INSERT) InterlockedExchange(&DeviceExtension->ArLockValue, 0);
+
+ /* Removal, clear the battery tag */
+ if (ArFlag & CMBATT_AR_REMOVE) DeviceExtension->Tag = 0;
+ /* Notification (or AC/DC adapter change from first pass above) */
+ if ((ArFlag & CMBATT_AR_NOTIFY) || (AcNotify))
+ {
+ /* Notify the class driver */
+ BatteryClassStatusNotify(DeviceExtension->ClassData);
+ }
+ }
+ }
}
VOID
if (DeviceExtension->FdoType == CmBattBattery)
{
/* Reset the current trip point */
- DeviceExtension->TripPointValue = 0xFFFFFFFF;
+ DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
/* Check what ARs have to be done */
ArFlag = DeviceExtension->ArFlag;
BatteryClassStatusNotify(DeviceExtension->ClassData);
}
}
- else
+ else if (DeviceExtension->ArFlag & CMBATT_AR_NOTIFY)
{
- /* The only known notification is AC/DC change */
- if (DeviceExtension->ArFlag & CMBATT_AR_NOTIFY)
+ /* The only known notification is AC/DC change. Loop device objects. */
+ for (DeviceObject = DeviceExtension->FdoDeviceObject->DriverObject->DeviceObject;
+ DeviceObject;
+ DeviceObject = DeviceObject->NextDevice)
{
- for (DeviceObject = DeviceExtension->FdoDeviceObject->DriverObject->DeviceObject;
- DeviceObject;
- DeviceObject = DeviceObject->NextDevice)
+ /* Is this a battery? */
+ FdoExtension = DeviceObject->DeviceExtension;
+ if (FdoExtension->FdoType == CmBattBattery)
{
- /* Is this a battery? */
- FdoExtension = DeviceObject->DeviceExtension;
- if (FdoExtension->FdoType == CmBattBattery)
- {
- /* Send a notification to the class driver */
- FdoExtension->NotifySent = TRUE;
- BatteryClassStatusNotify(FdoExtension->ClassData);
- }
+ /* Send a notification to the class driver */
+ FdoExtension->NotifySent = TRUE;
+ BatteryClassStatusNotify(FdoExtension->ClassData);
}
}
}
NTSTATUS
NTAPI
-CmBattIoctl(PDEVICE_OBJECT DeviceObject,
- PIRP Irp)
+CmBattIoctl(IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp)
{
- UNIMPLEMENTED;
- return STATUS_NOT_IMPLEMENTED;
+ PCMBATT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IoStackLocation;
+ ULONG IoControlCode, OutputBufferLength, InputBufferLength;
+ PAGED_CODE();
+ if (CmBattDebug & 2) DbgPrint("CmBattIoctl\n");
+
+ /* Acquire the remove lock */
+ Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, 0);
+ if (!NT_SUCCESS(Status))
+ {
+ /* It's too late, fail */
+ Irp->IoStatus.Status = STATUS_DEVICE_REMOVED;
+ IofCompleteRequest(Irp, IO_NO_INCREMENT);
+ return STATUS_DEVICE_REMOVED;
+ }
+
+ /* There's nothing to do for an AC adapter */
+ if (DeviceExtension->FdoType == CmBattAcAdapter)
+ {
+ /* Pass it down, and release the remove lock */
+ IoSkipCurrentIrpStackLocation(Irp);
+ Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
+ IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
+ return Status;
+ }
+
+ /* Send to class driver */
+ Status = BatteryClassIoctl(DeviceExtension->ClassData, Irp);
+ if (Status == STATUS_NOT_SUPPORTED)
+ {
+ /* Read IOCTL information from IRP stack */
+ IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
+ IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;
+ OutputBufferLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
+ InputBufferLength = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
+ if (CmBattDebug & 4)
+ DbgPrint("CmBattIoctl: Received Direct Access IOCTL %x\n", IoControlCode);
+
+ /* Handle internal IOCTLs */
+ switch (IoControlCode)
+ {
+ case IOCTL_BATTERY_QUERY_UNIQUE_ID:
+
+ /* Data is 4 bytes long */
+ if (OutputBufferLength == sizeof(ULONG))
+ {
+ /* Query it */
+ Status = CmBattGetUniqueId(DeviceExtension->PdoDeviceObject,
+ Irp->AssociatedIrp.SystemBuffer);
+ if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
+ }
+ else
+ {
+ /* Buffer size invalid */
+ Status = STATUS_INVALID_BUFFER_SIZE;
+ }
+ break;
+
+ case IOCTL_BATTERY_QUERY_STA:
+
+ /* Data is 4 bytes long */
+ if (OutputBufferLength == sizeof(ULONG))
+ {
+ /* Query it */
+ Status = CmBattGetStaData(DeviceExtension->PdoDeviceObject,
+ Irp->AssociatedIrp.SystemBuffer);
+ if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
+ }
+ else
+ {
+ /* Buffer size invalid */
+ Status = STATUS_INVALID_BUFFER_SIZE;
+ }
+ break;
+
+ case IOCTL_BATTERY_QUERY_PSR:
+
+ /* Data is 4 bytes long */
+ if (OutputBufferLength == sizeof(ULONG))
+ {
+ /* Do we have an AC adapter? */
+ if (AcAdapterPdo)
+ {
+ /* Query it */
+ Status = CmBattGetPsrData(AcAdapterPdo,
+ Irp->AssociatedIrp.SystemBuffer);
+ if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
+ }
+ else
+ {
+ /* No adapter, just a battery, so fail */
+ Status = STATUS_NO_SUCH_DEVICE;
+ }
+ }
+ else
+ {
+ /* Buffer size invalid */
+ Status = STATUS_INVALID_BUFFER_SIZE;
+ }
+ break;
+
+ case IOCTL_BATTERY_SET_TRIP_POINT:
+
+ /* Data is 4 bytes long */
+ if (InputBufferLength == sizeof(ULONG))
+ {
+ /* Query it */
+ Status = CmBattSetTripPpoint(DeviceExtension,
+ *(PULONG)Irp->AssociatedIrp.SystemBuffer);
+ Irp->IoStatus.Information = 0;
+ }
+ else
+ {
+ /* Buffer size invalid */
+ Status = STATUS_INVALID_BUFFER_SIZE;
+ }
+ break;
+
+ case IOCTL_BATTERY_QUERY_BIF:
+
+ /* Data is 1060 bytes long */
+ if (OutputBufferLength == sizeof(ACPI_BIF_DATA))
+ {
+ /* Query it */
+ Status = CmBattGetBifData(DeviceExtension,
+ Irp->AssociatedIrp.SystemBuffer);
+ if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ACPI_BIF_DATA);
+ }
+ else
+ {
+ /* Buffer size invalid */
+ Status = STATUS_INVALID_BUFFER_SIZE;
+ }
+ break;
+
+ case IOCTL_BATTERY_QUERY_BST:
+
+ /* Data is 16 bytes long */
+ if (OutputBufferLength == sizeof(ACPI_BST_DATA))
+ {
+ /* Query it */
+ Status = CmBattGetBstData(DeviceExtension,
+ Irp->AssociatedIrp.SystemBuffer);
+ if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ACPI_BST_DATA);
+ }
+ else
+ {
+ /* Buffer size invalid */
+ Status = STATUS_INVALID_BUFFER_SIZE;
+ }
+ break;
+
+ default:
+
+ /* Unknown, let us pass it on to ACPI */
+ if (CmBattDebug & 0xC)
+ DbgPrint("CmBattIoctl: Unknown IOCTL %x\n", IoControlCode);
+ break;
+ }
+
+ /* Did someone pick it up? */
+ if (Status != STATUS_NOT_SUPPORTED)
+ {
+ /* Complete the request */
+ Irp->IoStatus.Status = Status;
+ IofCompleteRequest(Irp, IO_NO_INCREMENT);
+ }
+ else
+ {
+ /* Still unsupported, try ACPI */
+ IoSkipCurrentIrpStackLocation(Irp);
+ Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
+ }
+ }
+
+ /* Release the remove lock and return status */
+ IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
+ return Status;
}
NTSTATUS
/* Reset trip point data */
DeviceExtension->TripPointOld = 0;
- DeviceExtension->TripPointValue = 0xFFFFFFFF;
+ DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
/* Clear AR lock and set new interrupt time */
InterlockedExchange(&DeviceExtension->ArLockValue, 0);
NTSTATUS
NTAPI
-CmBattDisableStatusNotify(PCMBATT_DEVICE_EXTENSION DeviceExtension)
+CmBattDisableStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension)
{
- UNIMPLEMENTED;
- return STATUS_NOT_IMPLEMENTED;
+ NTSTATUS Status;
+ PAGED_CODE();
+ if (CmBattDebug & 0xA) DbgPrint("CmBattDisableStatusNotify\n");
+
+ /* Do we have a trip point */
+ if (DeviceExtension->TripPointSet)
+ {
+ /* Is there a current value set? */
+ if (DeviceExtension->TripPointValue)
+ {
+ /* Reset it back to 0 */
+ DeviceExtension->TripPointValue = 0;
+ Status = CmBattSetTripPpoint(DeviceExtension, 0);
+ if (!NT_SUCCESS(Status))
+ {
+ /* If it failed, set unknown/invalid value */
+ DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
+ if (CmBattDebug & 8)
+ DbgPrint("CmBattDisableStatusNotify: SetTripPoint failed - %x\n", Status);
+ }
+ }
+ else
+ {
+ /* No trip point set, so this is a successful no-op */
+ Status = STATUS_SUCCESS;
+ }
+ }
+ else
+ {
+ /* Nothing we can do */
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* Return status */
+ return Status;
}
NTSTATUS
NTAPI
-CmBattSetStatusNotify(PCMBATT_DEVICE_EXTENSION DeviceExtension,
- ULONG BatteryTag,
- PBATTERY_NOTIFY BatteryNotify)
+CmBattSetStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
+ IN ULONG BatteryTag,
+ IN PBATTERY_NOTIFY BatteryNotify)
{
- UNIMPLEMENTED;
- return STATUS_NOT_IMPLEMENTED;
+ NTSTATUS Status;
+ ACPI_BST_DATA BstData;
+ ULONG Capacity, NewTripPoint, TripPoint, DesignVoltage;
+ BOOLEAN Charging;
+ PAGED_CODE();
+ if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
+ DbgPrint("CmBattSetStatusNotify: Tag (%d) Target(0x%x)\n",
+ BatteryTag, BatteryNotify->LowCapacity);
+
+ /* Update any ACPI evaluations */
+ Status = CmBattVerifyStaticInfo(DeviceExtension, BatteryTag);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Trip point not supported, fail */
+ if (!DeviceExtension->TripPointSet) return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ /* Are both capacities known? */
+ if ((BatteryNotify->HighCapacity == BATTERY_UNKNOWN_CAPACITY) ||
+ (BatteryNotify->LowCapacity == BATTERY_UNKNOWN_CAPACITY))
+ {
+ /* We can't set trip points without these */
+ if (CmBattDebug & CMBATT_GENERIC_WARNING)
+ DbgPrint("CmBattSetStatusNotify: Failing request because of BATTERY_UNKNOWN_CAPACITY.\n");
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ /* Is the battery charging? */
+ Charging = DeviceExtension->BstData.State & ACPI_BATT_STAT_CHARGING;
+ if (Charging)
+ {
+ /* Then the trip point is when we hit the cap */
+ Capacity = BatteryNotify->HighCapacity;
+ NewTripPoint = BatteryNotify->HighCapacity;
+ }
+ else
+ {
+ /* Otherwise it's when we discharge to the bottom */
+ Capacity = BatteryNotify->LowCapacity;
+ NewTripPoint = BatteryNotify->LowCapacity;
+ }
+
+ /* Do we have data in Amps or Watts? */
+ if (DeviceExtension->BifData.PowerUnit == ACPI_BATT_POWER_UNIT_AMPS)
+ {
+ /* We need the voltage to do the conversion */
+ DesignVoltage = DeviceExtension->BifData.DesignVoltage;
+ if ((DesignVoltage != BATTERY_UNKNOWN_VOLTAGE) && (DesignVoltage))
+ {
+ /* Convert from mAh into Ah */
+ TripPoint = 1000 * NewTripPoint;
+ if (Charging)
+ {
+ /* Scale the high trip point */
+ NewTripPoint = (TripPoint + 500) / DesignVoltage + ((TripPoint + 500) % DesignVoltage != 0);
+ }
+ else
+ {
+ /* Scale the low trip point */
+ NewTripPoint = (TripPoint - 500) / DesignVoltage - ((TripPoint - 500) % DesignVoltage == 0);
+ }
+ }
+ else
+ {
+ /* Without knowing the voltage, Amps are not enough data on consumption */
+ Status = STATUS_NOT_SUPPORTED;
+ if (CmBattDebug & CMBATT_ACPI_WARNING)
+ DbgPrint("CmBattSetStatusNotify: Can't calculate BTP, DesignVoltage = 0x%08x\n",
+ DesignVoltage);
+ }
+ }
+ else if (Charging)
+ {
+ /* Make it trip just one past the charge cap */
+ ++NewTripPoint;
+ }
+ else if (NewTripPoint > 0)
+ {
+ /* Make it trip just one below the drain cap */
+ --NewTripPoint;
+ }
+
+ /* Do we actually have a new trip point? */
+ if (NewTripPoint == DeviceExtension->TripPointValue)
+ {
+ /* No, so there is no work to be done */
+ if (CmBattDebug & CMBATT_GENERIC_STATUS)
+ DbgPrint("CmBattSetStatusNotify: Keeping original setting: %X\n", DeviceExtension->TripPointValue);
+ return STATUS_SUCCESS;
+ }
+
+ /* Set the trip point with ACPI and check for success */
+ DeviceExtension->TripPointValue = NewTripPoint;
+ Status = CmBattSetTripPpoint(DeviceExtension, NewTripPoint);
+ if (!(NewTripPoint) && (Capacity)) Status = STATUS_NOT_SUPPORTED;
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed to set the trip point, or there wasn't one settable */
+ DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
+ if (CmBattDebug & (CMBATT_GENERIC_WARNING | CMBATT_ACPI_WARNING))
+ DbgPrint("CmBattSetStatusNotify: SetTripPoint failed - %x\n", Status);
+ return Status;
+ }
+
+ /* Read the new BST data to see the latest state */
+ Status = CmBattGetBstData(DeviceExtension, &BstData);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We'll return failure to the caller */
+ if (CmBattDebug & (CMBATT_GENERIC_WARNING | CMBATT_ACPI_WARNING))
+ DbgPrint("CmBattSetStatusNotify: GetBstData - %x\n", Status);
+ }
+ else if ((Charging) && (BstData.RemainingCapacity >= NewTripPoint))
+ {
+ /* We are charging and our capacity is past the trip point, so trip now */
+ if (CmBattDebug & CMBATT_GENERIC_WARNING)
+ DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
+ NewTripPoint, BstData.RemainingCapacity);
+ CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
+ }
+ else if ((BstData.RemainingCapacity) && (Capacity))
+ {
+ /* We are discharging, and our capacity is below the trip point, trip now */
+ if (CmBattDebug & CMBATT_GENERIC_WARNING)
+ DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
+ NewTripPoint, BstData.RemainingCapacity);
+ CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
+ }
+
+ /* All should've went well if we got here, unless BST failed... return! */
+ if (CmBattDebug & CMBATT_GENERIC_STATUS)
+ DbgPrint("CmBattSetStatusNotify: Want %X CurrentCap %X\n",
+ Capacity, DeviceExtension->RemainingCapacity);
+ if (CmBattDebug & CMBATT_ACPI_WARNING)
+ DbgPrint("CmBattSetStatusNotify: Set to: [%#08lx][%#08lx][%#08lx] Status %x\n",
+ BatteryNotify->PowerState,
+ BatteryNotify->LowCapacity,
+ BatteryNotify->HighCapacity);
+ return Status;
}
NTSTATUS
RegistryPath->Buffer);
/* Setup the major dispatchers */
- DriverObject->MajorFunction[0] = CmBattOpenClose;
- DriverObject->MajorFunction[2] = CmBattOpenClose;
- DriverObject->MajorFunction[14] = CmBattIoctl;
- DriverObject->MajorFunction[22] = CmBattPowerDispatch;
- DriverObject->MajorFunction[27] = CmBattPnpDispatch;
- DriverObject->MajorFunction[23] = CmBattSystemControl;
+ DriverObject->MajorFunction[IRP_MJ_CREATE] = CmBattOpenClose;
+ DriverObject->MajorFunction[IRP_MJ_CLOSE] = CmBattOpenClose;
+ DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CmBattIoctl;
+ DriverObject->MajorFunction[IRP_MJ_POWER] = CmBattPowerDispatch;
+ DriverObject->MajorFunction[IRP_MJ_PNP] = CmBattPnpDispatch;
+ DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = CmBattSystemControl;
/* And the unload routine */
DriverObject->DriverUnload = CmBattUnload;