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.5-rc1 423 lines 8.8 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}; 32 33 34/* 35 * acpi functions 36 */ 37 38static enum rbtn_type rbtn_check(struct acpi_device *device) 39{ 40 unsigned long long output; 41 acpi_status status; 42 43 status = acpi_evaluate_integer(device->handle, "CRBT", NULL, &output); 44 if (ACPI_FAILURE(status)) 45 return RBTN_UNKNOWN; 46 47 switch (output) { 48 case 0: 49 case 1: 50 return RBTN_TOGGLE; 51 case 2: 52 case 3: 53 return RBTN_SLIDER; 54 default: 55 return RBTN_UNKNOWN; 56 } 57} 58 59static int rbtn_get(struct acpi_device *device) 60{ 61 unsigned long long output; 62 acpi_status status; 63 64 status = acpi_evaluate_integer(device->handle, "GRBT", NULL, &output); 65 if (ACPI_FAILURE(status)) 66 return -EINVAL; 67 68 return !output; 69} 70 71static int rbtn_acquire(struct acpi_device *device, bool enable) 72{ 73 struct acpi_object_list input; 74 union acpi_object param; 75 acpi_status status; 76 77 param.type = ACPI_TYPE_INTEGER; 78 param.integer.value = enable; 79 input.count = 1; 80 input.pointer = &param; 81 82 status = acpi_evaluate_object(device->handle, "ARBT", &input, NULL); 83 if (ACPI_FAILURE(status)) 84 return -EINVAL; 85 86 return 0; 87} 88 89 90/* 91 * rfkill device 92 */ 93 94static void rbtn_rfkill_query(struct rfkill *rfkill, void *data) 95{ 96 struct acpi_device *device = data; 97 int state; 98 99 state = rbtn_get(device); 100 if (state < 0) 101 return; 102 103 rfkill_set_states(rfkill, state, state); 104} 105 106static int rbtn_rfkill_set_block(void *data, bool blocked) 107{ 108 /* NOTE: setting soft rfkill state is not supported */ 109 return -EINVAL; 110} 111 112static struct rfkill_ops rbtn_ops = { 113 .query = rbtn_rfkill_query, 114 .set_block = rbtn_rfkill_set_block, 115}; 116 117static int rbtn_rfkill_init(struct acpi_device *device) 118{ 119 struct rbtn_data *rbtn_data = device->driver_data; 120 int ret; 121 122 if (rbtn_data->rfkill) 123 return 0; 124 125 /* 126 * NOTE: rbtn controls all radio devices, not only WLAN 127 * but rfkill interface does not support "ANY" type 128 * so "WLAN" type is used 129 */ 130 rbtn_data->rfkill = rfkill_alloc("dell-rbtn", &device->dev, 131 RFKILL_TYPE_WLAN, &rbtn_ops, device); 132 if (!rbtn_data->rfkill) 133 return -ENOMEM; 134 135 ret = rfkill_register(rbtn_data->rfkill); 136 if (ret) { 137 rfkill_destroy(rbtn_data->rfkill); 138 rbtn_data->rfkill = NULL; 139 return ret; 140 } 141 142 return 0; 143} 144 145static void rbtn_rfkill_exit(struct acpi_device *device) 146{ 147 struct rbtn_data *rbtn_data = device->driver_data; 148 149 if (!rbtn_data->rfkill) 150 return; 151 152 rfkill_unregister(rbtn_data->rfkill); 153 rfkill_destroy(rbtn_data->rfkill); 154 rbtn_data->rfkill = NULL; 155} 156 157static void rbtn_rfkill_event(struct acpi_device *device) 158{ 159 struct rbtn_data *rbtn_data = device->driver_data; 160 161 if (rbtn_data->rfkill) 162 rbtn_rfkill_query(rbtn_data->rfkill, device); 163} 164 165 166/* 167 * input device 168 */ 169 170static int rbtn_input_init(struct rbtn_data *rbtn_data) 171{ 172 int ret; 173 174 rbtn_data->input_dev = input_allocate_device(); 175 if (!rbtn_data->input_dev) 176 return -ENOMEM; 177 178 rbtn_data->input_dev->name = "DELL Wireless hotkeys"; 179 rbtn_data->input_dev->phys = "dellabce/input0"; 180 rbtn_data->input_dev->id.bustype = BUS_HOST; 181 rbtn_data->input_dev->evbit[0] = BIT(EV_KEY); 182 set_bit(KEY_RFKILL, rbtn_data->input_dev->keybit); 183 184 ret = input_register_device(rbtn_data->input_dev); 185 if (ret) { 186 input_free_device(rbtn_data->input_dev); 187 rbtn_data->input_dev = NULL; 188 return ret; 189 } 190 191 return 0; 192} 193 194static void rbtn_input_exit(struct rbtn_data *rbtn_data) 195{ 196 input_unregister_device(rbtn_data->input_dev); 197 rbtn_data->input_dev = NULL; 198} 199 200static void rbtn_input_event(struct rbtn_data *rbtn_data) 201{ 202 input_report_key(rbtn_data->input_dev, KEY_RFKILL, 1); 203 input_sync(rbtn_data->input_dev); 204 input_report_key(rbtn_data->input_dev, KEY_RFKILL, 0); 205 input_sync(rbtn_data->input_dev); 206} 207 208 209/* 210 * acpi driver 211 */ 212 213static int rbtn_add(struct acpi_device *device); 214static int rbtn_remove(struct acpi_device *device); 215static void rbtn_notify(struct acpi_device *device, u32 event); 216 217static const struct acpi_device_id rbtn_ids[] = { 218 { "DELRBTN", 0 }, 219 { "DELLABCE", 0 }, 220 { "", 0 }, 221}; 222 223static struct acpi_driver rbtn_driver = { 224 .name = "dell-rbtn", 225 .ids = rbtn_ids, 226 .ops = { 227 .add = rbtn_add, 228 .remove = rbtn_remove, 229 .notify = rbtn_notify, 230 }, 231 .owner = THIS_MODULE, 232}; 233 234 235/* 236 * notifier export functions 237 */ 238 239static bool auto_remove_rfkill = true; 240 241static ATOMIC_NOTIFIER_HEAD(rbtn_chain_head); 242 243static int rbtn_inc_count(struct device *dev, void *data) 244{ 245 struct acpi_device *device = to_acpi_device(dev); 246 struct rbtn_data *rbtn_data = device->driver_data; 247 int *count = data; 248 249 if (rbtn_data->type == RBTN_SLIDER) 250 (*count)++; 251 252 return 0; 253} 254 255static int rbtn_switch_dev(struct device *dev, void *data) 256{ 257 struct acpi_device *device = to_acpi_device(dev); 258 struct rbtn_data *rbtn_data = device->driver_data; 259 bool enable = data; 260 261 if (rbtn_data->type != RBTN_SLIDER) 262 return 0; 263 264 if (enable) 265 rbtn_rfkill_init(device); 266 else 267 rbtn_rfkill_exit(device); 268 269 return 0; 270} 271 272int dell_rbtn_notifier_register(struct notifier_block *nb) 273{ 274 bool first; 275 int count; 276 int ret; 277 278 count = 0; 279 ret = driver_for_each_device(&rbtn_driver.drv, NULL, &count, 280 rbtn_inc_count); 281 if (ret || count == 0) 282 return -ENODEV; 283 284 first = !rbtn_chain_head.head; 285 286 ret = atomic_notifier_chain_register(&rbtn_chain_head, nb); 287 if (ret != 0) 288 return ret; 289 290 if (auto_remove_rfkill && first) 291 ret = driver_for_each_device(&rbtn_driver.drv, NULL, 292 (void *)false, rbtn_switch_dev); 293 294 return ret; 295} 296EXPORT_SYMBOL_GPL(dell_rbtn_notifier_register); 297 298int dell_rbtn_notifier_unregister(struct notifier_block *nb) 299{ 300 int ret; 301 302 ret = atomic_notifier_chain_unregister(&rbtn_chain_head, nb); 303 if (ret != 0) 304 return ret; 305 306 if (auto_remove_rfkill && !rbtn_chain_head.head) 307 ret = driver_for_each_device(&rbtn_driver.drv, NULL, 308 (void *)true, rbtn_switch_dev); 309 310 return ret; 311} 312EXPORT_SYMBOL_GPL(dell_rbtn_notifier_unregister); 313 314 315/* 316 * acpi driver functions 317 */ 318 319static int rbtn_add(struct acpi_device *device) 320{ 321 struct rbtn_data *rbtn_data; 322 enum rbtn_type type; 323 int ret = 0; 324 325 type = rbtn_check(device); 326 if (type == RBTN_UNKNOWN) { 327 dev_info(&device->dev, "Unknown device type\n"); 328 return -EINVAL; 329 } 330 331 ret = rbtn_acquire(device, true); 332 if (ret < 0) { 333 dev_err(&device->dev, "Cannot enable device\n"); 334 return ret; 335 } 336 337 rbtn_data = devm_kzalloc(&device->dev, sizeof(*rbtn_data), GFP_KERNEL); 338 if (!rbtn_data) 339 return -ENOMEM; 340 341 rbtn_data->type = type; 342 device->driver_data = rbtn_data; 343 344 switch (rbtn_data->type) { 345 case RBTN_TOGGLE: 346 ret = rbtn_input_init(rbtn_data); 347 break; 348 case RBTN_SLIDER: 349 if (auto_remove_rfkill && rbtn_chain_head.head) 350 ret = 0; 351 else 352 ret = rbtn_rfkill_init(device); 353 break; 354 default: 355 ret = -EINVAL; 356 } 357 358 return ret; 359 360} 361 362static int rbtn_remove(struct acpi_device *device) 363{ 364 struct rbtn_data *rbtn_data = device->driver_data; 365 366 switch (rbtn_data->type) { 367 case RBTN_TOGGLE: 368 rbtn_input_exit(rbtn_data); 369 break; 370 case RBTN_SLIDER: 371 rbtn_rfkill_exit(device); 372 break; 373 default: 374 break; 375 } 376 377 rbtn_acquire(device, false); 378 device->driver_data = NULL; 379 380 return 0; 381} 382 383static void rbtn_notify(struct acpi_device *device, u32 event) 384{ 385 struct rbtn_data *rbtn_data = device->driver_data; 386 387 if (event != 0x80) { 388 dev_info(&device->dev, "Received unknown event (0x%x)\n", 389 event); 390 return; 391 } 392 393 switch (rbtn_data->type) { 394 case RBTN_TOGGLE: 395 rbtn_input_event(rbtn_data); 396 break; 397 case RBTN_SLIDER: 398 rbtn_rfkill_event(device); 399 atomic_notifier_call_chain(&rbtn_chain_head, event, device); 400 break; 401 default: 402 break; 403 } 404} 405 406 407/* 408 * module functions 409 */ 410 411module_acpi_driver(rbtn_driver); 412 413module_param(auto_remove_rfkill, bool, 0444); 414 415MODULE_PARM_DESC(auto_remove_rfkill, "Automatically remove rfkill devices when " 416 "other modules start receiving events " 417 "from this module and re-add them when " 418 "the last module stops receiving events " 419 "(default true)"); 420MODULE_DEVICE_TABLE(acpi, rbtn_ids); 421MODULE_DESCRIPTION("Dell Airplane Mode Switch driver"); 422MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>"); 423MODULE_LICENSE("GPL");