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