Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003     Dell Airplane Mode Switch driver
0004     Copyright (C) 2014-2015  Pali Rohár <pali@kernel.org>
0005 
0006 */
0007 
0008 #include <linux/module.h>
0009 #include <linux/acpi.h>
0010 #include <linux/rfkill.h>
0011 #include <linux/input.h>
0012 
0013 #include "dell-rbtn.h"
0014 
0015 enum rbtn_type {
0016     RBTN_UNKNOWN,
0017     RBTN_TOGGLE,
0018     RBTN_SLIDER,
0019 };
0020 
0021 struct rbtn_data {
0022     enum rbtn_type type;
0023     struct rfkill *rfkill;
0024     struct input_dev *input_dev;
0025     bool suspended;
0026 };
0027 
0028 
0029 /*
0030  * acpi functions
0031  */
0032 
0033 static enum rbtn_type rbtn_check(struct acpi_device *device)
0034 {
0035     unsigned long long output;
0036     acpi_status status;
0037 
0038     status = acpi_evaluate_integer(device->handle, "CRBT", NULL, &output);
0039     if (ACPI_FAILURE(status))
0040         return RBTN_UNKNOWN;
0041 
0042     switch (output) {
0043     case 0:
0044     case 1:
0045         return RBTN_TOGGLE;
0046     case 2:
0047     case 3:
0048         return RBTN_SLIDER;
0049     default:
0050         return RBTN_UNKNOWN;
0051     }
0052 }
0053 
0054 static int rbtn_get(struct acpi_device *device)
0055 {
0056     unsigned long long output;
0057     acpi_status status;
0058 
0059     status = acpi_evaluate_integer(device->handle, "GRBT", NULL, &output);
0060     if (ACPI_FAILURE(status))
0061         return -EINVAL;
0062 
0063     return !output;
0064 }
0065 
0066 static int rbtn_acquire(struct acpi_device *device, bool enable)
0067 {
0068     struct acpi_object_list input;
0069     union acpi_object param;
0070     acpi_status status;
0071 
0072     param.type = ACPI_TYPE_INTEGER;
0073     param.integer.value = enable;
0074     input.count = 1;
0075     input.pointer = &param;
0076 
0077     status = acpi_evaluate_object(device->handle, "ARBT", &input, NULL);
0078     if (ACPI_FAILURE(status))
0079         return -EINVAL;
0080 
0081     return 0;
0082 }
0083 
0084 
0085 /*
0086  * rfkill device
0087  */
0088 
0089 static void rbtn_rfkill_query(struct rfkill *rfkill, void *data)
0090 {
0091     struct acpi_device *device = data;
0092     int state;
0093 
0094     state = rbtn_get(device);
0095     if (state < 0)
0096         return;
0097 
0098     rfkill_set_states(rfkill, state, state);
0099 }
0100 
0101 static int rbtn_rfkill_set_block(void *data, bool blocked)
0102 {
0103     /* NOTE: setting soft rfkill state is not supported */
0104     return -EINVAL;
0105 }
0106 
0107 static const struct rfkill_ops rbtn_ops = {
0108     .query = rbtn_rfkill_query,
0109     .set_block = rbtn_rfkill_set_block,
0110 };
0111 
0112 static int rbtn_rfkill_init(struct acpi_device *device)
0113 {
0114     struct rbtn_data *rbtn_data = device->driver_data;
0115     int ret;
0116 
0117     if (rbtn_data->rfkill)
0118         return 0;
0119 
0120     /*
0121      * NOTE: rbtn controls all radio devices, not only WLAN
0122      *       but rfkill interface does not support "ANY" type
0123      *       so "WLAN" type is used
0124      */
0125     rbtn_data->rfkill = rfkill_alloc("dell-rbtn", &device->dev,
0126                      RFKILL_TYPE_WLAN, &rbtn_ops, device);
0127     if (!rbtn_data->rfkill)
0128         return -ENOMEM;
0129 
0130     ret = rfkill_register(rbtn_data->rfkill);
0131     if (ret) {
0132         rfkill_destroy(rbtn_data->rfkill);
0133         rbtn_data->rfkill = NULL;
0134         return ret;
0135     }
0136 
0137     return 0;
0138 }
0139 
0140 static void rbtn_rfkill_exit(struct acpi_device *device)
0141 {
0142     struct rbtn_data *rbtn_data = device->driver_data;
0143 
0144     if (!rbtn_data->rfkill)
0145         return;
0146 
0147     rfkill_unregister(rbtn_data->rfkill);
0148     rfkill_destroy(rbtn_data->rfkill);
0149     rbtn_data->rfkill = NULL;
0150 }
0151 
0152 static void rbtn_rfkill_event(struct acpi_device *device)
0153 {
0154     struct rbtn_data *rbtn_data = device->driver_data;
0155 
0156     if (rbtn_data->rfkill)
0157         rbtn_rfkill_query(rbtn_data->rfkill, device);
0158 }
0159 
0160 
0161 /*
0162  * input device
0163  */
0164 
0165 static int rbtn_input_init(struct rbtn_data *rbtn_data)
0166 {
0167     int ret;
0168 
0169     rbtn_data->input_dev = input_allocate_device();
0170     if (!rbtn_data->input_dev)
0171         return -ENOMEM;
0172 
0173     rbtn_data->input_dev->name = "DELL Wireless hotkeys";
0174     rbtn_data->input_dev->phys = "dellabce/input0";
0175     rbtn_data->input_dev->id.bustype = BUS_HOST;
0176     rbtn_data->input_dev->evbit[0] = BIT(EV_KEY);
0177     set_bit(KEY_RFKILL, rbtn_data->input_dev->keybit);
0178 
0179     ret = input_register_device(rbtn_data->input_dev);
0180     if (ret) {
0181         input_free_device(rbtn_data->input_dev);
0182         rbtn_data->input_dev = NULL;
0183         return ret;
0184     }
0185 
0186     return 0;
0187 }
0188 
0189 static void rbtn_input_exit(struct rbtn_data *rbtn_data)
0190 {
0191     input_unregister_device(rbtn_data->input_dev);
0192     rbtn_data->input_dev = NULL;
0193 }
0194 
0195 static void rbtn_input_event(struct rbtn_data *rbtn_data)
0196 {
0197     input_report_key(rbtn_data->input_dev, KEY_RFKILL, 1);
0198     input_sync(rbtn_data->input_dev);
0199     input_report_key(rbtn_data->input_dev, KEY_RFKILL, 0);
0200     input_sync(rbtn_data->input_dev);
0201 }
0202 
0203 
0204 /*
0205  * acpi driver
0206  */
0207 
0208 static int rbtn_add(struct acpi_device *device);
0209 static int rbtn_remove(struct acpi_device *device);
0210 static void rbtn_notify(struct acpi_device *device, u32 event);
0211 
0212 static const struct acpi_device_id rbtn_ids[] = {
0213     { "DELRBTN", 0 },
0214     { "DELLABCE", 0 },
0215 
0216     /*
0217      * This driver can also handle the "DELLABC6" device that
0218      * appears on the XPS 13 9350, but that device is disabled by
0219      * the DSDT unless booted with acpi_osi="!Windows 2012"
0220      * acpi_osi="!Windows 2013".
0221      *
0222      * According to Mario at Dell:
0223      *
0224      *  DELLABC6 is a custom interface that was created solely to
0225      *  have airplane mode support for Windows 7.  For Windows 10
0226      *  the proper interface is to use that which is handled by
0227      *  intel-hid. A OEM airplane mode driver is not used.
0228      *
0229      *  Since the kernel doesn't identify as Windows 7 it would be
0230      *  incorrect to do attempt to use that interface.
0231      *
0232      * Even if we override _OSI and bind to DELLABC6, we end up with
0233      * inconsistent behavior in which userspace can get out of sync
0234      * with the rfkill state as it conflicts with events from
0235      * intel-hid.
0236      *
0237      * The upshot is that it is better to just ignore DELLABC6
0238      * devices.
0239      */
0240 
0241     { "", 0 },
0242 };
0243 
0244 #ifdef CONFIG_PM_SLEEP
0245 static void ACPI_SYSTEM_XFACE rbtn_clear_suspended_flag(void *context)
0246 {
0247     struct rbtn_data *rbtn_data = context;
0248 
0249     rbtn_data->suspended = false;
0250 }
0251 
0252 static int rbtn_suspend(struct device *dev)
0253 {
0254     struct acpi_device *device = to_acpi_device(dev);
0255     struct rbtn_data *rbtn_data = acpi_driver_data(device);
0256 
0257     rbtn_data->suspended = true;
0258 
0259     return 0;
0260 }
0261 
0262 static int rbtn_resume(struct device *dev)
0263 {
0264     struct acpi_device *device = to_acpi_device(dev);
0265     struct rbtn_data *rbtn_data = acpi_driver_data(device);
0266     acpi_status status;
0267 
0268     /*
0269      * Upon resume, some BIOSes send an ACPI notification thet triggers
0270      * an unwanted input event. In order to ignore it, we use a flag
0271      * that we set at suspend and clear once we have received the extra
0272      * ACPI notification. Since ACPI notifications are delivered
0273      * asynchronously to drivers, we clear the flag from the workqueue
0274      * used to deliver the notifications. This should be enough
0275      * to have the flag cleared only after we received the extra
0276      * notification, if any.
0277      */
0278     status = acpi_os_execute(OSL_NOTIFY_HANDLER,
0279              rbtn_clear_suspended_flag, rbtn_data);
0280     if (ACPI_FAILURE(status))
0281         rbtn_clear_suspended_flag(rbtn_data);
0282 
0283     return 0;
0284 }
0285 #endif
0286 
0287 static SIMPLE_DEV_PM_OPS(rbtn_pm_ops, rbtn_suspend, rbtn_resume);
0288 
0289 static struct acpi_driver rbtn_driver = {
0290     .name = "dell-rbtn",
0291     .ids = rbtn_ids,
0292     .drv.pm = &rbtn_pm_ops,
0293     .ops = {
0294         .add = rbtn_add,
0295         .remove = rbtn_remove,
0296         .notify = rbtn_notify,
0297     },
0298     .owner = THIS_MODULE,
0299 };
0300 
0301 
0302 /*
0303  * notifier export functions
0304  */
0305 
0306 static bool auto_remove_rfkill = true;
0307 
0308 static ATOMIC_NOTIFIER_HEAD(rbtn_chain_head);
0309 
0310 static int rbtn_inc_count(struct device *dev, void *data)
0311 {
0312     struct acpi_device *device = to_acpi_device(dev);
0313     struct rbtn_data *rbtn_data = device->driver_data;
0314     int *count = data;
0315 
0316     if (rbtn_data->type == RBTN_SLIDER)
0317         (*count)++;
0318 
0319     return 0;
0320 }
0321 
0322 static int rbtn_switch_dev(struct device *dev, void *data)
0323 {
0324     struct acpi_device *device = to_acpi_device(dev);
0325     struct rbtn_data *rbtn_data = device->driver_data;
0326     bool enable = data;
0327 
0328     if (rbtn_data->type != RBTN_SLIDER)
0329         return 0;
0330 
0331     if (enable)
0332         rbtn_rfkill_init(device);
0333     else
0334         rbtn_rfkill_exit(device);
0335 
0336     return 0;
0337 }
0338 
0339 int dell_rbtn_notifier_register(struct notifier_block *nb)
0340 {
0341     bool first;
0342     int count;
0343     int ret;
0344 
0345     count = 0;
0346     ret = driver_for_each_device(&rbtn_driver.drv, NULL, &count,
0347                      rbtn_inc_count);
0348     if (ret || count == 0)
0349         return -ENODEV;
0350 
0351     first = !rbtn_chain_head.head;
0352 
0353     ret = atomic_notifier_chain_register(&rbtn_chain_head, nb);
0354     if (ret != 0)
0355         return ret;
0356 
0357     if (auto_remove_rfkill && first)
0358         ret = driver_for_each_device(&rbtn_driver.drv, NULL,
0359                          (void *)false, rbtn_switch_dev);
0360 
0361     return ret;
0362 }
0363 EXPORT_SYMBOL_GPL(dell_rbtn_notifier_register);
0364 
0365 int dell_rbtn_notifier_unregister(struct notifier_block *nb)
0366 {
0367     int ret;
0368 
0369     ret = atomic_notifier_chain_unregister(&rbtn_chain_head, nb);
0370     if (ret != 0)
0371         return ret;
0372 
0373     if (auto_remove_rfkill && !rbtn_chain_head.head)
0374         ret = driver_for_each_device(&rbtn_driver.drv, NULL,
0375                          (void *)true, rbtn_switch_dev);
0376 
0377     return ret;
0378 }
0379 EXPORT_SYMBOL_GPL(dell_rbtn_notifier_unregister);
0380 
0381 
0382 /*
0383  * acpi driver functions
0384  */
0385 
0386 static int rbtn_add(struct acpi_device *device)
0387 {
0388     struct rbtn_data *rbtn_data;
0389     enum rbtn_type type;
0390     int ret = 0;
0391 
0392     type = rbtn_check(device);
0393     if (type == RBTN_UNKNOWN) {
0394         dev_info(&device->dev, "Unknown device type\n");
0395         return -EINVAL;
0396     }
0397 
0398     ret = rbtn_acquire(device, true);
0399     if (ret < 0) {
0400         dev_err(&device->dev, "Cannot enable device\n");
0401         return ret;
0402     }
0403 
0404     rbtn_data = devm_kzalloc(&device->dev, sizeof(*rbtn_data), GFP_KERNEL);
0405     if (!rbtn_data)
0406         return -ENOMEM;
0407 
0408     rbtn_data->type = type;
0409     device->driver_data = rbtn_data;
0410 
0411     switch (rbtn_data->type) {
0412     case RBTN_TOGGLE:
0413         ret = rbtn_input_init(rbtn_data);
0414         break;
0415     case RBTN_SLIDER:
0416         if (auto_remove_rfkill && rbtn_chain_head.head)
0417             ret = 0;
0418         else
0419             ret = rbtn_rfkill_init(device);
0420         break;
0421     default:
0422         ret = -EINVAL;
0423     }
0424 
0425     return ret;
0426 
0427 }
0428 
0429 static int rbtn_remove(struct acpi_device *device)
0430 {
0431     struct rbtn_data *rbtn_data = device->driver_data;
0432 
0433     switch (rbtn_data->type) {
0434     case RBTN_TOGGLE:
0435         rbtn_input_exit(rbtn_data);
0436         break;
0437     case RBTN_SLIDER:
0438         rbtn_rfkill_exit(device);
0439         break;
0440     default:
0441         break;
0442     }
0443 
0444     rbtn_acquire(device, false);
0445     device->driver_data = NULL;
0446 
0447     return 0;
0448 }
0449 
0450 static void rbtn_notify(struct acpi_device *device, u32 event)
0451 {
0452     struct rbtn_data *rbtn_data = device->driver_data;
0453 
0454     /*
0455      * Some BIOSes send a notification at resume.
0456      * Ignore it to prevent unwanted input events.
0457      */
0458     if (rbtn_data->suspended) {
0459         dev_dbg(&device->dev, "ACPI notification ignored\n");
0460         return;
0461     }
0462 
0463     if (event != 0x80) {
0464         dev_info(&device->dev, "Received unknown event (0x%x)\n",
0465              event);
0466         return;
0467     }
0468 
0469     switch (rbtn_data->type) {
0470     case RBTN_TOGGLE:
0471         rbtn_input_event(rbtn_data);
0472         break;
0473     case RBTN_SLIDER:
0474         rbtn_rfkill_event(device);
0475         atomic_notifier_call_chain(&rbtn_chain_head, event, device);
0476         break;
0477     default:
0478         break;
0479     }
0480 }
0481 
0482 
0483 /*
0484  * module functions
0485  */
0486 
0487 module_acpi_driver(rbtn_driver);
0488 
0489 module_param(auto_remove_rfkill, bool, 0444);
0490 
0491 MODULE_PARM_DESC(auto_remove_rfkill, "Automatically remove rfkill devices when "
0492                      "other modules start receiving events "
0493                      "from this module and re-add them when "
0494                      "the last module stops receiving events "
0495                      "(default true)");
0496 MODULE_DEVICE_TABLE(acpi, rbtn_ids);
0497 MODULE_DESCRIPTION("Dell Airplane Mode Switch driver");
0498 MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
0499 MODULE_LICENSE("GPL");