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