linux/drivers/platform/x86/dell-rbtn.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3    Dell Airplane Mode Switch driver
   4    Copyright (C) 2014-2015  Pali Rohár <pali@kernel.org>
   5
   6*/
   7
   8#include <linux/module.h>
   9#include <linux/acpi.h>
  10#include <linux/rfkill.h>
  11#include <linux/input.h>
  12
  13#include "dell-rbtn.h"
  14
  15enum rbtn_type {
  16        RBTN_UNKNOWN,
  17        RBTN_TOGGLE,
  18        RBTN_SLIDER,
  19};
  20
  21struct rbtn_data {
  22        enum rbtn_type type;
  23        struct rfkill *rfkill;
  24        struct input_dev *input_dev;
  25        bool suspended;
  26};
  27
  28
  29/*
  30 * acpi functions
  31 */
  32
  33static enum rbtn_type rbtn_check(struct acpi_device *device)
  34{
  35        unsigned long long output;
  36        acpi_status status;
  37
  38        status = acpi_evaluate_integer(device->handle, "CRBT", NULL, &output);
  39        if (ACPI_FAILURE(status))
  40                return RBTN_UNKNOWN;
  41
  42        switch (output) {
  43        case 0:
  44        case 1:
  45                return RBTN_TOGGLE;
  46        case 2:
  47        case 3:
  48                return RBTN_SLIDER;
  49        default:
  50                return RBTN_UNKNOWN;
  51        }
  52}
  53
  54static int rbtn_get(struct acpi_device *device)
  55{
  56        unsigned long long output;
  57        acpi_status status;
  58
  59        status = acpi_evaluate_integer(device->handle, "GRBT", NULL, &output);
  60        if (ACPI_FAILURE(status))
  61                return -EINVAL;
  62
  63        return !output;
  64}
  65
  66static int rbtn_acquire(struct acpi_device *device, bool enable)
  67{
  68        struct acpi_object_list input;
  69        union acpi_object param;
  70        acpi_status status;
  71
  72        param.type = ACPI_TYPE_INTEGER;
  73        param.integer.value = enable;
  74        input.count = 1;
  75        input.pointer = &param;
  76
  77        status = acpi_evaluate_object(device->handle, "ARBT", &input, NULL);
  78        if (ACPI_FAILURE(status))
  79                return -EINVAL;
  80
  81        return 0;
  82}
  83
  84
  85/*
  86 * rfkill device
  87 */
  88
  89static void rbtn_rfkill_query(struct rfkill *rfkill, void *data)
  90{
  91        struct acpi_device *device = data;
  92        int state;
  93
  94        state = rbtn_get(device);
  95        if (state < 0)
  96                return;
  97
  98        rfkill_set_states(rfkill, state, state);
  99}
 100
 101static int rbtn_rfkill_set_block(void *data, bool blocked)
 102{
 103        /* NOTE: setting soft rfkill state is not supported */
 104        return -EINVAL;
 105}
 106
 107static const struct rfkill_ops rbtn_ops = {
 108        .query = rbtn_rfkill_query,
 109        .set_block = rbtn_rfkill_set_block,
 110};
 111
 112static int rbtn_rfkill_init(struct acpi_device *device)
 113{
 114        struct rbtn_data *rbtn_data = device->driver_data;
 115        int ret;
 116
 117        if (rbtn_data->rfkill)
 118                return 0;
 119
 120        /*
 121         * NOTE: rbtn controls all radio devices, not only WLAN
 122         *       but rfkill interface does not support "ANY" type
 123         *       so "WLAN" type is used
 124         */
 125        rbtn_data->rfkill = rfkill_alloc("dell-rbtn", &device->dev,
 126                                         RFKILL_TYPE_WLAN, &rbtn_ops, device);
 127        if (!rbtn_data->rfkill)
 128                return -ENOMEM;
 129
 130        ret = rfkill_register(rbtn_data->rfkill);
 131        if (ret) {
 132                rfkill_destroy(rbtn_data->rfkill);
 133                rbtn_data->rfkill = NULL;
 134                return ret;
 135        }
 136
 137        return 0;
 138}
 139
 140static void rbtn_rfkill_exit(struct acpi_device *device)
 141{
 142        struct rbtn_data *rbtn_data = device->driver_data;
 143
 144        if (!rbtn_data->rfkill)
 145                return;
 146
 147        rfkill_unregister(rbtn_data->rfkill);
 148        rfkill_destroy(rbtn_data->rfkill);
 149        rbtn_data->rfkill = NULL;
 150}
 151
 152static void rbtn_rfkill_event(struct acpi_device *device)
 153{
 154        struct rbtn_data *rbtn_data = device->driver_data;
 155
 156        if (rbtn_data->rfkill)
 157                rbtn_rfkill_query(rbtn_data->rfkill, device);
 158}
 159
 160
 161/*
 162 * input device
 163 */
 164
 165static int rbtn_input_init(struct rbtn_data *rbtn_data)
 166{
 167        int ret;
 168
 169        rbtn_data->input_dev = input_allocate_device();
 170        if (!rbtn_data->input_dev)
 171                return -ENOMEM;
 172
 173        rbtn_data->input_dev->name = "DELL Wireless hotkeys";
 174        rbtn_data->input_dev->phys = "dellabce/input0";
 175        rbtn_data->input_dev->id.bustype = BUS_HOST;
 176        rbtn_data->input_dev->evbit[0] = BIT(EV_KEY);
 177        set_bit(KEY_RFKILL, rbtn_data->input_dev->keybit);
 178
 179        ret = input_register_device(rbtn_data->input_dev);
 180        if (ret) {
 181                input_free_device(rbtn_data->input_dev);
 182                rbtn_data->input_dev = NULL;
 183                return ret;
 184        }
 185
 186        return 0;
 187}
 188
 189static void rbtn_input_exit(struct rbtn_data *rbtn_data)
 190{
 191        input_unregister_device(rbtn_data->input_dev);
 192        rbtn_data->input_dev = NULL;
 193}
 194
 195static void rbtn_input_event(struct rbtn_data *rbtn_data)
 196{
 197        input_report_key(rbtn_data->input_dev, KEY_RFKILL, 1);
 198        input_sync(rbtn_data->input_dev);
 199        input_report_key(rbtn_data->input_dev, KEY_RFKILL, 0);
 200        input_sync(rbtn_data->input_dev);
 201}
 202
 203
 204/*
 205 * acpi driver
 206 */
 207
 208static int rbtn_add(struct acpi_device *device);
 209static int rbtn_remove(struct acpi_device *device);
 210static void rbtn_notify(struct acpi_device *device, u32 event);
 211
 212static const struct acpi_device_id rbtn_ids[] = {
 213        { "DELRBTN", 0 },
 214        { "DELLABCE", 0 },
 215
 216        /*
 217         * This driver can also handle the "DELLABC6" device that
 218         * appears on the XPS 13 9350, but that device is disabled by
 219         * the DSDT unless booted with acpi_osi="!Windows 2012"
 220         * acpi_osi="!Windows 2013".
 221         *
 222         * According to Mario at Dell:
 223         *
 224         *  DELLABC6 is a custom interface that was created solely to
 225         *  have airplane mode support for Windows 7.  For Windows 10
 226         *  the proper interface is to use that which is handled by
 227         *  intel-hid. A OEM airplane mode driver is not used.
 228         *
 229         *  Since the kernel doesn't identify as Windows 7 it would be
 230         *  incorrect to do attempt to use that interface.
 231         *
 232         * Even if we override _OSI and bind to DELLABC6, we end up with
 233         * inconsistent behavior in which userspace can get out of sync
 234         * with the rfkill state as it conflicts with events from
 235         * intel-hid.
 236         *
 237         * The upshot is that it is better to just ignore DELLABC6
 238         * devices.
 239         */
 240
 241        { "", 0 },
 242};
 243
 244#ifdef CONFIG_PM_SLEEP
 245static void ACPI_SYSTEM_XFACE rbtn_clear_suspended_flag(void *context)
 246{
 247        struct rbtn_data *rbtn_data = context;
 248
 249        rbtn_data->suspended = false;
 250}
 251
 252static int rbtn_suspend(struct device *dev)
 253{
 254        struct acpi_device *device = to_acpi_device(dev);
 255        struct rbtn_data *rbtn_data = acpi_driver_data(device);
 256
 257        rbtn_data->suspended = true;
 258
 259        return 0;
 260}
 261
 262static int rbtn_resume(struct device *dev)
 263{
 264        struct acpi_device *device = to_acpi_device(dev);
 265        struct rbtn_data *rbtn_data = acpi_driver_data(device);
 266        acpi_status status;
 267
 268        /*
 269         * Upon resume, some BIOSes send an ACPI notification thet triggers
 270         * an unwanted input event. In order to ignore it, we use a flag
 271         * that we set at suspend and clear once we have received the extra
 272         * ACPI notification. Since ACPI notifications are delivered
 273         * asynchronously to drivers, we clear the flag from the workqueue
 274         * used to deliver the notifications. This should be enough
 275         * to have the flag cleared only after we received the extra
 276         * notification, if any.
 277         */
 278        status = acpi_os_execute(OSL_NOTIFY_HANDLER,
 279                         rbtn_clear_suspended_flag, rbtn_data);
 280        if (ACPI_FAILURE(status))
 281                rbtn_clear_suspended_flag(rbtn_data);
 282
 283        return 0;
 284}
 285#endif
 286
 287static SIMPLE_DEV_PM_OPS(rbtn_pm_ops, rbtn_suspend, rbtn_resume);
 288
 289static struct acpi_driver rbtn_driver = {
 290        .name = "dell-rbtn",
 291        .ids = rbtn_ids,
 292        .drv.pm = &rbtn_pm_ops,
 293        .ops = {
 294                .add = rbtn_add,
 295                .remove = rbtn_remove,
 296                .notify = rbtn_notify,
 297        },
 298        .owner = THIS_MODULE,
 299};
 300
 301
 302/*
 303 * notifier export functions
 304 */
 305
 306static bool auto_remove_rfkill = true;
 307
 308static ATOMIC_NOTIFIER_HEAD(rbtn_chain_head);
 309
 310static int rbtn_inc_count(struct device *dev, void *data)
 311{
 312        struct acpi_device *device = to_acpi_device(dev);
 313        struct rbtn_data *rbtn_data = device->driver_data;
 314        int *count = data;
 315
 316        if (rbtn_data->type == RBTN_SLIDER)
 317                (*count)++;
 318
 319        return 0;
 320}
 321
 322static int rbtn_switch_dev(struct device *dev, void *data)
 323{
 324        struct acpi_device *device = to_acpi_device(dev);
 325        struct rbtn_data *rbtn_data = device->driver_data;
 326        bool enable = data;
 327
 328        if (rbtn_data->type != RBTN_SLIDER)
 329                return 0;
 330
 331        if (enable)
 332                rbtn_rfkill_init(device);
 333        else
 334                rbtn_rfkill_exit(device);
 335
 336        return 0;
 337}
 338
 339int dell_rbtn_notifier_register(struct notifier_block *nb)
 340{
 341        bool first;
 342        int count;
 343        int ret;
 344
 345        count = 0;
 346        ret = driver_for_each_device(&rbtn_driver.drv, NULL, &count,
 347                                     rbtn_inc_count);
 348        if (ret || count == 0)
 349                return -ENODEV;
 350
 351        first = !rbtn_chain_head.head;
 352
 353        ret = atomic_notifier_chain_register(&rbtn_chain_head, nb);
 354        if (ret != 0)
 355                return ret;
 356
 357        if (auto_remove_rfkill && first)
 358                ret = driver_for_each_device(&rbtn_driver.drv, NULL,
 359                                             (void *)false, rbtn_switch_dev);
 360
 361        return ret;
 362}
 363EXPORT_SYMBOL_GPL(dell_rbtn_notifier_register);
 364
 365int dell_rbtn_notifier_unregister(struct notifier_block *nb)
 366{
 367        int ret;
 368
 369        ret = atomic_notifier_chain_unregister(&rbtn_chain_head, nb);
 370        if (ret != 0)
 371                return ret;
 372
 373        if (auto_remove_rfkill && !rbtn_chain_head.head)
 374                ret = driver_for_each_device(&rbtn_driver.drv, NULL,
 375                                             (void *)true, rbtn_switch_dev);
 376
 377        return ret;
 378}
 379EXPORT_SYMBOL_GPL(dell_rbtn_notifier_unregister);
 380
 381
 382/*
 383 * acpi driver functions
 384 */
 385
 386static int rbtn_add(struct acpi_device *device)
 387{
 388        struct rbtn_data *rbtn_data;
 389        enum rbtn_type type;
 390        int ret = 0;
 391
 392        type = rbtn_check(device);
 393        if (type == RBTN_UNKNOWN) {
 394                dev_info(&device->dev, "Unknown device type\n");
 395                return -EINVAL;
 396        }
 397
 398        ret = rbtn_acquire(device, true);
 399        if (ret < 0) {
 400                dev_err(&device->dev, "Cannot enable device\n");
 401                return ret;
 402        }
 403
 404        rbtn_data = devm_kzalloc(&device->dev, sizeof(*rbtn_data), GFP_KERNEL);
 405        if (!rbtn_data)
 406                return -ENOMEM;
 407
 408        rbtn_data->type = type;
 409        device->driver_data = rbtn_data;
 410
 411        switch (rbtn_data->type) {
 412        case RBTN_TOGGLE:
 413                ret = rbtn_input_init(rbtn_data);
 414                break;
 415        case RBTN_SLIDER:
 416                if (auto_remove_rfkill && rbtn_chain_head.head)
 417                        ret = 0;
 418                else
 419                        ret = rbtn_rfkill_init(device);
 420                break;
 421        default:
 422                ret = -EINVAL;
 423        }
 424
 425        return ret;
 426
 427}
 428
 429static int rbtn_remove(struct acpi_device *device)
 430{
 431        struct rbtn_data *rbtn_data = device->driver_data;
 432
 433        switch (rbtn_data->type) {
 434        case RBTN_TOGGLE:
 435                rbtn_input_exit(rbtn_data);
 436                break;
 437        case RBTN_SLIDER:
 438                rbtn_rfkill_exit(device);
 439                break;
 440        default:
 441                break;
 442        }
 443
 444        rbtn_acquire(device, false);
 445        device->driver_data = NULL;
 446
 447        return 0;
 448}
 449
 450static void rbtn_notify(struct acpi_device *device, u32 event)
 451{
 452        struct rbtn_data *rbtn_data = device->driver_data;
 453
 454        /*
 455         * Some BIOSes send a notification at resume.
 456         * Ignore it to prevent unwanted input events.
 457         */
 458        if (rbtn_data->suspended) {
 459                dev_dbg(&device->dev, "ACPI notification ignored\n");
 460                return;
 461        }
 462
 463        if (event != 0x80) {
 464                dev_info(&device->dev, "Received unknown event (0x%x)\n",
 465                         event);
 466                return;
 467        }
 468
 469        switch (rbtn_data->type) {
 470        case RBTN_TOGGLE:
 471                rbtn_input_event(rbtn_data);
 472                break;
 473        case RBTN_SLIDER:
 474                rbtn_rfkill_event(device);
 475                atomic_notifier_call_chain(&rbtn_chain_head, event, device);
 476                break;
 477        default:
 478                break;
 479        }
 480}
 481
 482
 483/*
 484 * module functions
 485 */
 486
 487module_acpi_driver(rbtn_driver);
 488
 489module_param(auto_remove_rfkill, bool, 0444);
 490
 491MODULE_PARM_DESC(auto_remove_rfkill, "Automatically remove rfkill devices when "
 492                                     "other modules start receiving events "
 493                                     "from this module and re-add them when "
 494                                     "the last module stops receiving events "
 495                                     "(default true)");
 496MODULE_DEVICE_TABLE(acpi, rbtn_ids);
 497MODULE_DESCRIPTION("Dell Airplane Mode Switch driver");
 498MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
 499MODULE_LICENSE("GPL");
 500