};
+/*
+ * notifier export functions
+ */
+
+static bool auto_remove_rfkill = true;
+
+static ATOMIC_NOTIFIER_HEAD(rbtn_chain_head);
+
+static int rbtn_inc_count(struct device *dev, void *data)
+{
+ struct acpi_device *device = to_acpi_device(dev);
+ struct rbtn_data *rbtn_data = device->driver_data;
+ int *count = data;
+
+ if (rbtn_data->type == RBTN_SLIDER)
+ (*count)++;
+
+ return 0;
+}
+
+static int rbtn_switch_dev(struct device *dev, void *data)
+{
+ struct acpi_device *device = to_acpi_device(dev);
+ struct rbtn_data *rbtn_data = device->driver_data;
+ bool enable = data;
+
+ if (rbtn_data->type != RBTN_SLIDER)
+ return 0;
+
+ if (enable)
+ rbtn_rfkill_init(device);
+ else
+ rbtn_rfkill_exit(device);
+
+ return 0;
+}
+
+int dell_rbtn_notifier_register(struct notifier_block *nb)
+{
+ bool first;
+ int count;
+ int ret;
+
+ count = 0;
+ ret = driver_for_each_device(&rbtn_driver.drv, NULL, &count,
+ rbtn_inc_count);
+ if (ret || count == 0)
+ return -ENODEV;
+
+ first = !rbtn_chain_head.head;
+
+ ret = atomic_notifier_chain_register(&rbtn_chain_head, nb);
+ if (ret != 0)
+ return ret;
+
+ if (auto_remove_rfkill && first)
+ ret = driver_for_each_device(&rbtn_driver.drv, NULL,
+ (void *)false, rbtn_switch_dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dell_rbtn_notifier_register);
+
+int dell_rbtn_notifier_unregister(struct notifier_block *nb)
+{
+ int ret;
+
+ ret = atomic_notifier_chain_unregister(&rbtn_chain_head, nb);
+ if (ret != 0)
+ return ret;
+
+ if (auto_remove_rfkill && !rbtn_chain_head.head)
+ ret = driver_for_each_device(&rbtn_driver.drv, NULL,
+ (void *)true, rbtn_switch_dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dell_rbtn_notifier_unregister);
+
+
/*
* acpi driver functions
*/
ret = rbtn_input_init(rbtn_data);
break;
case RBTN_SLIDER:
- ret = rbtn_rfkill_init(device);
+ if (auto_remove_rfkill && rbtn_chain_head.head)
+ ret = 0;
+ else
+ ret = rbtn_rfkill_init(device);
break;
default:
ret = -EINVAL;
break;
case RBTN_SLIDER:
rbtn_rfkill_event(device);
+ atomic_notifier_call_chain(&rbtn_chain_head, event, device);
break;
default:
break;
module_acpi_driver(rbtn_driver);
+module_param(auto_remove_rfkill, bool, 0444);
+
+MODULE_PARM_DESC(auto_remove_rfkill, "Automatically remove rfkill devices when "
+ "other modules start receiving events "
+ "from this module and re-add them when "
+ "the last module stops receiving events "
+ "(default true)");
MODULE_DEVICE_TABLE(acpi, rbtn_ids);
MODULE_DESCRIPTION("Dell Airplane Mode Switch driver");
MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
--- /dev/null
+/*
+ Dell Airplane Mode Switch driver
+ Copyright (C) 2014-2015 Pali Rohár <pali.rohar@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef _DELL_RBTN_H_
+#define _DELL_RBTN_H_
+
+struct notifier_block;
+
+int dell_rbtn_notifier_register(struct notifier_block *nb);
+int dell_rbtn_notifier_unregister(struct notifier_block *nb);
+
+#endif