Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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