at v3.14-rc4 458 lines 10 kB view raw
1/* 2 * quickstart.c - ACPI Direct App Launch driver 3 * 4 * 5 * Copyright (C) 2007-2010 Angelo Arrifano <miknix@gmail.com> 6 * 7 * Information gathered from disassembled dsdt and from here: 8 * <http://www.microsoft.com/whdc/system/platform/firmware/DirAppLaunch.mspx> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 * 24 */ 25 26#define QUICKSTART_VERSION "1.04" 27 28#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 29 30#include <linux/kernel.h> 31#include <linux/module.h> 32#include <linux/init.h> 33#include <linux/types.h> 34#include <linux/acpi.h> 35#include <linux/platform_device.h> 36#include <linux/input.h> 37 38MODULE_AUTHOR("Angelo Arrifano"); 39MODULE_DESCRIPTION("ACPI Direct App Launch driver"); 40MODULE_LICENSE("GPL"); 41 42#define QUICKSTART_ACPI_DEVICE_NAME "quickstart" 43#define QUICKSTART_ACPI_CLASS "quickstart" 44#define QUICKSTART_ACPI_HID "PNP0C32" 45 46#define QUICKSTART_PF_DRIVER_NAME "quickstart" 47#define QUICKSTART_PF_DEVICE_NAME "quickstart" 48 49/* 50 * There will be two events: 51 * 0x02 - A hot button was pressed while device was off/sleeping. 52 * 0x80 - A hot button was pressed while device was up. 53 */ 54#define QUICKSTART_EVENT_WAKE 0x02 55#define QUICKSTART_EVENT_RUNTIME 0x80 56 57struct quickstart_button { 58 char *name; 59 unsigned int id; 60 struct list_head list; 61}; 62 63struct quickstart_acpi { 64 struct acpi_device *device; 65 struct quickstart_button *button; 66}; 67 68static LIST_HEAD(buttons); 69static struct quickstart_button *pressed; 70 71static struct input_dev *quickstart_input; 72 73/* Platform driver functions */ 74static ssize_t buttons_show(struct device *dev, struct device_attribute *attr, 75 char *buf) 76{ 77 int count = 0; 78 struct quickstart_button *b; 79 80 if (list_empty(&buttons)) 81 return snprintf(buf, PAGE_SIZE, "none"); 82 83 list_for_each_entry(b, &buttons, list) { 84 count += snprintf(buf + count, PAGE_SIZE - count, "%u\t%s\n", 85 b->id, b->name); 86 87 if (count >= PAGE_SIZE) { 88 count = PAGE_SIZE; 89 break; 90 } 91 } 92 93 return count; 94} 95 96static ssize_t pressed_button_show(struct device *dev, 97 struct device_attribute *attr, char *buf) 98{ 99 return scnprintf(buf, PAGE_SIZE, "%s\n", 100 (pressed ? pressed->name : "none")); 101} 102 103 104static ssize_t pressed_button_store(struct device *dev, 105 struct device_attribute *attr, 106 const char *buf, size_t count) 107{ 108 if (count < 2) 109 return -EINVAL; 110 111 if (strncasecmp(buf, "none", 4) != 0) 112 return -EINVAL; 113 114 pressed = NULL; 115 return count; 116} 117 118/* Helper functions */ 119static struct quickstart_button *quickstart_buttons_add(void) 120{ 121 struct quickstart_button *b; 122 123 b = kzalloc(sizeof(*b), GFP_KERNEL); 124 if (!b) 125 return NULL; 126 127 list_add_tail(&b->list, &buttons); 128 129 return b; 130} 131 132static void quickstart_button_del(struct quickstart_button *data) 133{ 134 if (!data) 135 return; 136 137 list_del(&data->list); 138 kfree(data->name); 139 kfree(data); 140} 141 142static void quickstart_buttons_free(void) 143{ 144 struct quickstart_button *b, *n; 145 146 list_for_each_entry_safe(b, n, &buttons, list) 147 quickstart_button_del(b); 148} 149 150/* ACPI Driver functions */ 151static void quickstart_acpi_notify(acpi_handle handle, u32 event, void *data) 152{ 153 struct quickstart_acpi *quickstart = data; 154 155 if (!quickstart) 156 return; 157 158 switch (event) { 159 case QUICKSTART_EVENT_WAKE: 160 pressed = quickstart->button; 161 break; 162 case QUICKSTART_EVENT_RUNTIME: 163 input_report_key(quickstart_input, quickstart->button->id, 1); 164 input_sync(quickstart_input); 165 input_report_key(quickstart_input, quickstart->button->id, 0); 166 input_sync(quickstart_input); 167 break; 168 default: 169 pr_err("Unexpected ACPI event notify (%u)\n", event); 170 break; 171 } 172} 173 174static int quickstart_acpi_ghid(struct quickstart_acpi *quickstart) 175{ 176 acpi_status status; 177 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 178 int ret = 0; 179 180 /* 181 * This returns a buffer telling the button usage ID, 182 * and triggers pending notify events (The ones before booting). 183 */ 184 status = acpi_evaluate_object(quickstart->device->handle, "GHID", NULL, 185 &buffer); 186 if (ACPI_FAILURE(status)) { 187 pr_err("%s GHID method failed\n", quickstart->button->name); 188 return -EINVAL; 189 } 190 191 /* 192 * <<The GHID method can return a BYTE, WORD, or DWORD. 193 * The value must be encoded in little-endian byte 194 * order (least significant byte first).>> 195 */ 196 switch (buffer.length) { 197 case 1: 198 quickstart->button->id = *(uint8_t *)buffer.pointer; 199 break; 200 case 2: 201 quickstart->button->id = *(uint16_t *)buffer.pointer; 202 break; 203 case 4: 204 quickstart->button->id = *(uint32_t *)buffer.pointer; 205 break; 206 case 8: 207 quickstart->button->id = *(uint64_t *)buffer.pointer; 208 break; 209 default: 210 pr_err("%s GHID method returned buffer of unexpected length %lu\n", 211 quickstart->button->name, 212 (unsigned long)buffer.length); 213 ret = -EINVAL; 214 break; 215 } 216 217 kfree(buffer.pointer); 218 219 return ret; 220} 221 222static int quickstart_acpi_config(struct quickstart_acpi *quickstart) 223{ 224 char *bid = acpi_device_bid(quickstart->device); 225 char *name; 226 227 name = kmalloc(strlen(bid) + 1, GFP_KERNEL); 228 if (!name) 229 return -ENOMEM; 230 231 /* Add new button to list */ 232 quickstart->button = quickstart_buttons_add(); 233 if (!quickstart->button) { 234 kfree(name); 235 return -ENOMEM; 236 } 237 238 quickstart->button->name = name; 239 strcpy(quickstart->button->name, bid); 240 241 return 0; 242} 243 244static int quickstart_acpi_add(struct acpi_device *device) 245{ 246 int ret; 247 acpi_status status; 248 struct quickstart_acpi *quickstart; 249 250 if (!device) 251 return -EINVAL; 252 253 quickstart = kzalloc(sizeof(*quickstart), GFP_KERNEL); 254 if (!quickstart) 255 return -ENOMEM; 256 257 quickstart->device = device; 258 259 strcpy(acpi_device_name(device), QUICKSTART_ACPI_DEVICE_NAME); 260 strcpy(acpi_device_class(device), QUICKSTART_ACPI_CLASS); 261 device->driver_data = quickstart; 262 263 /* Add button to list and initialize some stuff */ 264 ret = quickstart_acpi_config(quickstart); 265 if (ret < 0) 266 goto fail_config; 267 268 status = acpi_install_notify_handler(device->handle, ACPI_ALL_NOTIFY, 269 quickstart_acpi_notify, 270 quickstart); 271 if (ACPI_FAILURE(status)) { 272 pr_err("Notify handler install error\n"); 273 ret = -ENODEV; 274 goto fail_installnotify; 275 } 276 277 ret = quickstart_acpi_ghid(quickstart); 278 if (ret < 0) 279 goto fail_ghid; 280 281 return 0; 282 283fail_ghid: 284 acpi_remove_notify_handler(device->handle, ACPI_ALL_NOTIFY, 285 quickstart_acpi_notify); 286 287fail_installnotify: 288 quickstart_button_del(quickstart->button); 289 290fail_config: 291 292 kfree(quickstart); 293 294 return ret; 295} 296 297static int quickstart_acpi_remove(struct acpi_device *device) 298{ 299 acpi_status status; 300 struct quickstart_acpi *quickstart; 301 302 if (!device) 303 return -EINVAL; 304 305 quickstart = acpi_driver_data(device); 306 if (!quickstart) 307 return -EINVAL; 308 309 status = acpi_remove_notify_handler(device->handle, ACPI_ALL_NOTIFY, 310 quickstart_acpi_notify); 311 if (ACPI_FAILURE(status)) 312 pr_err("Error removing notify handler\n"); 313 314 kfree(quickstart); 315 316 return 0; 317} 318 319/* Platform driver structs */ 320static DEVICE_ATTR_RW(pressed_button); 321static DEVICE_ATTR_RO(buttons); 322static struct platform_device *pf_device; 323static struct platform_driver pf_driver = { 324 .driver = { 325 .name = QUICKSTART_PF_DRIVER_NAME, 326 .owner = THIS_MODULE, 327 } 328}; 329 330static const struct acpi_device_id quickstart_device_ids[] = { 331 {QUICKSTART_ACPI_HID, 0}, 332 {"", 0}, 333}; 334 335static struct acpi_driver quickstart_acpi_driver = { 336 .name = "quickstart", 337 .class = QUICKSTART_ACPI_CLASS, 338 .ids = quickstart_device_ids, 339 .ops = { 340 .add = quickstart_acpi_add, 341 .remove = quickstart_acpi_remove, 342 }, 343}; 344 345/* Module functions */ 346static void quickstart_exit(void) 347{ 348 input_unregister_device(quickstart_input); 349 350 device_remove_file(&pf_device->dev, &dev_attr_pressed_button); 351 device_remove_file(&pf_device->dev, &dev_attr_buttons); 352 353 platform_device_unregister(pf_device); 354 355 platform_driver_unregister(&pf_driver); 356 357 acpi_bus_unregister_driver(&quickstart_acpi_driver); 358 359 quickstart_buttons_free(); 360} 361 362static int __init quickstart_init_input(void) 363{ 364 struct quickstart_button *b; 365 int ret; 366 367 quickstart_input = input_allocate_device(); 368 369 if (!quickstart_input) 370 return -ENOMEM; 371 372 quickstart_input->name = "Quickstart ACPI Buttons"; 373 quickstart_input->id.bustype = BUS_HOST; 374 375 list_for_each_entry(b, &buttons, list) { 376 set_bit(EV_KEY, quickstart_input->evbit); 377 set_bit(b->id, quickstart_input->keybit); 378 } 379 380 ret = input_register_device(quickstart_input); 381 if (ret) { 382 input_free_device(quickstart_input); 383 return ret; 384 } 385 386 return 0; 387} 388 389static int __init quickstart_init(void) 390{ 391 int ret; 392 393 /* ACPI driver register */ 394 ret = acpi_bus_register_driver(&quickstart_acpi_driver); 395 if (ret) 396 return ret; 397 398 /* If existing bus with no devices */ 399 if (list_empty(&buttons)) { 400 ret = -ENODEV; 401 goto fail_pfdrv_reg; 402 } 403 404 /* Platform driver register */ 405 ret = platform_driver_register(&pf_driver); 406 if (ret) 407 goto fail_pfdrv_reg; 408 409 /* Platform device register */ 410 pf_device = platform_device_alloc(QUICKSTART_PF_DEVICE_NAME, -1); 411 if (!pf_device) { 412 ret = -ENOMEM; 413 goto fail_pfdev_alloc; 414 } 415 ret = platform_device_add(pf_device); 416 if (ret) 417 goto fail_pfdev_add; 418 419 /* Create device sysfs file */ 420 ret = device_create_file(&pf_device->dev, &dev_attr_pressed_button); 421 if (ret) 422 goto fail_dev_file; 423 424 ret = device_create_file(&pf_device->dev, &dev_attr_buttons); 425 if (ret) 426 goto fail_dev_file2; 427 428 /* Input device */ 429 ret = quickstart_init_input(); 430 if (ret) 431 goto fail_input; 432 433 pr_info("ACPI Direct App Launch ver %s\n", QUICKSTART_VERSION); 434 435 return 0; 436fail_input: 437 device_remove_file(&pf_device->dev, &dev_attr_buttons); 438 439fail_dev_file2: 440 device_remove_file(&pf_device->dev, &dev_attr_pressed_button); 441 442fail_dev_file: 443 platform_device_del(pf_device); 444 445fail_pfdev_add: 446 platform_device_put(pf_device); 447 448fail_pfdev_alloc: 449 platform_driver_unregister(&pf_driver); 450 451fail_pfdrv_reg: 452 acpi_bus_unregister_driver(&quickstart_acpi_driver); 453 454 return ret; 455} 456 457module_init(quickstart_init); 458module_exit(quickstart_exit);