dell-rbtn.c (10752B)
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 = ¶m; 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");