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 ?
 145                         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
 360        device_remove_file(&pf_device->dev, &dev_attr_pressed_button);
 361        device_remove_file(&pf_device->dev, &dev_attr_buttons);
 362
 363        platform_device_unregister(pf_device);
 364
 365        platform_driver_unregister(&pf_driver);
 366
 367        acpi_bus_unregister_driver(&quickstart_acpi_driver);
 368
 369        quickstart_btnlst_free();
 370
 371        return;
 372}
 373
 374static int __init quickstart_init_input(void)
 375{
 376        struct quickstart_btn **ptr = &quickstart_data.btn_lst;
 377        int count;
 378        int ret;
 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        ret = input_register_device(quickstart_input);
 396        if (ret) {
 397                input_free_device(quickstart_input);
 398                return ret;
 399        }
 400
 401        return 0;
 402}
 403
 404static int __init quickstart_init(void)
 405{
 406        int ret;
 407
 408        /* ACPI Check */
 409        if (acpi_disabled)
 410                return -ENODEV;
 411
 412        /* ACPI driver register */
 413        ret = acpi_bus_register_driver(&quickstart_acpi_driver);
 414        if (ret)
 415                return ret;
 416
 417        /* If existing bus with no devices */
 418        if (!quickstart_data.btn_lst) {
 419                ret = -ENODEV;
 420                goto fail_pfdrv_reg;
 421        }
 422
 423        /* Platform driver register */
 424        ret = platform_driver_register(&pf_driver);
 425        if (ret)
 426                goto fail_pfdrv_reg;
 427
 428        /* Platform device register */
 429        pf_device = platform_device_alloc(QUICKSTART_PF_DEVICE_NAME, -1);
 430        if (!pf_device) {
 431                ret = -ENOMEM;
 432                goto fail_pfdev_alloc;
 433        }
 434        ret = platform_device_add(pf_device);
 435        if (ret)
 436                goto fail_pfdev_add;
 437
 438        /* Create device sysfs file */
 439        ret = device_create_file(&pf_device->dev, &dev_attr_pressed_button);
 440        if (ret)
 441                goto fail_dev_file;
 442
 443        ret = device_create_file(&pf_device->dev, &dev_attr_buttons);
 444        if (ret)
 445                goto fail_dev_file2;
 446
 447
 448        /* Input device */
 449        ret = quickstart_init_input();
 450        if (ret)
 451                goto fail_input;
 452
 453        printk(KERN_INFO "quickstart: ACPI Direct App Launch ver %s\n",
 454                                                QUICKSTART_VERSION);
 455
 456        return 0;
 457fail_input:
 458        device_remove_file(&pf_device->dev, &dev_attr_buttons);
 459
 460fail_dev_file2:
 461        device_remove_file(&pf_device->dev, &dev_attr_pressed_button);
 462
 463fail_dev_file:
 464        platform_device_del(pf_device);
 465
 466fail_pfdev_add:
 467        platform_device_put(pf_device);
 468
 469fail_pfdev_alloc:
 470        platform_driver_unregister(&pf_driver);
 471
 472fail_pfdrv_reg:
 473        acpi_bus_unregister_driver(&quickstart_acpi_driver);
 474
 475        return ret;
 476}
 477
 478module_init(quickstart_init);
 479module_exit(quickstart_exit);
 480
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.