Path: blob/master/drivers/input/misc/pcf8574_keypad.c
15109 views
/*1* Driver for a keypad w/16 buttons connected to a PCF8574 I2C I/O expander2*3* Copyright 2005-2008 Analog Devices Inc.4*5* Licensed under the GPL-2 or later.6*/78#include <linux/module.h>9#include <linux/init.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>1516#define DRV_NAME "pcf8574_keypad"1718static 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_136};3738struct kp_data {39unsigned short btncode[ARRAY_SIZE(pcf8574_kp_btncode)];40struct input_dev *idev;41struct i2c_client *client;42char name[64];43char phys[32];44unsigned char laststate;45};4647static short read_state(struct kp_data *lp)48{49unsigned char x, y, a, b;5051i2c_smbus_write_byte(lp->client, 240);52x = 0xF & (~(i2c_smbus_read_byte(lp->client) >> 4));5354i2c_smbus_write_byte(lp->client, 15);55y = 0xF & (~i2c_smbus_read_byte(lp->client));5657for (a = 0; x > 0; a++)58x = x >> 1;59for (b = 0; y > 0; b++)60y = y >> 1;6162return ((a - 1) * 4) + b;63}6465static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id)66{67struct kp_data *lp = dev_id;68unsigned char nextstate = read_state(lp);6970if (lp->laststate != nextstate) {71int key_down = nextstate < ARRAY_SIZE(lp->btncode);72unsigned short keycode = key_down ?73lp->btncode[nextstate] : lp->btncode[lp->laststate];7475input_report_key(lp->idev, keycode, key_down);76input_sync(lp->idev);7778lp->laststate = nextstate;79}8081return IRQ_HANDLED;82}8384static int __devinit pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_id *id)85{86int i, ret;87struct input_dev *idev;88struct kp_data *lp;8990if (i2c_smbus_write_byte(client, 240) < 0) {91dev_err(&client->dev, "probe: write fail\n");92return -ENODEV;93}9495lp = kzalloc(sizeof(*lp), GFP_KERNEL);96if (!lp)97return -ENOMEM;9899idev = input_allocate_device();100if (!idev) {101dev_err(&client->dev, "Can't allocate input device\n");102ret = -ENOMEM;103goto fail_allocate;104}105106lp->idev = idev;107lp->client = client;108109idev->evbit[0] = BIT_MASK(EV_KEY);110idev->keycode = lp->btncode;111idev->keycodesize = sizeof(lp->btncode[0]);112idev->keycodemax = ARRAY_SIZE(lp->btncode);113114for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) {115lp->btncode[i] = pcf8574_kp_btncode[i];116__set_bit(lp->btncode[i] & KEY_MAX, idev->keybit);117}118119sprintf(lp->name, DRV_NAME);120sprintf(lp->phys, "kp_data/input0");121122idev->name = lp->name;123idev->phys = lp->phys;124idev->id.bustype = BUS_I2C;125idev->id.vendor = 0x0001;126idev->id.product = 0x0001;127idev->id.version = 0x0100;128129lp->laststate = read_state(lp);130131ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler,132IRQF_TRIGGER_LOW | IRQF_ONESHOT,133DRV_NAME, lp);134if (ret) {135dev_err(&client->dev, "IRQ %d is not free\n", client->irq);136goto fail_free_device;137}138139ret = input_register_device(idev);140if (ret) {141dev_err(&client->dev, "input_register_device() failed\n");142goto fail_free_irq;143}144145i2c_set_clientdata(client, lp);146return 0;147148fail_free_irq:149free_irq(client->irq, lp);150fail_free_device:151input_free_device(idev);152fail_allocate:153kfree(lp);154155return ret;156}157158static int __devexit pcf8574_kp_remove(struct i2c_client *client)159{160struct kp_data *lp = i2c_get_clientdata(client);161162free_irq(client->irq, lp);163164input_unregister_device(lp->idev);165kfree(lp);166167return 0;168}169170#ifdef CONFIG_PM171static int pcf8574_kp_resume(struct device *dev)172{173struct i2c_client *client = to_i2c_client(dev);174175enable_irq(client->irq);176177return 0;178}179180static int pcf8574_kp_suspend(struct device *dev)181{182struct i2c_client *client = to_i2c_client(dev);183184disable_irq(client->irq);185186return 0;187}188189static const struct dev_pm_ops pcf8574_kp_pm_ops = {190.suspend = pcf8574_kp_suspend,191.resume = pcf8574_kp_resume,192};193194#else195# define pcf8574_kp_resume NULL196# define pcf8574_kp_suspend NULL197#endif198199static const struct i2c_device_id pcf8574_kp_id[] = {200{ DRV_NAME, 0 },201{ }202};203MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);204205static struct i2c_driver pcf8574_kp_driver = {206.driver = {207.name = DRV_NAME,208.owner = THIS_MODULE,209#ifdef CONFIG_PM210.pm = &pcf8574_kp_pm_ops,211#endif212},213.probe = pcf8574_kp_probe,214.remove = __devexit_p(pcf8574_kp_remove),215.id_table = pcf8574_kp_id,216};217218static int __init pcf8574_kp_init(void)219{220return i2c_add_driver(&pcf8574_kp_driver);221}222module_init(pcf8574_kp_init);223224static void __exit pcf8574_kp_exit(void)225{226i2c_del_driver(&pcf8574_kp_driver);227}228module_exit(pcf8574_kp_exit);229230MODULE_AUTHOR("Michael Hennerich");231MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");232MODULE_LICENSE("GPL");233234235