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