linux/drivers/staging/quickstart/quickstart.c
<<
>>
Prefs
   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);
 479