Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/input/misc/apanel.c
15109 views
1
/*
2
* Fujitsu Lifebook Application Panel button drive
3
*
4
* Copyright (C) 2007 Stephen Hemminger <[email protected]>
5
* Copyright (C) 2001-2003 Jochen Eisinger <[email protected]>
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 */
36
enum 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
46
enum 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 */
54
static enum apanel_chip device_chip[APANEL_DEV_MAX];
55
56
#define MAX_PANEL_KEYS 12
57
58
struct 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
69
static int apanel_probe(struct i2c_client *, const struct i2c_device_id *);
70
71
static 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
*/
89
static 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 */
114
static 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
121
static 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
134
static 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
147
static void apanel_shutdown(struct i2c_client *client)
148
{
149
apanel_remove(client);
150
}
151
152
static const struct i2c_device_id apanel_id[] = {
153
{ "fujitsu_apanel", 0 },
154
{ }
155
};
156
MODULE_DEVICE_TABLE(i2c, apanel_id);
157
158
static 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
168
static 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. */
188
static 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;
247
out4:
248
input_unregister_polled_device(ipdev);
249
out3:
250
input_free_polled_device(ipdev);
251
out1:
252
return err;
253
}
254
255
/* Scan the system ROM for the signature "FJKEYINF" */
256
static __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
271
static 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
}
336
module_init(apanel_init);
337
338
static void __exit apanel_cleanup(void)
339
{
340
i2c_del_driver(&apanel_driver);
341
}
342
module_exit(apanel_cleanup);
343
344
MODULE_AUTHOR("Stephen Hemminger <[email protected]>");
345
MODULE_DESCRIPTION(APANEL_NAME " driver");
346
MODULE_LICENSE("GPL");
347
MODULE_VERSION(APANEL_VERSION);
348
349
MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*");
350
MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");
351
352