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