Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/input/misc/pcf8574_keypad.c
15109 views
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
19
static 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
39
struct 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
48
static 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
66
static 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
85
static 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
159
static 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
172
static 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
181
static 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
190
static 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
200
static const struct i2c_device_id pcf8574_kp_id[] = {
201
{ DRV_NAME, 0 },
202
{ }
203
};
204
MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
205
206
static 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
219
static int __init pcf8574_kp_init(void)
220
{
221
return i2c_add_driver(&pcf8574_kp_driver);
222
}
223
module_init(pcf8574_kp_init);
224
225
static void __exit pcf8574_kp_exit(void)
226
{
227
i2c_del_driver(&pcf8574_kp_driver);
228
}
229
module_exit(pcf8574_kp_exit);
230
231
MODULE_AUTHOR("Michael Hennerich");
232
MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");
233
MODULE_LICENSE("GPL");
234
235