21
21
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22
22
*/
23
23
24
+ #define pr_fmt (fmt ) KBUILD_MODNAME ": " fmt
25
+
24
26
#include <linux/kernel.h>
27
+ #include <linux/list.h>
25
28
#include <linux/module.h>
29
+ #include <linux/mutex.h>
26
30
#include <linux/init.h>
27
31
#include <linux/types.h>
28
32
#include <linux/jiffies.h>
42
46
#include <linux/acpi.h>
43
47
#include <linux/power_supply.h>
44
48
45
- #include " battery.h"
49
+ #include <acpi/ battery.h>
46
50
47
51
#define PREFIX "ACPI: "
48
52
@@ -125,6 +129,7 @@ struct acpi_battery {
125
129
struct power_supply_desc bat_desc ;
126
130
struct acpi_device * device ;
127
131
struct notifier_block pm_nb ;
132
+ struct list_head list ;
128
133
unsigned long update_time ;
129
134
int revision ;
130
135
int rate_now ;
@@ -630,6 +635,139 @@ static const struct device_attribute alarm_attr = {
630
635
.store = acpi_battery_alarm_store ,
631
636
};
632
637
638
+ /*
639
+ * The Battery Hooking API
640
+ *
641
+ * This API is used inside other drivers that need to expose
642
+ * platform-specific behaviour within the generic driver in a
643
+ * generic way.
644
+ *
645
+ */
646
+
647
+ static LIST_HEAD (acpi_battery_list );
648
+ static LIST_HEAD (battery_hook_list );
649
+ static DEFINE_MUTEX (hook_mutex );
650
+
651
+ void __battery_hook_unregister (struct acpi_battery_hook * hook , int lock )
652
+ {
653
+ struct acpi_battery * battery ;
654
+ /*
655
+ * In order to remove a hook, we first need to
656
+ * de-register all the batteries that are registered.
657
+ */
658
+ if (lock )
659
+ mutex_lock (& hook_mutex );
660
+ list_for_each_entry (battery , & acpi_battery_list , list ) {
661
+ hook -> remove_battery (battery -> bat );
662
+ }
663
+ list_del (& hook -> list );
664
+ if (lock )
665
+ mutex_unlock (& hook_mutex );
666
+ pr_info ("extension unregistered: %s\n" , hook -> name );
667
+ }
668
+
669
+ void battery_hook_unregister (struct acpi_battery_hook * hook )
670
+ {
671
+ __battery_hook_unregister (hook , 1 );
672
+ }
673
+ EXPORT_SYMBOL_GPL (battery_hook_unregister );
674
+
675
+ void battery_hook_register (struct acpi_battery_hook * hook )
676
+ {
677
+ struct acpi_battery * battery ;
678
+
679
+ mutex_lock (& hook_mutex );
680
+ INIT_LIST_HEAD (& hook -> list );
681
+ list_add (& hook -> list , & battery_hook_list );
682
+ /*
683
+ * Now that the driver is registered, we need
684
+ * to notify the hook that a battery is available
685
+ * for each battery, so that the driver may add
686
+ * its attributes.
687
+ */
688
+ list_for_each_entry (battery , & acpi_battery_list , list ) {
689
+ if (hook -> add_battery (battery -> bat )) {
690
+ /*
691
+ * If a add-battery returns non-zero,
692
+ * the registration of the extension has failed,
693
+ * and we will not add it to the list of loaded
694
+ * hooks.
695
+ */
696
+ pr_err ("extension failed to load: %s" , hook -> name );
697
+ __battery_hook_unregister (hook , 0 );
698
+ return ;
699
+ }
700
+ }
701
+ pr_info ("new extension: %s\n" , hook -> name );
702
+ mutex_unlock (& hook_mutex );
703
+ }
704
+ EXPORT_SYMBOL_GPL (battery_hook_register );
705
+
706
+ /*
707
+ * This function gets called right after the battery sysfs
708
+ * attributes have been added, so that the drivers that
709
+ * define custom sysfs attributes can add their own.
710
+ */
711
+ static void battery_hook_add_battery (struct acpi_battery * battery )
712
+ {
713
+ struct acpi_battery_hook * hook_node ;
714
+
715
+ mutex_lock (& hook_mutex );
716
+ INIT_LIST_HEAD (& battery -> list );
717
+ list_add (& battery -> list , & acpi_battery_list );
718
+ /*
719
+ * Since we added a new battery to the list, we need to
720
+ * iterate over the hooks and call add_battery for each
721
+ * hook that was registered. This usually happens
722
+ * when a battery gets hotplugged or initialized
723
+ * during the battery module initialization.
724
+ */
725
+ list_for_each_entry (hook_node , & battery_hook_list , list ) {
726
+ if (hook_node -> add_battery (battery -> bat )) {
727
+ /*
728
+ * The notification of the extensions has failed, to
729
+ * prevent further errors we will unload the extension.
730
+ */
731
+ __battery_hook_unregister (hook_node , 0 );
732
+ pr_err ("error in extension, unloading: %s" ,
733
+ hook_node -> name );
734
+ }
735
+ }
736
+ mutex_unlock (& hook_mutex );
737
+ }
738
+
739
+ static void battery_hook_remove_battery (struct acpi_battery * battery )
740
+ {
741
+ struct acpi_battery_hook * hook ;
742
+
743
+ mutex_lock (& hook_mutex );
744
+ /*
745
+ * Before removing the hook, we need to remove all
746
+ * custom attributes from the battery.
747
+ */
748
+ list_for_each_entry (hook , & battery_hook_list , list ) {
749
+ hook -> remove_battery (battery -> bat );
750
+ }
751
+ /* Then, just remove the battery from the list */
752
+ list_del (& battery -> list );
753
+ mutex_unlock (& hook_mutex );
754
+ }
755
+
756
+ static void __exit battery_hook_exit (void )
757
+ {
758
+ struct acpi_battery_hook * hook ;
759
+ struct acpi_battery_hook * ptr ;
760
+ /*
761
+ * At this point, the acpi_bus_unregister_driver()
762
+ * has called remove for all batteries. We just
763
+ * need to remove the hooks.
764
+ */
765
+ list_for_each_entry_safe (hook , ptr , & battery_hook_list , list ) {
766
+ __battery_hook_unregister (hook , 1 );
767
+ }
768
+ mutex_destroy (& hook_mutex );
769
+ }
770
+
633
771
static int sysfs_add_battery (struct acpi_battery * battery )
634
772
{
635
773
struct power_supply_config psy_cfg = { .drv_data = battery , };
@@ -657,6 +795,7 @@ static int sysfs_add_battery(struct acpi_battery *battery)
657
795
battery -> bat = NULL ;
658
796
return result ;
659
797
}
798
+ battery_hook_add_battery (battery );
660
799
return device_create_file (& battery -> bat -> dev , & alarm_attr );
661
800
}
662
801
@@ -667,7 +806,7 @@ static void sysfs_remove_battery(struct acpi_battery *battery)
667
806
mutex_unlock (& battery -> sysfs_lock );
668
807
return ;
669
808
}
670
-
809
+ battery_hook_remove_battery ( battery );
671
810
device_remove_file (& battery -> bat -> dev , & alarm_attr );
672
811
power_supply_unregister (battery -> bat );
673
812
battery -> bat = NULL ;
@@ -1399,8 +1538,10 @@ static int __init acpi_battery_init(void)
1399
1538
static void __exit acpi_battery_exit (void )
1400
1539
{
1401
1540
async_synchronize_cookie (async_cookie + 1 );
1402
- if (battery_driver_registered )
1541
+ if (battery_driver_registered ) {
1403
1542
acpi_bus_unregister_driver (& acpi_battery_driver );
1543
+ battery_hook_exit ();
1544
+ }
1404
1545
#ifdef CONFIG_ACPI_PROCFS_POWER
1405
1546
if (acpi_battery_dir )
1406
1547
acpi_unlock_battery_dir (acpi_battery_dir );
0 commit comments