Subject: [PATCH] [acpi battery] Add low-level batttery access - Add struct acpi_bif and struct acpi_bst definitions - Add exports for battery_check(), battery_get_status(), and battery_set_alarm(). - Add err() macro - Fill out drivers/acpi/drivers/batter/device.c - Get battery status when device is bound Signed-off-by: Patrick Mochel --- drivers/acpi/drivers/battery/battery.h | 31 +++++ drivers/acpi/drivers/battery/device.c | 208 ++++++++++++++++++++++++++++++++ drivers/acpi/drivers/battery/driver.c | 7 + 3 files changed, 246 insertions(+), 0 deletions(-) applies-to: c4b48f71228b91d9f94c16401d98f12a5d04a596 f1acc91af05c68dacc082d310c343a8541393b4f diff --git a/drivers/acpi/drivers/battery/battery.h b/drivers/acpi/drivers/battery/battery.h index 407a566..1fad0e3 100644 --- a/drivers/acpi/drivers/battery/battery.h +++ b/drivers/acpi/drivers/battery/battery.h @@ -5,6 +5,8 @@ #define dbg(fmt, ...) #endif +#define err(fmt, ...) printk(KERN_ERR PREFIX "battery: " fmt "\n", ## __VA_ARGS__) + #define _COMPONENT ACPI_BATTERY_COMPONENT ACPI_MODULE_NAME("acpi_battery"); @@ -17,9 +19,38 @@ ACPI_MODULE_NAME("acpi_battery"); #define ACPI_BATTERY_HID "PNP0C0A" +struct acpi_bif { + acpi_integer power_unit; + acpi_integer design_capacity; + acpi_integer last_full_capacity; + acpi_integer battery_technology; + acpi_integer design_voltage; + acpi_integer design_capacity_warning; + acpi_integer design_capacity_low; + acpi_integer battery_capacity_granularity_1; + acpi_integer battery_capacity_granularity_2; + acpi_string model_number; + acpi_string serial_number; + acpi_string battery_type; + acpi_string oem_info; +}; + +struct acpi_bst { + acpi_integer state; + acpi_integer present_rate; + acpi_integer remaining_capacity; + acpi_integer present_voltage; +}; + struct acpi_battery { struct acpi_dev * b_ad; unsigned int b_present; unsigned int b_have_alarm; unsigned int b_alarm; + struct acpi_bif * b_bif; }; + + +extern int battery_check(struct acpi_battery * ab); +extern int battery_set_alarm(struct acpi_battery * ab, u32 alarm); +extern int battery_get_status(struct acpi_battery * ab, struct acpi_bst ** bst); diff --git a/drivers/acpi/drivers/battery/device.c b/drivers/acpi/drivers/battery/device.c index 9036102..d0c3af5 100644 --- a/drivers/acpi/drivers/battery/device.c +++ b/drivers/acpi/drivers/battery/device.c @@ -14,3 +14,211 @@ #include #include "battery.h" + + + +int battery_get_status(struct acpi_battery * ab, struct acpi_bst ** bst) +{ + struct acpi_buffer buffer = { + .length = ACPI_ALLOCATE_BUFFER, + }; + struct acpi_buffer format = { + .length = sizeof(ACPI_BATTERY_FORMAT_BST), + .pointer = ACPI_BATTERY_FORMAT_BST, + }; + struct acpi_buffer data = {}; + union acpi_object * package; + acpi_status status; + int ret = 0; + + /* + * Evaluate _BST + */ + status = acpi_evaluate_object(ab->b_ad->acpi_device->handle, + "_BST", NULL, &buffer); + if (ACPI_FAILURE(status)) { + dbg("Error evaluating _BST"); + return -ENODEV; + } + + package = buffer.pointer; + + /* + * Extract Package Data + */ + status = acpi_extract_package(package, &format, &data); + if (status != AE_BUFFER_OVERFLOW) { + dbg("Error extracting _BST (the first time)"); + ret = -ENODEV; + goto Done; + } + + data.pointer = kzalloc(data.length, GFP_KERNEL); + if (!data.pointer) { + ret = -ENOMEM; + goto Done; + } + + status = acpi_extract_package(package, &format, &data); + if (ACPI_FAILURE(status)) { + dbg("Error extracting _BST"); + kfree(data.pointer); + ret = -ENODEV; + goto Done; + } + *bst = data.pointer; +Done: + kfree(buffer.pointer); + return ret; +} + + +static int get_bif(struct acpi_battery * ab) +{ + struct acpi_buffer buffer = { + .length = ACPI_ALLOCATE_BUFFER, + }; + struct acpi_buffer format = { + .length = sizeof(ACPI_BATTERY_FORMAT_BIF), + .pointer = ACPI_BATTERY_FORMAT_BIF, + }; + struct acpi_buffer data = { }; + union acpi_object * package; + acpi_status status; + int ret = 0; + + /* + * Evaluate _BIF + */ + status = acpi_evaluate_object(ab->b_ad->acpi_device->handle, + "_BIF", NULL, &buffer); + if (ACPI_FAILURE(status)) { + dbg("Error evaluating _BIF"); + return -ENODEV; + } + + package = buffer.pointer; + + /* + * Extract Package Data + */ + status = acpi_extract_package(package, &format, &data); + if (status != AE_BUFFER_OVERFLOW) { + dbg("Error extracting _BIF (the first time)"); + ret = -ENODEV; + goto Done; + } + + data.pointer = kzalloc(data.length, GFP_KERNEL); + if (!data.pointer) { + ret = -ENOMEM; + goto Done; + } + + status = acpi_extract_package(package, &format, &data); + if (ACPI_FAILURE(status)) { + dbg("Error extracting _BIF (the second time)"); + kfree(data.pointer); + ret = -ENODEV; + goto Done; + } + + /* + * Free previous info, if there + */ + if (ab->b_bif) + kfree(ab->b_bif); + ab->b_bif = data.pointer; + +Done: + kfree(buffer.pointer); + return ret; +} + + +int battery_set_alarm(struct acpi_battery * ab, u32 alarm) +{ + acpi_status status; + union acpi_object arg = { + .type = ACPI_TYPE_INTEGER, + .integer = { + .value = alarm, + }, + }; + struct acpi_object_list arg_list = { + .count = 1, + .pointer = &arg, + }; + int ret = 0; + + status = acpi_evaluate_object(ab->b_ad->acpi_device->handle, + "_BTP", &arg_list, NULL); + + if (ACPI_SUCCESS(status)) { + dbg("Alarm set to %u", alarm); + ab->b_alarm = alarm; + } else + ret = -ENODEV; + return ret; +} + +static int get_btp(struct acpi_battery * ab) +{ + acpi_status status; + acpi_handle handle; + int ret = 0; + + status = acpi_get_handle(ab->b_ad->acpi_device->handle, + "_BTP", &handle); + if (ACPI_SUCCESS(status)) { + ab->b_have_alarm = 1; + ret = battery_set_alarm(ab, ab->b_bif->design_capacity_warning); + } + return ret; +} + + +int battery_check(struct acpi_battery * ab) +{ + int ret; + + ret = acpi_bus_get_status(ab->b_ad->acpi_device); + if (ret) + return ret; + + if (!ab->b_present) { + /* + * Battery Inserted + */ + if (ab->b_ad->acpi_device->status.battery_present) { + dbg("Battery %s inserted", acpi_dev_bid(ab->b_ad)); + + /* + * Get battery info (via "_BIF") + */ + ret = get_bif(ab); + if (ret) { + err("Getting _BIF Failed\n"); + return ret; + } + + /* + * Get alarm info (via "_BTP") + */ + get_btp(ab); + if (ret) + err("Getting _BTP Failed\n"); + } + ab->b_present = 1; + } else { + /* + * Battery Removed + */ + if (!ab->b_ad->acpi_device->status.battery_present) + dbg("Battery %s removed", acpi_dev_bid(ab->b_ad);); + ab->b_present = 0; + } + + return 0; +} + diff --git a/drivers/acpi/drivers/battery/driver.c b/drivers/acpi/drivers/battery/driver.c index 06ebe2f..e337b86 100644 --- a/drivers/acpi/drivers/battery/driver.c +++ b/drivers/acpi/drivers/battery/driver.c @@ -19,6 +19,7 @@ static int battery_add(struct acpi_dev * ad) { struct acpi_battery * ab; + int ret = 0; ab = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL); if (!ab) @@ -26,6 +27,12 @@ static int battery_add(struct acpi_dev * ab->b_ad = ad; + ret = battery_check(ab); + if (ret) { + kfree(ab); + return ret; + } + dev_set_drvdata(&ad->dev, ab); printk(KERN_INFO PREFIX "Battery Slot [%s] (battery %s)\n", --- 0.99.9.GIT