linux/drivers/input/misc/pcf8574_keypad.c
<<
>>
Prefs
   1/*
   2 * Driver for a keypad w/16 buttons connected to a PCF8574 I2C I/O expander
   3 *
   4 * Copyright 2005-2008 Analog Devices Inc.
   5 *
   6 * Licensed under the GPL-2 or later.
   7 */
   8
   9#include <linux/module.h>
  10#include <linux/init.h>
  11#include <linux/input.h>
  12#include <linux/interrupt.h>
  13#include <linux/i2c.h>
  14#include <linux/slab.h>
  15#include <linux/workqueue.h>
  16
  17#define DRV_NAME "pcf8574_keypad"
  18
  19static const unsigned char pcf8574_kp_btncode[] = {
  20        [0] = KEY_RESERVED,
  21        [1] = KEY_ENTER,
  22        [2] = KEY_BACKSLASH,
  23        [3] = KEY_0,
  24        [4] = KEY_RIGHTBRACE,
  25        [5] = KEY_C,
  26        [6] = KEY_9,
  27        [7] = KEY_8,
  28        [8] = KEY_7,
  29        [9] = KEY_B,
  30        [10] = KEY_6,
  31        [11] = KEY_5,
  32        [12] = KEY_4,
  33        [13] = KEY_A,
  34        [14] = KEY_3,
  35        [15] = KEY_2,
  36        [16] = KEY_1
  37};
  38
  39struct kp_data {
  40        unsigned short btncode[ARRAY_SIZE(pcf8574_kp_btncode)];
  41        struct input_dev *idev;
  42        struct i2c_client *client;
  43        char name[64];
  44        char phys[32];
  45        unsigned char laststate;
  46};
  47
  48static short read_state(struct kp_data *lp)
  49{
  50        unsigned char x, y, a, b;
  51
  52        i2c_smbus_write_byte(lp->client, 240);
  53        x = 0xF & (~(i2c_smbus_read_byte(lp->client) >> 4));
  54
  55        i2c_smbus_write_byte(lp->client, 15);
  56        y = 0xF & (~i2c_smbus_read_byte(lp->client));
  57
  58        for (a = 0; x > 0; a++)
  59                x = x >> 1;
  60        for (b = 0; y > 0; b++)
  61                y = y >> 1;
  62
  63        return ((a - 1) * 4) + b;
  64}
  65
  66static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id)
  67{
  68        struct kp_data *lp = dev_id;
  69        unsigned char nextstate = read_state(lp);
  70
  71        if (lp->laststate != nextstate) {
  72                int key_down = nextstate < ARRAY_SIZE(lp->btncode);
  73                unsigned short keycode = key_down ?
  74                        lp->btncode[nextstate] : lp->btncode[lp->laststate];
  75
  76                input_report_key(lp->idev, keycode, key_down);
  77                input_sync(lp->idev);
  78
  79                lp->laststate = nextstate;
  80        }
  81
  82        return IRQ_HANDLED;
  83}
  84
  85static int __devinit pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_id *id)
  86{
  87        int i, ret;
  88        struct input_dev *idev;
  89        struct kp_data *lp;
  90
  91        if (i2c_smbus_write_byte(client, 240) < 0) {
  92                dev_err(&client->dev, "probe: write fail\n");
  93                return -ENODEV;
  94        }
  95
  96        lp = kzalloc(sizeof(*lp), GFP_KERNEL);
  97        if (!lp)
  98                return -ENOMEM;
  99
 100        idev = input_allocate_device();
 101        if (!idev) {
 102                dev_err(&client->dev, "Can't allocate input device\n");
 103                ret = -ENOMEM;
 104                goto fail_allocate;
 105        }
 106
 107        lp->idev = idev;
 108        lp->client = client;
 109
 110        idev->evbit[0] = BIT_MASK(EV_KEY);
 111        idev->keycode = lp->btncode;
 112        idev->keycodesize = sizeof(lp->btncode[0]);
 113        idev->keycodemax = ARRAY_SIZE(lp->btncode);
 114
 115        for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) {
 116                lp->btncode[i] = pcf8574_kp_btncode[i];
 117                __set_bit(lp->btncode[i] & KEY_MAX, idev->keybit);
 118        }
 119
 120        sprintf(lp->name, DRV_NAME);
 121        sprintf(lp->phys, "kp_data/input0");
 122
 123        idev->name = lp->name;
 124        idev->phys = lp->phys;
 125        idev->id.bustype = BUS_I2C;
 126        idev->id.vendor = 0x0001;
 127        idev->id.product = 0x0001;
 128        idev->id.version = 0x0100;
 129
 130        lp->laststate = read_state(lp);
 131
 132        ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler,
 133                                   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
 134                                   DRV_NAME, lp);
 135        if (ret) {
 136                dev_err(&client->dev, "IRQ %d is not free\n", client->irq);
 137                goto fail_free_device;
 138        }
 139
 140        ret = input_register_device(idev);
 141        if (ret) {
 142                dev_err(&client->dev, "input_register_device() failed\n");
 143                goto fail_free_irq;
 144        }
 145
 146        i2c_set_clientdata(client, lp);
 147        return 0;
 148
 149 fail_free_irq:
 150        free_irq(client->irq, lp);
 151 fail_free_device:
 152        input_free_device(idev);
 153 fail_allocate:
 154        kfree(lp);
 155
 156        return ret;
 157}
 158
 159static int __devexit pcf8574_kp_remove(struct i2c_client *client)
 160{
 161        struct kp_data *lp = i2c_get_clientdata(client);
 162
 163        free_irq(client->irq, lp);
 164
 165        input_unregister_device(lp->idev);
 166        kfree(lp);
 167
 168        return 0;
 169}
 170
 171#ifdef CONFIG_PM
 172static int pcf8574_kp_resume(struct device *dev)
 173{
 174        struct i2c_client *client = to_i2c_client(dev);
 175
 176        enable_irq(client->irq);
 177
 178        return 0;
 179}
 180
 181static int pcf8574_kp_suspend(struct device *dev)
 182{
 183        struct i2c_client *client = to_i2c_client(dev);
 184
 185        disable_irq(client->irq);
 186
 187        return 0;
 188}
 189
 190static const struct dev_pm_ops pcf8574_kp_pm_ops = {
 191        .suspend        = pcf8574_kp_suspend,
 192        .resume         = pcf8574_kp_resume,
 193};
 194
 195#else
 196# define pcf8574_kp_resume  NULL
 197# define pcf8574_kp_suspend NULL
 198#endif
 199
 200static const struct i2c_device_id pcf8574_kp_id[] = {
 201        { DRV_NAME, 0 },
 202        { }
 203};
 204MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
 205
 206static struct i2c_driver pcf8574_kp_driver = {
 207        .driver = {
 208                .name  = DRV_NAME,
 209                .owner = THIS_MODULE,
 210#ifdef CONFIG_PM
 211                .pm = &pcf8574_kp_pm_ops,
 212#endif
 213        },
 214        .probe    = pcf8574_kp_probe,
 215        .remove   = __devexit_p(pcf8574_kp_remove),
 216        .id_table = pcf8574_kp_id,
 217};
 218
 219module_i2c_driver(pcf8574_kp_driver);
 220
 221MODULE_AUTHOR("Michael Hennerich");
 222MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");
 223MODULE_LICENSE("GPL");
 224