linux/drivers/input/misc/apanel.c
<<
>>
Prefs
   1/*
   2 *  Fujitsu Lifebook Application Panel button drive
   3 *
   4 *  Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org>
   5 *  Copyright (C) 2001-2003 Jochen Eisinger <jochen@penguin-breeder.org>
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License version 2 as published by
   9 * the Free Software Foundation.
  10 *
  11 * Many Fujitsu Lifebook laptops have a small panel of buttons that are
  12 * accessible via the i2c/smbus interface. This driver polls those
  13 * buttons and generates input events.
  14 *
  15 * For more details see:
  16 *      http://apanel.sourceforge.net/tech.php
  17 */
  18
  19#include <linux/kernel.h>
  20#include <linux/module.h>
  21#include <linux/ioport.h>
  22#include <linux/io.h>
  23#include <linux/input-polldev.h>
  24#include <linux/i2c.h>
  25#include <linux/workqueue.h>
  26#include <linux/leds.h>
  27
  28#define APANEL_NAME     "Fujitsu Application Panel"
  29#define APANEL          "apanel"
  30
  31/* How often we poll keys - msecs */
  32#define POLL_INTERVAL_DEFAULT   1000
  33
  34/* Magic constants in BIOS that tell about buttons */
  35enum apanel_devid {
  36        APANEL_DEV_NONE   = 0,
  37        APANEL_DEV_APPBTN = 1,
  38        APANEL_DEV_CDBTN  = 2,
  39        APANEL_DEV_LCD    = 3,
  40        APANEL_DEV_LED    = 4,
  41
  42        APANEL_DEV_MAX,
  43};
  44
  45enum apanel_chip {
  46        CHIP_NONE    = 0,
  47        CHIP_OZ992C  = 1,
  48        CHIP_OZ163T  = 2,
  49        CHIP_OZ711M3 = 4,
  50};
  51
  52/* Result of BIOS snooping/probing -- what features are supported */
  53static enum apanel_chip device_chip[APANEL_DEV_MAX];
  54
  55#define MAX_PANEL_KEYS  12
  56
  57struct apanel {
  58        struct input_polled_dev *ipdev;
  59        struct i2c_client *client;
  60        unsigned short keymap[MAX_PANEL_KEYS];
  61        u16    nkeys;
  62        u16    led_bits;
  63        struct work_struct led_work;
  64        struct led_classdev mail_led;
  65};
  66
  67
  68static int apanel_probe(struct i2c_client *, const struct i2c_device_id *);
  69
  70static void report_key(struct input_dev *input, unsigned keycode)
  71{
  72        pr_debug(APANEL ": report key %#x\n", keycode);
  73        input_report_key(input, keycode, 1);
  74        input_sync(input);
  75
  76        input_report_key(input, keycode, 0);
  77        input_sync(input);
  78}
  79
  80/* Poll for key changes
  81 *
  82 * Read Application keys via SMI
  83 *  A (0x4), B (0x8), Internet (0x2), Email (0x1).
  84 *
  85 * CD keys:
  86 * Forward (0x100), Rewind (0x200), Stop (0x400), Pause (0x800)
  87 */
  88static void apanel_poll(struct input_polled_dev *ipdev)
  89{
  90        struct apanel *ap = ipdev->private;
  91        struct input_dev *idev = ipdev->input;
  92        u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
  93        s32 data;
  94        int i;
  95
  96        data = i2c_smbus_read_word_data(ap->client, cmd);
  97        if (data < 0)
  98                return; /* ignore errors (due to ACPI??) */
  99
 100        /* write back to clear latch */
 101        i2c_smbus_write_word_data(ap->client, cmd, 0);
 102
 103        if (!data)
 104                return;
 105
 106        dev_dbg(&idev->dev, APANEL ": data %#x\n", data);
 107        for (i = 0; i < idev->keycodemax; i++)
 108                if ((1u << i) & data)
 109                        report_key(idev, ap->keymap[i]);
 110}
 111
 112/* Track state changes of LED */
 113static void led_update(struct work_struct *work)
 114{
 115        struct apanel *ap = container_of(work, struct apanel, led_work);
 116
 117        i2c_smbus_write_word_data(ap->client, 0x10, ap->led_bits);
 118}
 119
 120static void mail_led_set(struct led_classdev *led,
 121                         enum led_brightness value)
 122{
 123        struct apanel *ap = container_of(led, struct apanel, mail_led);
 124
 125        if (value != LED_OFF)
 126                ap->led_bits |= 0x8000;
 127        else
 128                ap->led_bits &= ~0x8000;
 129
 130        schedule_work(&ap->led_work);
 131}
 132
 133static int apanel_remove(struct i2c_client *client)
 134{
 135        struct apanel *ap = i2c_get_clientdata(client);
 136
 137        if (device_chip[APANEL_DEV_LED] != CHIP_NONE)
 138                led_classdev_unregister(&ap->mail_led);
 139
 140        input_unregister_polled_device(ap->ipdev);
 141        input_free_polled_device(ap->ipdev);
 142
 143        return 0;
 144}
 145
 146static void apanel_shutdown(struct i2c_client *client)
 147{
 148        apanel_remove(client);
 149}
 150
 151static const struct i2c_device_id apanel_id[] = {
 152        { "fujitsu_apanel", 0 },
 153        { }
 154};
 155MODULE_DEVICE_TABLE(i2c, apanel_id);
 156
 157static struct i2c_driver apanel_driver = {
 158        .driver = {
 159                .name = APANEL,
 160        },
 161        .probe          = &apanel_probe,
 162        .remove         = &apanel_remove,
 163        .shutdown       = &apanel_shutdown,
 164        .id_table       = apanel_id,
 165};
 166
 167static struct apanel apanel = {
 168        .keymap = {
 169                [0] = KEY_MAIL,
 170                [1] = KEY_WWW,
 171                [2] = KEY_PROG2,
 172                [3] = KEY_PROG1,
 173
 174                [8] = KEY_FORWARD,
 175                [9] = KEY_REWIND,
 176                [10] = KEY_STOPCD,
 177                [11] = KEY_PLAYPAUSE,
 178
 179        },
 180        .mail_led = {
 181                .name = "mail:blue",
 182                .brightness_set = mail_led_set,
 183        },
 184};
 185
 186/* NB: Only one panel on the i2c. */
 187static int apanel_probe(struct i2c_client *client,
 188                        const struct i2c_device_id *id)
 189{
 190        struct apanel *ap;
 191        struct input_polled_dev *ipdev;
 192        struct input_dev *idev;
 193        u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
 194        int i, err = -ENOMEM;
 195
 196        ap = &apanel;
 197
 198        ipdev = input_allocate_polled_device();
 199        if (!ipdev)
 200                goto out1;
 201
 202        ap->ipdev = ipdev;
 203        ap->client = client;
 204
 205        i2c_set_clientdata(client, ap);
 206
 207        err = i2c_smbus_write_word_data(client, cmd, 0);
 208        if (err) {
 209                dev_warn(&client->dev, APANEL ": smbus write error %d\n",
 210                         err);
 211                goto out3;
 212        }
 213
 214        ipdev->poll = apanel_poll;
 215        ipdev->poll_interval = POLL_INTERVAL_DEFAULT;
 216        ipdev->private = ap;
 217
 218        idev = ipdev->input;
 219        idev->name = APANEL_NAME " buttons";
 220        idev->phys = "apanel/input0";
 221        idev->id.bustype = BUS_HOST;
 222        idev->dev.parent = &client->dev;
 223
 224        set_bit(EV_KEY, idev->evbit);
 225
 226        idev->keycode = ap->keymap;
 227        idev->keycodesize = sizeof(ap->keymap[0]);
 228        idev->keycodemax = (device_chip[APANEL_DEV_CDBTN] != CHIP_NONE) ? 12 : 4;
 229
 230        for (i = 0; i < idev->keycodemax; i++)
 231                if (ap->keymap[i])
 232                        set_bit(ap->keymap[i], idev->keybit);
 233
 234        err = input_register_polled_device(ipdev);
 235        if (err)
 236                goto out3;
 237
 238        INIT_WORK(&ap->led_work, led_update);
 239        if (device_chip[APANEL_DEV_LED] != CHIP_NONE) {
 240                err = led_classdev_register(&client->dev, &ap->mail_led);
 241                if (err)
 242                        goto out4;
 243        }
 244
 245        return 0;
 246out4:
 247        input_unregister_polled_device(ipdev);
 248out3:
 249        input_free_polled_device(ipdev);
 250out1:
 251        return err;
 252}
 253
 254/* Scan the system ROM for the signature "FJKEYINF" */
 255static __init const void __iomem *bios_signature(const void __iomem *bios)
 256{
 257        ssize_t offset;
 258        const unsigned char signature[] = "FJKEYINF";
 259
 260        for (offset = 0; offset < 0x10000; offset += 0x10) {
 261                if (check_signature(bios + offset, signature,
 262                                    sizeof(signature)-1))
 263                        return bios + offset;
 264        }
 265        pr_notice(APANEL ": Fujitsu BIOS signature '%s' not found...\n",
 266                  signature);
 267        return NULL;
 268}
 269
 270static int __init apanel_init(void)
 271{
 272        void __iomem *bios;
 273        const void __iomem *p;
 274        u8 devno;
 275        unsigned char i2c_addr;
 276        int found = 0;
 277
 278        bios = ioremap(0xF0000, 0x10000); /* Can't fail */
 279
 280        p = bios_signature(bios);
 281        if (!p) {
 282                iounmap(bios);
 283                return -ENODEV;
 284        }
 285
 286        /* just use the first address */
 287        p += 8;
 288        i2c_addr = readb(p + 3) >> 1;
 289
 290        for ( ; (devno = readb(p)) & 0x7f; p += 4) {
 291                unsigned char method, slave, chip;
 292
 293                method = readb(p + 1);
 294                chip = readb(p + 2);
 295                slave = readb(p + 3) >> 1;
 296
 297                if (slave != i2c_addr) {
 298                        pr_notice(APANEL ": only one SMBus slave "
 299                                  "address supported, skipping device...\n");
 300                        continue;
 301                }
 302
 303                /* translate alternative device numbers */
 304                switch (devno) {
 305                case 6:
 306                        devno = APANEL_DEV_APPBTN;
 307                        break;
 308                case 7:
 309                        devno = APANEL_DEV_LED;
 310                        break;
 311                }
 312
 313                if (devno >= APANEL_DEV_MAX)
 314                        pr_notice(APANEL ": unknown device %u found\n", devno);
 315                else if (device_chip[devno] != CHIP_NONE)
 316                        pr_warn(APANEL ": duplicate entry for devno %u\n",
 317                                devno);
 318
 319                else if (method != 1 && method != 2 && method != 4) {
 320                        pr_notice(APANEL ": unknown method %u for devno %u\n",
 321                                  method, devno);
 322                } else {
 323                        device_chip[devno] = (enum apanel_chip) chip;
 324                        ++found;
 325                }
 326        }
 327        iounmap(bios);
 328
 329        if (found == 0) {
 330                pr_info(APANEL ": no input devices reported by BIOS\n");
 331                return -EIO;
 332        }
 333
 334        return i2c_add_driver(&apanel_driver);
 335}
 336module_init(apanel_init);
 337
 338static void __exit apanel_cleanup(void)
 339{
 340        i2c_del_driver(&apanel_driver);
 341}
 342module_exit(apanel_cleanup);
 343
 344MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>");
 345MODULE_DESCRIPTION(APANEL_NAME " driver");
 346MODULE_LICENSE("GPL");
 347
 348MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*");
 349MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");
 350