/*++ Copyright (c) 1990 Microsoft Corporation Module Name: pnppower.c Abstract: SMBus Smart Battery Subsystem Miniport Driver (Selector, Battery, Charger) Plug and Play and Power Management IRP dispatch routines. Author: Scott Brenden Environment: Notes: Revision History: Chris Windle 1/27/98 Bug Fixes --*/ #include "smbbattp.h" #include #include #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,SmbBattPnpDispatch) #pragma alloc_text(PAGE,SmbBattPowerDispatch) #pragma alloc_text(PAGE,SmbBattRegisterForAlarm) #pragma alloc_text(PAGE,SmbBattUnregisterForAlarm) #pragma alloc_text(PAGE,SmbGetSBS) #pragma alloc_text(PAGE,SmbGetGLK) #pragma alloc_text(PAGE,SmbBattCreatePdos) #pragma alloc_text(PAGE,SmbBattBuildDeviceRelations) #pragma alloc_text(PAGE,SmbBattQueryDeviceRelations) #pragma alloc_text(PAGE,SmbBattRemoveDevice) #pragma alloc_text(PAGE,SmbBattQueryId) #pragma alloc_text(PAGE,SmbBattQueryCapabilities) #pragma alloc_text(PAGE,SmbBattBuildSelectorStruct) #endif NTSTATUS SmbBattPnpDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This is a dispatch routine for the IRPs that come to the driver with the IRP_MJ_PNP major code (plug-and-play IRPs). Arguments: DeviceObject - Pointer to the device object for this device Irp - Pointer to the IRP for the current request Return Value: The function value is the final status of the call --*/ { PSMB_BATT smbBatt; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status; BOOLEAN complete = TRUE; KEVENT syncEvent; // // This routine handles PnP IRPs for three different types of device objects: // the battery subsystem FDO, each battery PDO and each battery PDO. The // subsystem PDO creates children since each battery in the system must have // it's own device object, so this driver is essential two device drivers in // one: the smart battery selector bus driver (It is actually it's own bus // because the selector arbitrates between the two batteries.) and the // battery function driver. The two drivers are integrated because it would // not make sense to replace one and not the other, and having separate // drivers would require additional defined interfaces between them. // // The device extensions for the three device types are different structures. // PSMB_BATT_SUBSYSTEM subsystemExt = (PSMB_BATT_SUBSYSTEM) DeviceObject->DeviceExtension; PSMB_NP_BATT batteryExt = (PSMB_NP_BATT) DeviceObject->DeviceExtension; PDEVICE_OBJECT lowerDevice = NULL; PAGED_CODE(); status = IoAcquireRemoveLock (&batteryExt->RemoveLock, Irp); if (NT_SUCCESS(status)) { status = STATUS_NOT_SUPPORTED; if (batteryExt->SmbBattFdoType == SmbTypeSubsystem) { lowerDevice = subsystemExt->LowerDevice; } else if (batteryExt->SmbBattFdoType == SmbTypeBattery) { lowerDevice = batteryExt->LowerDevice; } else { // Assuming (batteryExt->SmbBattFdoType == SmbTypePdo) ASSERT (batteryExt->SmbBattFdoType == SmbTypePdo); lowerDevice = NULL; } switch (irpStack->MinorFunction) { case IRP_MN_QUERY_DEVICE_RELATIONS: { BattPrint( BAT_IRPS, ("SmbBattPnpDispatch: got IRP_MN_QUERY_DEVICE_RELATIONS, " "type = %x\n", irpStack->Parameters.QueryDeviceRelations.Type) ); status = SmbBattQueryDeviceRelations (DeviceObject, Irp); break; } case IRP_MN_QUERY_CAPABILITIES: { BattPrint( BAT_IRPS, ("SmbBattPnpDispatch: got IRP_MN_QUERY_CAPABILITIES for device %x\n", DeviceObject) ); status = SmbBattQueryCapabilities (DeviceObject, Irp); break; } case IRP_MN_START_DEVICE: { BattPrint( BAT_IRPS, ("SmbBattPnpDispatch: got IRP_MN_START_DEVICE for %x\n", DeviceObject) ); if (subsystemExt->SmbBattFdoType == SmbTypeSubsystem) { // // Get the SMB host controller FDO // subsystemExt->SmbHcFdo = subsystemExt->LowerDevice; status = STATUS_SUCCESS; } else if (subsystemExt->SmbBattFdoType == SmbTypeBattery) { // // This is a battery. Just get the SMB host controller FDO. // smbBatt = batteryExt->Batt; smbBatt->SmbHcFdo = ((PSMB_BATT_SUBSYSTEM)(((PSMB_BATT_PDO) (smbBatt->PDO->DeviceExtension))-> SubsystemFdo->DeviceExtension))-> LowerDevice; status = STATUS_SUCCESS; } else if (subsystemExt->SmbBattFdoType == SmbTypePdo) { status = STATUS_SUCCESS; } break; } case IRP_MN_STOP_DEVICE: { status = STATUS_SUCCESS; BattPrint(BAT_IRPS, ("SmbBattPnpDispatch: got IRP_MN_STOP_DEVICE\n")); break; } case IRP_MN_QUERY_REMOVE_DEVICE: { status = STATUS_SUCCESS; BattPrint(BAT_IRPS, ("SmbBattPnpDispatch: got IRP_MN_QUERY_REMOVE_DEVICE\n")); break; } case IRP_MN_CANCEL_REMOVE_DEVICE: { status = STATUS_SUCCESS; BattPrint(BAT_IRPS, ("SmbBattPnpDispatch: got IRP_MN_CANCEL_REMOVE_DEVICE\n")); break; } case IRP_MN_SURPRISE_REMOVAL: { status = STATUS_SUCCESS; BattPrint(BAT_IRPS, ("SmbBattPnpDispatch: got IRP_MN_SURPRISE_REMOVAL\n")); break; } case IRP_MN_REMOVE_DEVICE: { BattPrint(BAT_IRPS, ("SmbBattPnpDispatch: got IRP_MN_REMOVE_DEVICE\n")); status = SmbBattRemoveDevice (DeviceObject, Irp); return status; break; } case IRP_MN_QUERY_ID: { BattPrint( BAT_IRPS, ("SmbBattPnpDispatch: got IRP_MN_QUERY_ID for %x, query type is - %x\n", DeviceObject, irpStack->Parameters.QueryId.IdType) ); if (batteryExt->SmbBattFdoType == SmbTypePdo) { status = SmbBattQueryId (DeviceObject, Irp); } break; } case IRP_MN_QUERY_PNP_DEVICE_STATE: { BattPrint(BAT_IRPS, ("SmbBattPnpDispatch: got IRP_MN_PNP_DEVICE_STATE\n")); if (subsystemExt->SmbBattFdoType == SmbTypeSubsystem) { IoCopyCurrentIrpStackLocationToNext (Irp); KeInitializeEvent(&syncEvent, SynchronizationEvent, FALSE); IoSetCompletionRoutine(Irp, SmbBattSynchronousRequest, &syncEvent, TRUE, TRUE, TRUE); status = IoCallDriver(lowerDevice, Irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&syncEvent, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; } Irp->IoStatus.Information &= ~PNP_DEVICE_NOT_DISABLEABLE; IoCompleteRequest(Irp, IO_NO_INCREMENT); IoReleaseRemoveLock (&batteryExt->RemoveLock, Irp); return status; } break; } } // switch (irpStack->MinorFunction) IoReleaseRemoveLock (&batteryExt->RemoveLock, Irp); } // // Only set status if we have something to add // if (status != STATUS_NOT_SUPPORTED) { Irp->IoStatus.Status = status ; } // // Do we need to send it down? // if ((NT_SUCCESS(status) || (status == STATUS_NOT_SUPPORTED)) && (lowerDevice != NULL)) { // // Forward request // IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(lowerDevice,Irp); } else { // // Complete the request with the current status // status = Irp->IoStatus.Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } return status; } NTSTATUS SmbBattPowerDispatch( IN PDEVICE_OBJECT Fdo, IN PIRP Irp ) /*++ Routine Description: This is a dispatch routine for the IRPs that come to the driver with the IRP_MJ_POWER major code (power IRPs). Arguments: DeviceObject - Pointer to the device object for this device Irp - Pointer to the IRP for the current request Return Value: The function value is the final status of the call --*/ { PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = STATUS_NOT_SUPPORTED; PSMB_NP_BATT batteryExt = (PSMB_NP_BATT) Fdo->DeviceExtension; PSMB_BATT_SUBSYSTEM subsystemExt = (PSMB_BATT_SUBSYSTEM) Fdo->DeviceExtension; PDEVICE_OBJECT lowerDevice; PAGED_CODE(); // // Not using Remove lock in this function because this function doesn't use // any resoureces that the remove lock protects. // if (batteryExt->SmbBattFdoType == SmbTypeSubsystem) { lowerDevice = subsystemExt->LowerDevice; } else if (batteryExt->SmbBattFdoType == SmbTypeBattery) { lowerDevice = batteryExt->LowerDevice; } else { // Assuming (batteryExt->SmbBattFdoType == SmbTypePdo) ASSERT (batteryExt->SmbBattFdoType == SmbTypePdo); lowerDevice = NULL; status = STATUS_SUCCESS; } switch (irpStack->MinorFunction) { case IRP_MN_WAIT_WAKE: { BattPrint(BAT_IRPS, ("SmbBattPowerDispatch: got IRP_MN_WAIT_WAKE\n")); // // Smart batteries can't wake the system. // status = STATUS_NOT_SUPPORTED; break; } case IRP_MN_POWER_SEQUENCE: { BattPrint(BAT_IRPS, ("SmbBattPowerDispatch: got IRP_MN_POWER_SEQUENCE\n")); break; } case IRP_MN_SET_POWER: { BattPrint(BAT_IRPS, ("SmbBattPowerDispatch: got IRP_MN_SET_POWER\n")); break; } case IRP_MN_QUERY_POWER: { BattPrint(BAT_IRPS, ("SmbBattPowerDispatch: got IRP_MN_QUERY_POWER\n")); break; } default: { status = STATUS_NOT_SUPPORTED; } } // switch (irpStack->MinorFunction) if (status != STATUS_NOT_SUPPORTED) { Irp->IoStatus.Status = status; } PoStartNextPowerIrp( Irp ); if ((NT_SUCCESS(status) || (status == STATUS_NOT_SUPPORTED)) && (lowerDevice != NULL)) { // // Forward the request along // IoSkipCurrentIrpStackLocation( Irp ); status = PoCallDriver( lowerDevice, Irp ); } else { // // Complete the request with the current status // status = Irp->IoStatus.Status; IoCompleteRequest( Irp, IO_NO_INCREMENT ); } return status; } NTSTATUS SmbBattRegisterForAlarm( IN PDEVICE_OBJECT Fdo ) /*++ Routine Description: This routine register with the SmbHc for alarm notifications. This is only done when smart battery subsystem FDO is started. Arguments: Fdo - Pointer to the Fdo for this device Irp - Pointer to the IRP for the current request Return Value: The function value is the final status of the call --*/ { PIRP irp; PIO_STACK_LOCATION irpStack; SMB_REGISTER_ALARM registerAlarm; KEVENT event; NTSTATUS status; PSMB_BATT_SUBSYSTEM subsystemExtension = (PSMB_BATT_SUBSYSTEM) Fdo->DeviceExtension; PAGED_CODE(); // // Register for alarm notifications // registerAlarm.MinAddress = SMB_CHARGER_ADDRESS; registerAlarm.MaxAddress = SMB_BATTERY_ADDRESS; registerAlarm.NotifyFunction = SmbBattAlarm; registerAlarm.NotifyContext = subsystemExtension; KeInitializeEvent (&event, NotificationEvent, FALSE); irp = IoAllocateIrp (subsystemExtension->SmbHcFdo->StackSize, FALSE); if (!irp) { BattPrint(BAT_ERROR, ("SmbBattRegisterForAlarm: couldn't allocate irp\n")); return STATUS_INSUFFICIENT_RESOURCES; } irpStack = IoGetNextIrpStackLocation(irp); irp->UserBuffer = &subsystemExtension->SmbAlarmHandle; irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; irpStack->Parameters.DeviceIoControl.IoControlCode = SMB_REGISTER_ALARM_NOTIFY; irpStack->Parameters.DeviceIoControl.InputBufferLength = sizeof(registerAlarm); irpStack->Parameters.DeviceIoControl.Type3InputBuffer = ®isterAlarm; irpStack->Parameters.DeviceIoControl.OutputBufferLength = sizeof(subsystemExtension->SmbAlarmHandle); IoSetCompletionRoutine (irp, SmbBattSynchronousRequest, &event, TRUE, TRUE, TRUE); IoCallDriver (subsystemExtension->SmbHcFdo, irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = irp->IoStatus.Status; if (!NT_SUCCESS(status)) { BattPrint(BAT_ERROR, ("SmbBattRegisterForAlarm: couldn't register for alarms - %x\n", status)); } IoFreeIrp (irp); return status; } NTSTATUS SmbBattUnregisterForAlarm( IN PDEVICE_OBJECT Fdo ) /*++ Routine Description: This routine unregisters with the SmbHc for alarm notifications. This is only done when smart battery subsystem FDO is stopped or unloaded. Arguments: Fdo - Pointer to the Fdo for this device Irp - Pointer to the IRP for the current request Return Value: The function value is the final status of the call --*/ { PIRP irp; PIO_STACK_LOCATION irpStack; KEVENT event; NTSTATUS status; PSMB_BATT_SUBSYSTEM subsystemExtension = (PSMB_BATT_SUBSYSTEM) Fdo->DeviceExtension; PAGED_CODE(); // // DeRegister for alarm notifications // KeInitializeEvent (&event, NotificationEvent, FALSE); irp = IoAllocateIrp (subsystemExtension->SmbHcFdo->StackSize, FALSE); if (!irp) { BattPrint(BAT_ERROR, ("SmbBattUnregisterForAlarm: couldn't allocate irp\n")); return STATUS_INSUFFICIENT_RESOURCES; } irpStack = IoGetNextIrpStackLocation(irp); irp->UserBuffer = NULL; irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; irpStack->Parameters.DeviceIoControl.IoControlCode = SMB_DEREGISTER_ALARM_NOTIFY; irpStack->Parameters.DeviceIoControl.InputBufferLength = sizeof(subsystemExtension->SmbAlarmHandle); irpStack->Parameters.DeviceIoControl.Type3InputBuffer = &subsystemExtension->SmbAlarmHandle; irpStack->Parameters.DeviceIoControl.OutputBufferLength = 0; IoSetCompletionRoutine (irp, SmbBattSynchronousRequest, &event, TRUE, TRUE, TRUE); IoCallDriver (subsystemExtension->SmbHcFdo, irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = irp->IoStatus.Status; if (!NT_SUCCESS(status)) { BattPrint(BAT_ERROR, ("SmbBattUnregisterForAlarm: couldn't unregister for alarms - %x\n", status)); } IoFreeIrp (irp); return status; } NTSTATUS SmbGetSBS ( IN PULONG NumberOfBatteries, IN PBOOLEAN SelectorPresent, IN PDEVICE_OBJECT LowerDevice ) /*++ Routine Description: This routine has the ACPI driver run the control method _SBS for the smart battery subsystem. This control method returns a value that tells the driver how many batteries are supported and whether or not the system contains a selector. Arguments: NumberOfBatteries - pointer to return the number of batteries SelectorPresent - Pointer to return whether a selector is present (TRUE) LowerDevice - device object to call Return Value: Status of the IOCTL to the ACPI driver. --*/ { ACPI_EVAL_INPUT_BUFFER inputBuffer; ACPI_EVAL_OUTPUT_BUFFER outputBuffer; KEVENT event; IO_STATUS_BLOCK ioStatusBlock; NTSTATUS status; PACPI_METHOD_ARGUMENT argument; PIRP irp; PAGED_CODE(); BattPrint (BAT_TRACE, ("SmbGetSBS: Entering\n")); // // Initialize the input structure // RtlZeroMemory( &inputBuffer, sizeof(ACPI_EVAL_INPUT_BUFFER) ); inputBuffer.MethodNameAsUlong = SMBATT_SBS_METHOD; inputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE; // // Set the event object to the unsignaled state. // It will be used to signal request completion. // KeInitializeEvent(&event, NotificationEvent, FALSE); // // Build synchronous request with no transfer. // irp = IoBuildDeviceIoControlRequest( IOCTL_ACPI_ASYNC_EVAL_METHOD, LowerDevice, &inputBuffer, sizeof(ACPI_EVAL_INPUT_BUFFER), &outputBuffer, sizeof(ACPI_EVAL_OUTPUT_BUFFER), FALSE, &event, &ioStatusBlock ); if (irp == NULL) { BattPrint (BAT_ERROR, ("SmbGetSBS: couldn't create Irp\n")); return STATUS_INSUFFICIENT_RESOURCES; } // // Pass request to port driver and wait for request to complete. // status = IoCallDriver (LowerDevice, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatusBlock.Status; } // // Sanity check the data // if (outputBuffer.Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE || outputBuffer.Count == 0) { status = STATUS_ACPI_INVALID_DATA; } *SelectorPresent = FALSE; *NumberOfBatteries = 0; if (!NT_SUCCESS(status)) { BattPrint (BAT_BIOS_ERROR | BAT_ERROR, ("SmbGetSBS: Irp failed - %x\n", status)); } else { argument = outputBuffer.Argument; if (argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) { return STATUS_ACPI_INVALID_DATA; } switch (argument->Argument) { case 0: BattPrint(BAT_DATA, ("SmbGetSBS: Number of batteries = 1, no selector\n")); *NumberOfBatteries = 1; break; case 1: case 2: case 3: case 4: BattPrint(BAT_DATA, ("SmbGetSBS: Number of batteries found - %x\n", argument->Argument)); *SelectorPresent = TRUE; *NumberOfBatteries = argument->Argument; break; default: BattPrint(BAT_ERROR, ("SmbGetSBS: Invalid number of batteries - %x\n", argument->Argument)); return STATUS_NO_SUCH_DEVICE; } } return status; } NTSTATUS SmbGetGLK ( IN PBOOLEAN GlobalLockRequired, IN PDEVICE_OBJECT LowerDevice ) /*++ Routine Description: This routine has the ACPI driver run the control method _SBS for the smart battery subsystem. This control method returns a value that tells the driver how many batteries are supported and whether or not the system contains a selector. Arguments: GlobalLockRequired - Pointer to return whether lock acquisition is needed LowerDevice - device object to call Return Value: Status of the IOCTL to the ACPI driver. --*/ { ACPI_EVAL_INPUT_BUFFER inputBuffer; ACPI_EVAL_OUTPUT_BUFFER outputBuffer; KEVENT event; IO_STATUS_BLOCK ioStatusBlock; NTSTATUS status; PACPI_METHOD_ARGUMENT argument; PIRP irp; PAGED_CODE(); BattPrint (BAT_TRACE, ("SmbGetGLK: Entering\n")); // // Initialize the input structure // RtlZeroMemory( &inputBuffer, sizeof(ACPI_EVAL_INPUT_BUFFER) ); inputBuffer.MethodNameAsUlong = SMBATT_GLK_METHOD; inputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE; // // Set the event object to the unsignaled state. // It will be used to signal request completion. // KeInitializeEvent(&event, NotificationEvent, FALSE); // // Build synchronous request with no transfer. // irp = IoBuildDeviceIoControlRequest( IOCTL_ACPI_ASYNC_EVAL_METHOD, LowerDevice, &inputBuffer, sizeof(ACPI_EVAL_INPUT_BUFFER), &outputBuffer, sizeof(ACPI_EVAL_OUTPUT_BUFFER), FALSE, &event, &ioStatusBlock ); if (irp == NULL) { BattPrint (BAT_ERROR, ("SmbGetGLK: couldn't create Irp\n")); return STATUS_INSUFFICIENT_RESOURCES; } // // Pass request to port driver and wait for request to complete. // status = IoCallDriver (LowerDevice, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatusBlock.Status; } if (!NT_SUCCESS(status)) { if (status == STATUS_OBJECT_NAME_NOT_FOUND) { status = STATUS_SUCCESS; *GlobalLockRequired = FALSE; BattPrint (BAT_NOTE, ("SmbGetGLK: _GLK not found assuming lock is not needed.\n")); } else { BattPrint (BAT_ERROR, ("SmbGetGLK: Irp failed - %x\n", status)); } } else { // // Sanity check the data // if (outputBuffer.Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE || outputBuffer.Count == 0) { return STATUS_ACPI_INVALID_DATA; } argument = outputBuffer.Argument; if (argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) { return STATUS_ACPI_INVALID_DATA; } if (argument->Argument == 0) { *GlobalLockRequired = FALSE; } else if (argument->Argument == 1) { *GlobalLockRequired = TRUE; } else { BattPrint(BAT_BIOS_ERROR, ("SmbGetGLK: Invalid value returned - %x\n", argument->Argument)); status = STATUS_UNSUCCESSFUL; } } BattPrint (BAT_DATA, ("SmbGetGLK: Returning %x GLK = %d\n", status, SmbBattUseGlobalLock)); return status; } NTSTATUS SmbBattCreatePdos( IN PDEVICE_OBJECT SubsystemFdo ) /*++ Routine Description: This routine creates a PDO for each battery supported by the system and puts it into a list kept with the FDO for the smart battery subsystem. Arguments: SubsystemFdo - Fdo for the smart battery subsystem Return Value: Status for creation of battery PDO. --*/ { ULONG i; NTSTATUS status; PSMB_BATT_PDO batteryPdoExt; UNICODE_STRING numberString; WCHAR numberBuffer[10]; PDEVICE_OBJECT pdo; PSMB_BATT_SUBSYSTEM subsystemExt = (PSMB_BATT_SUBSYSTEM) SubsystemFdo->DeviceExtension; BOOLEAN selectorPresent = FALSE; PAGED_CODE(); // // Find out if there are multiple batteries and a selector on this machine. // status = SmbGetSBS ( &subsystemExt->NumberOfBatteries, &subsystemExt->SelectorPresent, subsystemExt->LowerDevice ); if (!NT_SUCCESS(status)) { BattPrint(BAT_ERROR, ("SmbBattCreatePdos: error reading SBS\n")); return status; } status = SmbGetGLK ( &SmbBattUseGlobalLock, subsystemExt->LowerDevice ); if (!NT_SUCCESS(status)) { BattPrint(BAT_ERROR, ("SmbBattCreatePdos: error reading GLK\n")); // // If this failed, ignore the failure and continue. This is not critical. // } // // Build the selector information structure // // Adjust Number of Batteries to Match SelectorInfo Supported Batteries // Just in case the ACPI information is incorrect status = SmbBattBuildSelectorStruct (SubsystemFdo); if (!NT_SUCCESS (status)) { BattPrint(BAT_ERROR, ("SmbBattCreatePdos: couldn't talk to the selector\n")); return status; } // // Build device object for each battery // for (i = 0; i < subsystemExt->NumberOfBatteries; i++) { // // Create the device object // status = IoCreateDevice( SubsystemFdo->DriverObject, sizeof (SMB_BATT_PDO), NULL, FILE_DEVICE_BATTERY, FILE_DEVICE_SECURE_OPEN|FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &pdo ); if (status != STATUS_SUCCESS) { BattPrint(BAT_ERROR, ("SmbBattCreatePdos: error creating battery pdo %x\n", status)); // // Make sure we don't later try to use devices that weren't created. // subsystemExt->NumberOfBatteries = i; return(status); } // // Initialize the Pdo // pdo->Flags |= DO_BUFFERED_IO; pdo->Flags |= DO_POWER_PAGABLE; // // Save the PDO in the subsystem FDO PDO list // subsystemExt->BatteryPdoList[i] = pdo; // // Initialize the PDO extension // batteryPdoExt = (PSMB_BATT_PDO) pdo->DeviceExtension; batteryPdoExt->SmbBattFdoType = SmbTypePdo; batteryPdoExt->DeviceObject = pdo; batteryPdoExt->BatteryNumber = i; batteryPdoExt->SubsystemFdo = SubsystemFdo; IoInitializeRemoveLock (&batteryPdoExt->RemoveLock, SMB_BATTERY_TAG, REMOVE_LOCK_MAX_LOCKED_MINUTES, REMOVE_LOCK_HIGH_WATER_MARK); // // Device is ready for use // pdo->Flags &= ~DO_DEVICE_INITIALIZING; } // for (i = 0; i < subsystemExt->NumberOfBatteries; i++) return STATUS_SUCCESS; } NTSTATUS SmbBattBuildDeviceRelations( IN PSMB_BATT_SUBSYSTEM SubsystemExt, IN PDEVICE_RELATIONS *DeviceRelations ) /*++ Routine Description: This routine is checks the device relations structure for existing device relations, calculates how big a new device relations structure has to be, allocates it, and fills it with the PDOs created for the batteries. Arguments: SubsystemExt - Device extension for the smart battery subsystem FDO DeviceRelations - The Current DeviceRelations for the device... Return Value: NTSTATUS --*/ { PDEVICE_RELATIONS newDeviceRelations; ULONG i, j; ULONG newDeviceRelationsSize; NTSTATUS status; ULONG existingPdos = 0; ULONG deviceRelationsSize = 0; ULONG numberOfPdos = 0; PAGED_CODE(); // // Calculate the size the new device relations structure has to be // if (*DeviceRelations != NULL && (*DeviceRelations)->Count > 0) { // // There are existing device relations to be copied // existingPdos = (*DeviceRelations)->Count; deviceRelationsSize = sizeof (ULONG) + (sizeof (PDEVICE_OBJECT) * existingPdos); } // // Calculate the size needed for the new device relations structure and allocate it // numberOfPdos = existingPdos + SubsystemExt->NumberOfBatteries; newDeviceRelationsSize = sizeof (ULONG) + (sizeof (PDEVICE_OBJECT) * numberOfPdos); newDeviceRelations = ExAllocatePoolWithTag (PagedPool, newDeviceRelationsSize, 'StaB'); if (!newDeviceRelations) { BattPrint (BAT_ERROR, ("SmbBattBuildDeviceRelations: couldn't allocate device relations buffer\n")); return STATUS_INSUFFICIENT_RESOURCES; } // // If there are existing device relations copy them to the new device // relations structure and free the old one. // if (existingPdos) { RtlCopyMemory (newDeviceRelations, *DeviceRelations, deviceRelationsSize); } if (*DeviceRelations) { // Could be a zero length list, but still need freeing ExFreePool (*DeviceRelations); } // // Now add the battery PDOs to the end of the list and reference it // for (i = existingPdos, j = 0; i < numberOfPdos; i ++, j ++) { newDeviceRelations->Objects[i] = SubsystemExt->BatteryPdoList[j]; status = ObReferenceObjectByPointer( SubsystemExt->BatteryPdoList[j], 0, NULL, KernelMode ); if (!NT_SUCCESS(status) ) { // // This should theoretically never happen... // BattPrint(BAT_ERROR, ("SmbBattBuildDeviceRelations: error referencing battery pdo %x\n", status)); return status; } } newDeviceRelations->Count = numberOfPdos; *DeviceRelations = newDeviceRelations; return STATUS_SUCCESS; } NTSTATUS SmbBattQueryDeviceRelations( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine handles the IRP_MN_QUERY_DEVICE_RELATIONS. Arguments: Pdo - Battery PDO Irp - The query Irp Return Value: NTSTATUS --*/ { PSMB_NP_BATT SmbNPBatt = (PSMB_NP_BATT) DeviceObject->DeviceExtension; PSMB_BATT_PDO PdoExt = (PSMB_BATT_PDO) DeviceObject->DeviceExtension; PSMB_BATT_SUBSYSTEM SubsystemExt = (PSMB_BATT_SUBSYSTEM) DeviceObject->DeviceExtension; PDEVICE_OBJECT pdo; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = STATUS_NOT_SUPPORTED; ULONG i; PDEVICE_RELATIONS deviceRelations = (PDEVICE_RELATIONS) Irp->IoStatus.Information; PAGED_CODE(); BattPrint(BAT_TRACE, ("SmbBattQueryDeviceRelations: ENTERING\n")); switch (SmbNPBatt->SmbBattFdoType) { case SmbTypeSubsystem: { if (IrpSp->Parameters.QueryDeviceRelations.Type == BusRelations) { BattPrint( BAT_IRPS, ("SmbBattQueryDeviceRelations: Handling Bus relations request\n") ); if (SubsystemExt->NumberOfBatteries != 0) { // // We've already found our batteries, so we don't need to // look again since smart batteries are static. // Just rebuild the return structure. // status = SmbBattBuildDeviceRelations (SubsystemExt, &deviceRelations); } else { status = SmbBattCreatePdos (DeviceObject); if (NT_SUCCESS (status)) { status = SmbBattBuildDeviceRelations (SubsystemExt, &deviceRelations); } if (NT_SUCCESS (status)) { // // Now register for alarms // (Used to register during START_DEVICE, // but don't need notifications until after the batteries // are here. This avoids some other problems too.) status = SmbBattRegisterForAlarm (DeviceObject); } } Irp->IoStatus.Information = (ULONG_PTR) deviceRelations; } break; } case SmbTypeBattery: { status = STATUS_NOT_SUPPORTED; break; } case SmbTypePdo: { if (IrpSp->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation ) { BattPrint( BAT_IRPS, ("SmbBattQueryDeviceRelations: Handling TargetDeviceRelation request\n") ); deviceRelations = ExAllocatePoolWithTag (PagedPool, sizeof(DEVICE_RELATIONS), SMB_BATTERY_TAG); if (!deviceRelations) { return STATUS_INSUFFICIENT_RESOURCES; } status = ObReferenceObjectByPointer(DeviceObject, 0, NULL, KernelMode); if (!NT_SUCCESS(status)) { ExFreePool(deviceRelations); return status; } deviceRelations->Count = 1; deviceRelations->Objects[0] = DeviceObject; Irp->IoStatus.Information = (ULONG_PTR) deviceRelations; } else { status = STATUS_NOT_SUPPORTED; } break; } default: { ASSERT (FALSE); } } return status; } NTSTATUS SmbBattRemoveDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine handles the IRP_MN_REMOVE_DEVICE. Arguments: Pdo - Battery PDO Irp - The query Irp Return Value: NTSTATUS --*/ { PSMB_NP_BATT SmbNPBatt = (PSMB_NP_BATT) DeviceObject->DeviceExtension; PSMB_BATT_PDO PdoExt = (PSMB_BATT_PDO) DeviceObject->DeviceExtension; PSMB_BATT_SUBSYSTEM SubsystemExt = (PSMB_BATT_SUBSYSTEM) DeviceObject->DeviceExtension; PDEVICE_OBJECT pdo; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = STATUS_NOT_SUPPORTED; ULONG i; PAGED_CODE(); BattPrint(BAT_TRACE, ("SmbBattRemoveDevice: ENTERING\n")); switch (SmbNPBatt->SmbBattFdoType) { case SmbTypeSubsystem: { BattPrint(BAT_IRPS, ("SmbBattRemoveDevice: Removing Subsystem FDO.\n")); // // De-register for notifications // SmbBattUnregisterForAlarm (DeviceObject); IoFreeWorkItem (SubsystemExt->WorkerThread); // // Remove PDOs // for (i = 0; i < SubsystemExt->NumberOfBatteries; i++) { pdo = SubsystemExt->BatteryPdoList[i]; if (pdo) { PdoExt = (PSMB_BATT_PDO) pdo->DeviceExtension; status = IoAcquireRemoveLock (&PdoExt->RemoveLock, Irp); IoReleaseRemoveLockAndWait (&PdoExt->RemoveLock, Irp); SubsystemExt->BatteryPdoList[i] = NULL; IoDeleteDevice (pdo); } } if ((SubsystemExt->SelectorPresent) && (SubsystemExt->Selector)) { ExFreePool (SubsystemExt->Selector); } IoSkipCurrentIrpStackLocation (Irp); Irp->IoStatus.Status = STATUS_SUCCESS; status = IoCallDriver (SubsystemExt->LowerDevice, Irp); IoDetachDevice (SubsystemExt->LowerDevice); IoDeleteDevice (DeviceObject); break; } case SmbTypeBattery: { BattPrint(BAT_IRPS, ("SmbBattRemoveDevice: Removing Battery FDO\n")); IoReleaseRemoveLockAndWait (&SmbNPBatt->RemoveLock, Irp); // // Unregister as a WMI Provider. // SmbBattWmiDeRegistration(SmbNPBatt); // // Tell the class driver we are going away // status = BatteryClassUnload (SmbNPBatt->Class); ASSERT (NT_SUCCESS(status)); ExFreePool (SmbNPBatt->Batt); ((PSMB_BATT_PDO) SmbNPBatt->LowerDevice->DeviceExtension)->Fdo = NULL; IoSkipCurrentIrpStackLocation (Irp); Irp->IoStatus.Status = STATUS_SUCCESS; status = IoCallDriver (SmbNPBatt->LowerDevice, Irp); IoDetachDevice (SmbNPBatt->LowerDevice); IoDeleteDevice (DeviceObject); break; } case SmbTypePdo: { BattPrint(BAT_IRPS, ("SmbBattRemoveDevice: Remove for Battery PDO (doing nothing)\n")); // // Don't delete the device until it is physically removed. // Usually, the battery subsystem can't be physically removed... // // // Need to release Remove lock, since PnP dispatch won't... // IoReleaseRemoveLock (&PdoExt->RemoveLock, Irp); status = STATUS_SUCCESS; // // Complete the request with the current status // Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); break; } default: { ASSERT (FALSE); } } BattPrint(BAT_TRACE, ("SmbBattRemoveDevice: EXITING\n")); return status; } NTSTATUS SmbBattQueryId( IN PDEVICE_OBJECT Pdo, IN PIRP Irp ) /*++ Routine Description: This routine handles the IRP_MN_QUERY_ID for the newly created battery PDOs. Arguments: Pdo - Battery PDO Irp - The query Irp Return Value: NTSTATUS --*/ { UNICODE_STRING unicodeString; WCHAR unicodeBuffer[MAX_DEVICE_NAME_LENGTH]; UNICODE_STRING numberString; WCHAR numberBuffer[10]; PSMB_BATT_PDO pdoExt = (PSMB_BATT_PDO) Pdo->DeviceExtension; NTSTATUS status = STATUS_SUCCESS; PWCHAR idString = NULL; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PAGED_CODE(); BattPrint(BAT_TRACE, ("SmbBattQueryId: ENTERING\n")); RtlZeroMemory (unicodeBuffer, MAX_DEVICE_NAME_LENGTH); unicodeString.MaximumLength = MAX_DEVICE_NAME_LENGTH; unicodeString.Length = 0; unicodeString.Buffer = unicodeBuffer; switch (irpStack->Parameters.QueryId.IdType) { case BusQueryDeviceID: // // This string has to have the form BUS\DEVICE. // // Use SMB as bus and SBS as device // RtlAppendUnicodeToString (&unicodeString, SubSystemIdentifier); break; case BusQueryInstanceID: // // Return the string "Batteryxx" where xx is the battery number // numberString.MaximumLength = 10; numberString.Buffer = &numberBuffer[0]; RtlIntegerToUnicodeString (pdoExt->BatteryNumber, 10, &numberString); RtlAppendUnicodeToString (&unicodeString, BatteryInstance); RtlAppendUnicodeToString (&unicodeString, &numberString.Buffer[0]); break; case BusQueryHardwareIDs: // // This is the Pnp ID for the smart battery subsystem "ACPI0002". // Make new hardware ID SMB\SBS, SmartBattery as a MULTIZ string // so we have to add a NULL string to terminate. // RtlAppendUnicodeToString (&unicodeString, HidSmartBattery); unicodeString.Length += sizeof (WCHAR); break; default: // // Unknown Query Type // status = STATUS_NOT_SUPPORTED; } if (status != STATUS_NOT_SUPPORTED) { // // If we created a string, allocate a buffer for it and copy it into the buffer. // We need to make sure that we also copy the NULL terminator. // if (unicodeString.Length) { idString = ExAllocatePoolWithTag (PagedPool, unicodeString.Length + sizeof (WCHAR), 'StaB'); if (!idString) { BattPrint (BAT_ERROR, ("SmbBattQueryId: couldn't allocate id string buffer\n")); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory (idString, unicodeString.Length + sizeof (WCHAR)); RtlCopyMemory (idString, unicodeString.Buffer, unicodeString.Length); } Irp->IoStatus.Status = status; Irp->IoStatus.Information = (ULONG_PTR) idString; } BattPrint(BAT_DATA, ("SmbBattQueryId: returning ID = %x\n", idString)); return status; } NTSTATUS SmbBattQueryCapabilities( IN PDEVICE_OBJECT Pdo, IN PIRP Irp ) /*++ Routine Description: This routine handles the IRP_MN_QUERY_CAPABILITIES for the newly created battery PDOs. Arguments: Pdo - Battery PDO Irp - The query Irp Return Value: NTSTATUS --*/ { PDEVICE_CAPABILITIES deviceCaps; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PAGED_CODE(); deviceCaps = irpStack->Parameters.DeviceCapabilities.Capabilities; if (deviceCaps->Version != 1) { return STATUS_NOT_SUPPORTED; } // // Now set up the bits for the capabilities. // //All bits are initialized to false. Only set bits that we support deviceCaps->SilentInstall = TRUE; // // Now fill in the po manager information. // deviceCaps->SystemWake = PowerSystemUnspecified; deviceCaps->DeviceWake = PowerDeviceUnspecified; deviceCaps->D1Latency = 1; deviceCaps->D2Latency = 1; deviceCaps->D3Latency = 1; return STATUS_SUCCESS; } SmbBattBuildSelectorStruct( IN PDEVICE_OBJECT SubsystemFdo ) /*++ Routine Description: This routine determines that address of the selector (whether it is a stand alone selector of part of the charger) and builds a selector structure with this information. It also reads the initial selector information and caches this in the structure. This structure will be passed out to all of the smart batteries in the system. Arguments: SubsystemFdo - Fdo for the smart battery subsystem Return Value: NTSTATUS --*/ { ULONG result; UCHAR smbStatus; PBATTERY_SELECTOR selector = NULL; PSMB_BATT_SUBSYSTEM subsystemExt = (PSMB_BATT_SUBSYSTEM) SubsystemFdo->DeviceExtension; ULONG numberOfBatteries; PAGED_CODE(); if (subsystemExt->SelectorPresent) { // // Allocate the selector structure. This has to be from non-paged pool because // it will be accessed as part of the alarm processing. // selector = ExAllocatePoolWithTag (NonPagedPool, sizeof (BATTERY_SELECTOR), 'StaB'); if (!selector) { BattPrint (BAT_ERROR, ("SmbBattBuildSelectorStruct: Couldn't allocate selector structure\n")); // // Force Selector Not Present if allocation fails // subsystemExt->Selector = NULL; subsystemExt->SelectorPresent = FALSE; subsystemExt->NumberOfBatteries = 0; return STATUS_INSUFFICIENT_RESOURCES; } // // See if the selector is part of the charger. We do this by reading // directly from the selector first. If this fails, then we verify // the charger is implementing the selector. // smbStatus = SmbBattGenericRW ( subsystemExt->SmbHcFdo, SMB_SELECTOR_ADDRESS, SELECTOR_SELECTOR_STATE, &result ); if (smbStatus == SMB_STATUS_OK) { // // We have a stand alone selector // selector->SelectorAddress = SMB_SELECTOR_ADDRESS; selector->SelectorStateCommand = SELECTOR_SELECTOR_STATE; selector->SelectorPresetsCommand= SELECTOR_SELECTOR_PRESETS; selector->SelectorInfoCommand = SELECTOR_SELECTOR_INFO; BattPrint (BAT_NOTE, ("SmbBattBuildSelectorStruct: The selector is standalone\n")); } else { // // Read the Charger Spec Info to check Selector Implemented Bit // NOTE: We're doing this for verification and information purposes // smbStatus = SmbBattGenericRW ( subsystemExt->SmbHcFdo, SMB_CHARGER_ADDRESS, CHARGER_SPEC_INFO, &result ); if (smbStatus == SMB_STATUS_OK) { if (result & CHARGER_SELECTOR_SUPPORT_BIT) { // If Selector Support Bit is present, then Selector implemented in Charger BattPrint (BAT_NOTE, ("SmbBattBuildSelectorStruct: ChargerSpecInfo indicates charger implementing selector\n")); } else { // If Charger says it doesn't implement Selector, let's double-check anyway BattPrint (BAT_NOTE, ("SmbBattBuildSelectorStruct: ChargerSpecInfo indicates charger does not implement selector\n")); } } else { // If it returns an error, let's double-check anyway BattPrint (BAT_ERROR, ("SmbBattBuildSelectorStruct: Couldn't read ChargerSpecInfo - %x\n", smbStatus)); } // // Read SelectorState for Cache // smbStatus = SmbBattGenericRW ( subsystemExt->SmbHcFdo, SMB_CHARGER_ADDRESS, CHARGER_SELECTOR_STATE, &result ); if (smbStatus == SMB_STATUS_OK) { BattPrint (BAT_DATA, ("SmbBattBuildSelectorStruct: Selector state %x\n", result)); } else { BattPrint (BAT_ERROR, ("SmbBattBuildSelectorStruct: Couldn't read charger selector state - %x\n", smbStatus)); goto SelectorErrorExit; } // // The charger is implementing the selector // selector->SelectorAddress = SMB_CHARGER_ADDRESS; selector->SelectorStateCommand = CHARGER_SELECTOR_STATE; selector->SelectorPresetsCommand= CHARGER_SELECTOR_PRESETS; selector->SelectorInfoCommand = CHARGER_SELECTOR_INFO; BattPrint (BAT_NOTE, ("SmbBattBuildSelectorStruct: Charger implements the selector\n")); } // // Initialize the selector mutex // ExInitializeFastMutex (&selector->Mutex); // // Store SelectorState in Cache // selector->SelectorState = result; // // Read SelectorPresets for Cache // smbStatus = SmbBattGenericRW ( subsystemExt->SmbHcFdo, selector->SelectorAddress, selector->SelectorPresetsCommand, &selector->SelectorPresets ); if (smbStatus != SMB_STATUS_OK) { BattPrint (BAT_ERROR, ("SmbBattBuildSelectorStruct: Couldn't read selector presets - %x\n", smbStatus)); // // Should we really fail the whole thing, because of an error reading SelectorPresets? // Let's Emulate the Information (Ok To Use All, Use Next A if available) // selector->SelectorPresets = (selector->SelectorState & SELECTOR_PRESETS_OKTOUSE_MASK); if (selector->SelectorPresets & BATTERY_A_PRESENT) { selector->SelectorPresets |= (BATTERY_A_PRESENT << SELECTOR_SHIFT_USENEXT); } BattPrint (BAT_ERROR, ("SmbBattBuildSelectorStruct: Emulating Selector Presets - %x\n", selector->SelectorPresets)); } else { BattPrint (BAT_DATA, ("SmbBattBuildSelectorStruct: Selector presets %x\n", selector->SelectorPresets)); } // // Read Selector Info for Cache // smbStatus = SmbBattGenericRW ( subsystemExt->SmbHcFdo, selector->SelectorAddress, selector->SelectorInfoCommand, &selector->SelectorInfo ); if (smbStatus != SMB_STATUS_OK) { BattPrint (BAT_ERROR, ("SmbBattBuildSelectorStruct: Couldn't read selector info - %x\n", smbStatus)); // // Should we really fail the whole thing, because of an error reading SelectorInfo? // Let's Emulate the Information (Specification 1.0, No Charge Indicator) // selector->SelectorInfo = 0x0010; if (subsystemExt->NumberOfBatteries > 0) { selector->SelectorInfo |= BATTERY_A_PRESENT; } if (subsystemExt->NumberOfBatteries > 1) { selector->SelectorInfo |= BATTERY_B_PRESENT; } if (subsystemExt->NumberOfBatteries > 2) { selector->SelectorInfo |= BATTERY_C_PRESENT; } if (subsystemExt->NumberOfBatteries > 3) { selector->SelectorInfo |= BATTERY_D_PRESENT; } BattPrint (BAT_ERROR, ("SmbBattBuildSelectorStruct: Emulating Selector Info - %x\n", selector->SelectorInfo)); } else { BattPrint (BAT_NOTE, ("SmbBattBuildSelectorStruct: Selector info %x\n", selector->SelectorInfo)); // Verify the Number of Batteries against the SelectorInfo numberOfBatteries = 0; result = (selector->SelectorInfo & SELECTOR_INFO_SUPPORT_MASK); if (result & BATTERY_A_PRESENT) numberOfBatteries++; if (result & BATTERY_B_PRESENT) numberOfBatteries++; if (result & BATTERY_C_PRESENT) numberOfBatteries++; if (result & BATTERY_D_PRESENT) numberOfBatteries++; // Should we always override ACPI?? // Proposed Solution: if Selector supports less batteries than // ACPI says, then Override ACPI with selector support. If // Selector supports more than ACPI says, then don't override, // unless ACPI was invalid and the # of batteries = 1 if (subsystemExt->NumberOfBatteries > numberOfBatteries) { subsystemExt->NumberOfBatteries = numberOfBatteries; } else if ((subsystemExt->NumberOfBatteries == 1) && (numberOfBatteries > 1)) { subsystemExt->NumberOfBatteries = numberOfBatteries; } else if (subsystemExt->NumberOfBatteries < numberOfBatteries) { //subsystemExt->NumberOfBatteries = numberOfBatteries; } } } // if (subsystemFdo->SelectorPresent) // // Everything was OK // subsystemExt->Selector = selector; return STATUS_SUCCESS; SelectorErrorExit: // // If a failure occurs, free the selector structure and don't creat any batery devices. // ExFreePool (selector); subsystemExt->Selector = NULL; subsystemExt->SelectorPresent = FALSE; subsystemExt->NumberOfBatteries = 0; return STATUS_UNSUCCESSFUL; }