|
@@ -232,6 +232,86 @@ static struct acpi_driver rbtn_driver = {
|
|
|
};
|
|
|
|
|
|
|
|
|
+/*
|
|
|
+ * 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
|
|
|
*/
|
|
@@ -266,7 +346,10 @@ static int rbtn_add(struct acpi_device *device)
|
|
|
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;
|
|
@@ -313,6 +396,7 @@ static void rbtn_notify(struct acpi_device *device, u32 event)
|
|
|
break;
|
|
|
case RBTN_SLIDER:
|
|
|
rbtn_rfkill_event(device);
|
|
|
+ atomic_notifier_call_chain(&rbtn_chain_head, event, device);
|
|
|
break;
|
|
|
default:
|
|
|
break;
|
|
@@ -326,6 +410,13 @@ static void rbtn_notify(struct acpi_device *device, u32 event)
|
|
|
|
|
|
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>");
|