/* * Copyright 2009, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Clemens Zeidler, haiku@clemens-zeidler.de * Alexander von Gluck, kallisti5@unixzen.com */ #include #include #include #include #include #include #include #include #include "device/power_managment.h" struct battery_driver_cookie { device_node* node; acpi_device_module_info* acpi; acpi_device acpi_cookie; }; struct battery_device_cookie { battery_driver_cookie* driver_cookie; int32 stop_watching; }; #define ACPI_BATTERY_DRIVER_NAME "drivers/power/acpi_battery/driver_v1" #define ACPI_BATTERY_DEVICE_NAME "drivers/power/acpi_battery/device_v1" /* Base Namespace devices are published to */ #define ACPI_BATTERY_BASENAME "power/acpi_battery/%d" // name of pnp generator of path ids #define ACPI_BATTERY_PATHID_GENERATOR "acpi_battery/path_id" #define ACPI_NAME_BATTERY "PNP0C0A" //#define TRACE_BATTERY #ifdef TRACE_BATTERY # define TRACE(x...) dprintf("acpi_battery: "x) #else # define TRACE(x...) #endif static device_manager_info *sDeviceManager; static ConditionVariable sBatteryCondition; status_t ReadBatteryStatus(battery_driver_cookie* cookie, acpi_battery_info* batteryStatus) { status_t status = B_ERROR; acpi_data buffer; buffer.pointer = NULL; buffer.length = ACPI_ALLOCATE_BUFFER; acpi_object_type* object; acpi_object_type* pointer; status = cookie->acpi->evaluate_method(cookie->acpi_cookie, "_BST", NULL, &buffer); if (status != B_OK) goto exit; object = (acpi_object_type*)buffer.pointer; if (object->object_type != ACPI_TYPE_PACKAGE || object->package.count < 4) { status = B_ERROR; goto exit; } pointer = object->package.objects; batteryStatus->state = (pointer->object_type == ACPI_TYPE_INTEGER) ? pointer->integer.integer : BATTERY_CRITICAL_STATE; pointer++; batteryStatus->current_rate = (pointer->object_type == ACPI_TYPE_INTEGER) ? pointer->integer.integer : -1; pointer++; batteryStatus->capacity = (pointer->object_type == ACPI_TYPE_INTEGER) ? pointer->integer.integer : -1; pointer++; batteryStatus->voltage = (pointer->object_type == ACPI_TYPE_INTEGER) ? pointer->integer.integer : -1; /* If key values are all < 0, it is likely that the battery slot is empty * or the battery is damaged. Set BATTERY_CRITICAL_STATE */ if (batteryStatus->voltage < 0 && batteryStatus->current_rate < 0 && batteryStatus->capacity < 0) { batteryStatus->state = BATTERY_CRITICAL_STATE; } exit: free(buffer.pointer); return status; } status_t ReadBatteryInfo(battery_driver_cookie* cookie, acpi_extended_battery_info* batteryInfo) { acpi_data buffer; buffer.pointer = NULL; buffer.length = ACPI_ALLOCATE_BUFFER; acpi_object_type* object; acpi_object_type* pointer; status_t status = cookie->acpi->evaluate_method(cookie->acpi_cookie, "_BIF", NULL, &buffer); if (status != B_OK) goto exit; object = (acpi_object_type*)buffer.pointer; if (object->object_type != ACPI_TYPE_PACKAGE || object->package.count < 13) { status = B_ERROR; goto exit; } pointer = object->package.objects; batteryInfo->power_unit = (pointer->object_type == ACPI_TYPE_INTEGER) ? pointer->integer.integer : -1; pointer++; batteryInfo->design_capacity = (pointer->object_type == ACPI_TYPE_INTEGER) ? pointer->integer.integer : -1; pointer++; batteryInfo->last_full_charge = (pointer->object_type == ACPI_TYPE_INTEGER) ? pointer->integer.integer : -1; pointer++; batteryInfo->technology = (pointer->object_type == ACPI_TYPE_INTEGER) ? pointer->integer.integer : -1; pointer++; batteryInfo->design_voltage = (pointer->object_type == ACPI_TYPE_INTEGER) ? pointer->integer.integer : -1; pointer++; batteryInfo->design_capacity_warning = (pointer->object_type == ACPI_TYPE_INTEGER) ? pointer->integer.integer : -1; pointer++; batteryInfo->design_capacity_low = (pointer->object_type == ACPI_TYPE_INTEGER) ? pointer->integer.integer : -1; pointer++; batteryInfo->capacity_granularity_1 = (pointer->object_type == ACPI_TYPE_INTEGER) ? pointer->integer.integer : -1; pointer++; batteryInfo->capacity_granularity_2 = (pointer->object_type == ACPI_TYPE_INTEGER) ? pointer->integer.integer : -1; pointer++; strlcpy(batteryInfo->model_number, (pointer->object_type == ACPI_TYPE_STRING) ? pointer->string.string : "", sizeof(batteryInfo->model_number)); pointer++; strlcpy(batteryInfo->serial_number, (pointer->object_type == ACPI_TYPE_STRING) ? pointer->string.string : "", sizeof(batteryInfo->serial_number)); pointer++; strlcpy(batteryInfo->type, (pointer->object_type == ACPI_TYPE_STRING) ? pointer->string.string : "", sizeof(batteryInfo->type)); pointer++; strlcpy(batteryInfo->oem_info, (pointer->object_type == ACPI_TYPE_STRING) ? pointer->string.string : "", sizeof(batteryInfo->oem_info)); exit: free(buffer.pointer); return status; } int EstimatedRuntime(battery_driver_cookie* cookie, acpi_battery_info* info) { status_t status = B_ERROR; acpi_object_type argument; argument.object_type = ACPI_TYPE_INTEGER; argument.integer.integer = info->current_rate; acpi_objects arguments; arguments.count = 1; arguments.pointer = &argument; acpi_object_type object; acpi_data buffer; buffer.pointer = &object; buffer.length = sizeof(object); acpi_object_type* returnObject; status = cookie->acpi->evaluate_method(cookie->acpi_cookie, "_BTM", &arguments, &buffer); if (status != B_OK) return -1; returnObject = (acpi_object_type*)buffer.pointer; if (returnObject->object_type != ACPI_TYPE_INTEGER) return -1; int result = returnObject->integer.integer; return result; } void battery_notify_handler(acpi_handle device, uint32 value, void *context) { TRACE("battery_notify_handler event 0x%x\n", int(value)); sBatteryCondition.NotifyAll(); } void TraceBatteryInfo(acpi_extended_battery_info* batteryInfo) { TRACE("BIF power unit %i\n", batteryInfo->power_unit); TRACE("BIF design capacity %i\n", batteryInfo->design_capacity); TRACE("BIF last full charge %i\n", batteryInfo->last_full_charge); TRACE("BIF technology %i\n", batteryInfo->technology); TRACE("BIF design voltage %i\n", batteryInfo->design_voltage); TRACE("BIF design capacity warning %i\n", batteryInfo->design_capacity_warning); TRACE("BIF design capacity low %i\n", batteryInfo->design_capacity_low); TRACE("BIF capacity granularity 1 %i\n", batteryInfo->capacity_granularity_1); TRACE("BIF capacity granularity 2 %i\n", batteryInfo->capacity_granularity_2); TRACE("BIF model number %s\n", batteryInfo->model_number); TRACE("BIF serial number %s\n", batteryInfo->serial_number); TRACE("BIF type %s\n", batteryInfo->type); TRACE("BIF oem info %s\n", batteryInfo->oem_info); } // #pragma mark - device module API static status_t acpi_battery_init_device(void *driverCookie, void **cookie) { *cookie = driverCookie; return B_OK; } static void acpi_battery_uninit_device(void *_cookie) { } static status_t acpi_battery_open(void *initCookie, const char *path, int flags, void** cookie) { battery_device_cookie *device; device = (battery_device_cookie*)calloc(1, sizeof(battery_device_cookie)); if (device == NULL) return B_NO_MEMORY; device->driver_cookie = (battery_driver_cookie*)initCookie; device->stop_watching = 0; *cookie = device; return B_OK; } static status_t acpi_battery_close(void* cookie) { return B_OK; } static status_t acpi_battery_read(void* _cookie, off_t position, void *buffer, size_t* numBytes) { if (*numBytes < 1) return B_IO_ERROR; battery_device_cookie *device = (battery_device_cookie*)_cookie; acpi_battery_info batteryStatus; ReadBatteryStatus(device->driver_cookie, &batteryStatus); acpi_extended_battery_info batteryInfo; ReadBatteryInfo(device->driver_cookie, &batteryInfo); if (position == 0) { char string[512]; char *str = string; size_t max_len = sizeof(string); snprintf(str, max_len, "Battery Status:\n"); max_len -= strlen(str); str += strlen(str); snprintf(str, max_len, " State %i, Current Rate %i, Capacity %i, " "Voltage %i\n", batteryStatus.state, batteryStatus.current_rate, batteryStatus.capacity, batteryStatus.voltage); max_len -= strlen(str); str += strlen(str); snprintf(str, max_len, "\nBattery Info:\n"); max_len -= strlen(str); str += strlen(str); snprintf(str, max_len, " Power Unit %i, Design Capacity %i, " "Last Full Charge %i, Technology %i\n", batteryInfo.power_unit, batteryInfo.design_capacity, batteryInfo.last_full_charge, batteryInfo.technology); max_len -= strlen(str); str += strlen(str); snprintf(str, max_len, " Design Voltage %i, Design Capacity Warning %i, " "Design Capacity Low %i, Capacity Granularity1 %i, " "Capacity Granularity1 %i\n", batteryInfo.design_voltage, batteryInfo.design_capacity_warning, batteryInfo.design_capacity_low, batteryInfo.capacity_granularity_1, batteryInfo.capacity_granularity_1); max_len -= strlen(str); str += strlen(str); snprintf(str, max_len, " Model Number %s, Serial Number %s, " "Type %s, OEM Info %s\n", batteryInfo.model_number, batteryInfo.serial_number, batteryInfo.type, batteryInfo.oem_info); max_len -= strlen(str); str += strlen(str); max_len = user_strlcpy((char*)buffer, string, *numBytes); if (max_len < B_OK) return B_BAD_ADDRESS; *numBytes = max_len; } else *numBytes = 0; return B_OK; } static status_t acpi_battery_write(void* cookie, off_t position, const void* buffer, size_t* numBytes) { return B_ERROR; } static status_t acpi_battery_control(void* _cookie, uint32 op, void* arg, size_t len) { battery_device_cookie* device = (battery_device_cookie*)_cookie; status_t err; switch (op) { case IDENTIFY_DEVICE: { if (len < sizeof(uint32)) return B_BAD_VALUE; uint32 magicId = kMagicACPIBatteryID; if (!IS_USER_ADDRESS(arg) || user_memcpy(arg, &magicId, sizeof(magicId)) < B_OK) { return B_BAD_ADDRESS; } return B_OK; } case GET_BATTERY_INFO: { if (len < sizeof(acpi_battery_info)) return B_BAD_VALUE; acpi_battery_info batteryInfo; err = ReadBatteryStatus(device->driver_cookie, &batteryInfo); if (err != B_OK) return err; if (!IS_USER_ADDRESS(arg) || user_memcpy(arg, &batteryInfo, sizeof(batteryInfo)) < B_OK) { return B_BAD_ADDRESS; } return B_OK; } case GET_EXTENDED_BATTERY_INFO: { if (len < sizeof(acpi_extended_battery_info)) return B_BAD_VALUE; acpi_extended_battery_info extBatteryInfo; err = ReadBatteryInfo(device->driver_cookie, &extBatteryInfo); if (err != B_OK) return err; if (!IS_USER_ADDRESS(arg) || user_memcpy(arg, &extBatteryInfo, sizeof(extBatteryInfo)) < B_OK) { return B_BAD_ADDRESS; } return B_OK; } case WATCH_BATTERY: sBatteryCondition.Wait(); if (atomic_get(&(device->stop_watching))) { atomic_set(&(device->stop_watching), 0); return B_ERROR; } return B_OK; case STOP_WATCHING_BATTERY: atomic_set(&(device->stop_watching), 1); sBatteryCondition.NotifyAll(); return B_OK; } return B_DEV_INVALID_IOCTL; } static status_t acpi_battery_free(void* cookie) { battery_device_cookie* device = (battery_device_cookie*)cookie; free(device); return B_OK; } // #pragma mark - driver module API static float acpi_battery_support(device_node *parent) { // make sure parent is really the ACPI bus manager const char *bus; if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) return -1; if (strcmp(bus, "acpi")) return 0.0; // check whether it's really a device uint32 device_type; if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, &device_type, false) != B_OK || device_type != ACPI_TYPE_DEVICE) { return 0.0; } // check whether it's a battery device const char *name; if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &name, false) != B_OK || strcmp(name, ACPI_NAME_BATTERY)) { return 0.0; } return 0.6; } static status_t acpi_battery_register_device(device_node *node) { device_attr attrs[] = { { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "ACPI Battery" }}, { NULL } }; return sDeviceManager->register_node(node, ACPI_BATTERY_DRIVER_NAME, attrs, NULL, NULL); } static status_t acpi_battery_init_driver(device_node *node, void **driverCookie) { battery_driver_cookie *device; device = (battery_driver_cookie *)calloc(1, sizeof(battery_driver_cookie)); if (device == NULL) return B_NO_MEMORY; *driverCookie = device; device->node = node; device_node *parent; parent = sDeviceManager->get_parent_node(node); sDeviceManager->get_driver(parent, (driver_module_info **)&device->acpi, (void **)&device->acpi_cookie); sDeviceManager->put_node(parent); // install notify handler device->acpi->install_notify_handler(device->acpi_cookie, ACPI_ALL_NOTIFY, battery_notify_handler, device); return B_OK; } static void acpi_battery_uninit_driver(void *driverCookie) { TRACE("acpi_battery_uninit_driver\n"); battery_driver_cookie *device = (battery_driver_cookie*)driverCookie; device->acpi->remove_notify_handler(device->acpi_cookie, ACPI_ALL_NOTIFY, battery_notify_handler); free(device); } static status_t acpi_battery_register_child_devices(void *cookie) { battery_driver_cookie *device = (battery_driver_cookie*)cookie; int pathID = sDeviceManager->create_id(ACPI_BATTERY_PATHID_GENERATOR); if (pathID < 0) { TRACE("register_child_devices: couldn't create a path_id\n"); return B_ERROR; } char name[128]; snprintf(name, sizeof(name), ACPI_BATTERY_BASENAME, pathID); return sDeviceManager->publish_device(device->node, name, ACPI_BATTERY_DEVICE_NAME); } module_dependency module_dependencies[] = { { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager }, {} }; driver_module_info acpi_battery_driver_module = { { ACPI_BATTERY_DRIVER_NAME, 0, NULL }, acpi_battery_support, acpi_battery_register_device, acpi_battery_init_driver, acpi_battery_uninit_driver, acpi_battery_register_child_devices, NULL, // rescan NULL, // removed }; struct device_module_info acpi_battery_device_module = { { ACPI_BATTERY_DEVICE_NAME, 0, NULL }, acpi_battery_init_device, acpi_battery_uninit_device, NULL, acpi_battery_open, acpi_battery_close, acpi_battery_free, acpi_battery_read, acpi_battery_write, NULL, acpi_battery_control, NULL, NULL }; module_info *modules[] = { (module_info *)&acpi_battery_driver_module, (module_info *)&acpi_battery_device_module, NULL };