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