|
@@ -21,8 +21,12 @@
|
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
*/
|
|
|
|
|
|
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
+
|
|
|
#include <linux/kernel.h>
|
|
|
+#include <linux/list.h>
|
|
|
#include <linux/module.h>
|
|
|
+#include <linux/mutex.h>
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/types.h>
|
|
|
#include <linux/jiffies.h>
|
|
@@ -42,7 +46,7 @@
|
|
|
#include <linux/acpi.h>
|
|
|
#include <linux/power_supply.h>
|
|
|
|
|
|
-#include "battery.h"
|
|
|
+#include <acpi/battery.h>
|
|
|
|
|
|
#define PREFIX "ACPI: "
|
|
|
|
|
@@ -125,6 +129,7 @@ struct acpi_battery {
|
|
|
struct power_supply_desc bat_desc;
|
|
|
struct acpi_device *device;
|
|
|
struct notifier_block pm_nb;
|
|
|
+ struct list_head list;
|
|
|
unsigned long update_time;
|
|
|
int revision;
|
|
|
int rate_now;
|
|
@@ -630,6 +635,139 @@ static const struct device_attribute alarm_attr = {
|
|
|
.store = acpi_battery_alarm_store,
|
|
|
};
|
|
|
|
|
|
+/*
|
|
|
+ * The Battery Hooking API
|
|
|
+ *
|
|
|
+ * This API is used inside other drivers that need to expose
|
|
|
+ * platform-specific behaviour within the generic driver in a
|
|
|
+ * generic way.
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+static LIST_HEAD(acpi_battery_list);
|
|
|
+static LIST_HEAD(battery_hook_list);
|
|
|
+static DEFINE_MUTEX(hook_mutex);
|
|
|
+
|
|
|
+void __battery_hook_unregister(struct acpi_battery_hook *hook, int lock)
|
|
|
+{
|
|
|
+ struct acpi_battery *battery;
|
|
|
+ /*
|
|
|
+ * In order to remove a hook, we first need to
|
|
|
+ * de-register all the batteries that are registered.
|
|
|
+ */
|
|
|
+ if (lock)
|
|
|
+ mutex_lock(&hook_mutex);
|
|
|
+ list_for_each_entry(battery, &acpi_battery_list, list) {
|
|
|
+ hook->remove_battery(battery->bat);
|
|
|
+ }
|
|
|
+ list_del(&hook->list);
|
|
|
+ if (lock)
|
|
|
+ mutex_unlock(&hook_mutex);
|
|
|
+ pr_info("extension unregistered: %s\n", hook->name);
|
|
|
+}
|
|
|
+
|
|
|
+void battery_hook_unregister(struct acpi_battery_hook *hook)
|
|
|
+{
|
|
|
+ __battery_hook_unregister(hook, 1);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(battery_hook_unregister);
|
|
|
+
|
|
|
+void battery_hook_register(struct acpi_battery_hook *hook)
|
|
|
+{
|
|
|
+ struct acpi_battery *battery;
|
|
|
+
|
|
|
+ mutex_lock(&hook_mutex);
|
|
|
+ INIT_LIST_HEAD(&hook->list);
|
|
|
+ list_add(&hook->list, &battery_hook_list);
|
|
|
+ /*
|
|
|
+ * Now that the driver is registered, we need
|
|
|
+ * to notify the hook that a battery is available
|
|
|
+ * for each battery, so that the driver may add
|
|
|
+ * its attributes.
|
|
|
+ */
|
|
|
+ list_for_each_entry(battery, &acpi_battery_list, list) {
|
|
|
+ if (hook->add_battery(battery->bat)) {
|
|
|
+ /*
|
|
|
+ * If a add-battery returns non-zero,
|
|
|
+ * the registration of the extension has failed,
|
|
|
+ * and we will not add it to the list of loaded
|
|
|
+ * hooks.
|
|
|
+ */
|
|
|
+ pr_err("extension failed to load: %s", hook->name);
|
|
|
+ __battery_hook_unregister(hook, 0);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ pr_info("new extension: %s\n", hook->name);
|
|
|
+ mutex_unlock(&hook_mutex);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(battery_hook_register);
|
|
|
+
|
|
|
+/*
|
|
|
+ * This function gets called right after the battery sysfs
|
|
|
+ * attributes have been added, so that the drivers that
|
|
|
+ * define custom sysfs attributes can add their own.
|
|
|
+*/
|
|
|
+static void battery_hook_add_battery(struct acpi_battery *battery)
|
|
|
+{
|
|
|
+ struct acpi_battery_hook *hook_node;
|
|
|
+
|
|
|
+ mutex_lock(&hook_mutex);
|
|
|
+ INIT_LIST_HEAD(&battery->list);
|
|
|
+ list_add(&battery->list, &acpi_battery_list);
|
|
|
+ /*
|
|
|
+ * Since we added a new battery to the list, we need to
|
|
|
+ * iterate over the hooks and call add_battery for each
|
|
|
+ * hook that was registered. This usually happens
|
|
|
+ * when a battery gets hotplugged or initialized
|
|
|
+ * during the battery module initialization.
|
|
|
+ */
|
|
|
+ list_for_each_entry(hook_node, &battery_hook_list, list) {
|
|
|
+ if (hook_node->add_battery(battery->bat)) {
|
|
|
+ /*
|
|
|
+ * The notification of the extensions has failed, to
|
|
|
+ * prevent further errors we will unload the extension.
|
|
|
+ */
|
|
|
+ __battery_hook_unregister(hook_node, 0);
|
|
|
+ pr_err("error in extension, unloading: %s",
|
|
|
+ hook_node->name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ mutex_unlock(&hook_mutex);
|
|
|
+}
|
|
|
+
|
|
|
+static void battery_hook_remove_battery(struct acpi_battery *battery)
|
|
|
+{
|
|
|
+ struct acpi_battery_hook *hook;
|
|
|
+
|
|
|
+ mutex_lock(&hook_mutex);
|
|
|
+ /*
|
|
|
+ * Before removing the hook, we need to remove all
|
|
|
+ * custom attributes from the battery.
|
|
|
+ */
|
|
|
+ list_for_each_entry(hook, &battery_hook_list, list) {
|
|
|
+ hook->remove_battery(battery->bat);
|
|
|
+ }
|
|
|
+ /* Then, just remove the battery from the list */
|
|
|
+ list_del(&battery->list);
|
|
|
+ mutex_unlock(&hook_mutex);
|
|
|
+}
|
|
|
+
|
|
|
+static void __exit battery_hook_exit(void)
|
|
|
+{
|
|
|
+ struct acpi_battery_hook *hook;
|
|
|
+ struct acpi_battery_hook *ptr;
|
|
|
+ /*
|
|
|
+ * At this point, the acpi_bus_unregister_driver()
|
|
|
+ * has called remove for all batteries. We just
|
|
|
+ * need to remove the hooks.
|
|
|
+ */
|
|
|
+ list_for_each_entry_safe(hook, ptr, &battery_hook_list, list) {
|
|
|
+ __battery_hook_unregister(hook, 1);
|
|
|
+ }
|
|
|
+ mutex_destroy(&hook_mutex);
|
|
|
+}
|
|
|
+
|
|
|
static int sysfs_add_battery(struct acpi_battery *battery)
|
|
|
{
|
|
|
struct power_supply_config psy_cfg = { .drv_data = battery, };
|
|
@@ -657,6 +795,7 @@ static int sysfs_add_battery(struct acpi_battery *battery)
|
|
|
battery->bat = NULL;
|
|
|
return result;
|
|
|
}
|
|
|
+ battery_hook_add_battery(battery);
|
|
|
return device_create_file(&battery->bat->dev, &alarm_attr);
|
|
|
}
|
|
|
|
|
@@ -667,7 +806,7 @@ static void sysfs_remove_battery(struct acpi_battery *battery)
|
|
|
mutex_unlock(&battery->sysfs_lock);
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
+ battery_hook_remove_battery(battery);
|
|
|
device_remove_file(&battery->bat->dev, &alarm_attr);
|
|
|
power_supply_unregister(battery->bat);
|
|
|
battery->bat = NULL;
|
|
@@ -1399,8 +1538,10 @@ static int __init acpi_battery_init(void)
|
|
|
static void __exit acpi_battery_exit(void)
|
|
|
{
|
|
|
async_synchronize_cookie(async_cookie + 1);
|
|
|
- if (battery_driver_registered)
|
|
|
+ if (battery_driver_registered) {
|
|
|
acpi_bus_unregister_driver(&acpi_battery_driver);
|
|
|
+ battery_hook_exit();
|
|
|
+ }
|
|
|
#ifdef CONFIG_ACPI_PROCFS_POWER
|
|
|
if (acpi_battery_dir)
|
|
|
acpi_unlock_battery_dir(acpi_battery_dir);
|