Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.26-rc5 377 lines 8.9 kB view raw
1/* 2 * Fujitsu Lifebook Application Panel button drive 3 * 4 * Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org> 5 * Copyright (C) 2001-2003 Jochen Eisinger <jochen@penguin-breeder.org> 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 */ 36enum 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 46enum 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 */ 54static enum apanel_chip device_chip[APANEL_DEV_MAX]; 55 56#define MAX_PANEL_KEYS 12 57 58struct 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 69static int apanel_probe(struct i2c_adapter *, int, int); 70 71/* for now, we only support one address */ 72static unsigned short normal_i2c[] = {0, I2C_CLIENT_END}; 73static unsigned short ignore = I2C_CLIENT_END; 74static struct i2c_client_address_data addr_data = { 75 .normal_i2c = normal_i2c, 76 .probe = &ignore, 77 .ignore = &ignore, 78}; 79 80static void report_key(struct input_dev *input, unsigned keycode) 81{ 82 pr_debug(APANEL ": report key %#x\n", keycode); 83 input_report_key(input, keycode, 1); 84 input_sync(input); 85 86 input_report_key(input, keycode, 0); 87 input_sync(input); 88} 89 90/* Poll for key changes 91 * 92 * Read Application keys via SMI 93 * A (0x4), B (0x8), Internet (0x2), Email (0x1). 94 * 95 * CD keys: 96 * Forward (0x100), Rewind (0x200), Stop (0x400), Pause (0x800) 97 */ 98static void apanel_poll(struct input_polled_dev *ipdev) 99{ 100 struct apanel *ap = ipdev->private; 101 struct input_dev *idev = ipdev->input; 102 u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8; 103 s32 data; 104 int i; 105 106 data = i2c_smbus_read_word_data(&ap->client, cmd); 107 if (data < 0) 108 return; /* ignore errors (due to ACPI??) */ 109 110 /* write back to clear latch */ 111 i2c_smbus_write_word_data(&ap->client, cmd, 0); 112 113 if (!data) 114 return; 115 116 dev_dbg(&idev->dev, APANEL ": data %#x\n", data); 117 for (i = 0; i < idev->keycodemax; i++) 118 if ((1u << i) & data) 119 report_key(idev, ap->keymap[i]); 120} 121 122/* Track state changes of LED */ 123static void led_update(struct work_struct *work) 124{ 125 struct apanel *ap = container_of(work, struct apanel, led_work); 126 127 i2c_smbus_write_word_data(&ap->client, 0x10, ap->led_bits); 128} 129 130static void mail_led_set(struct led_classdev *led, 131 enum led_brightness value) 132{ 133 struct apanel *ap = container_of(led, struct apanel, mail_led); 134 135 if (value != LED_OFF) 136 ap->led_bits |= 0x8000; 137 else 138 ap->led_bits &= ~0x8000; 139 140 schedule_work(&ap->led_work); 141} 142 143static int apanel_detach_client(struct i2c_client *client) 144{ 145 struct apanel *ap = i2c_get_clientdata(client); 146 147 if (device_chip[APANEL_DEV_LED] != CHIP_NONE) 148 led_classdev_unregister(&ap->mail_led); 149 150 input_unregister_polled_device(ap->ipdev); 151 i2c_detach_client(&ap->client); 152 input_free_polled_device(ap->ipdev); 153 154 return 0; 155} 156 157/* Function is invoked for every i2c adapter. */ 158static int apanel_attach_adapter(struct i2c_adapter *adap) 159{ 160 dev_dbg(&adap->dev, APANEL ": attach adapter id=%d\n", adap->id); 161 162 /* Our device is connected only to i801 on laptop */ 163 if (adap->id != I2C_HW_SMBUS_I801) 164 return -ENODEV; 165 166 return i2c_probe(adap, &addr_data, apanel_probe); 167} 168 169static void apanel_shutdown(struct i2c_client *client) 170{ 171 apanel_detach_client(client); 172} 173 174static struct i2c_driver apanel_driver = { 175 .driver = { 176 .name = APANEL, 177 }, 178 .attach_adapter = &apanel_attach_adapter, 179 .detach_client = &apanel_detach_client, 180 .shutdown = &apanel_shutdown, 181}; 182 183static struct apanel apanel = { 184 .client = { 185 .driver = &apanel_driver, 186 .name = APANEL, 187 }, 188 .keymap = { 189 [0] = KEY_MAIL, 190 [1] = KEY_WWW, 191 [2] = KEY_PROG2, 192 [3] = KEY_PROG1, 193 194 [8] = KEY_FORWARD, 195 [9] = KEY_REWIND, 196 [10] = KEY_STOPCD, 197 [11] = KEY_PLAYPAUSE, 198 199 }, 200 .mail_led = { 201 .name = "mail:blue", 202 .brightness_set = mail_led_set, 203 }, 204}; 205 206/* NB: Only one panel on the i2c. */ 207static int apanel_probe(struct i2c_adapter *bus, int address, int kind) 208{ 209 struct apanel *ap; 210 struct input_polled_dev *ipdev; 211 struct input_dev *idev; 212 u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8; 213 int i, err = -ENOMEM; 214 215 dev_dbg(&bus->dev, APANEL ": probe adapter %p addr %d kind %d\n", 216 bus, address, kind); 217 218 ap = &apanel; 219 220 ipdev = input_allocate_polled_device(); 221 if (!ipdev) 222 goto out1; 223 224 ap->ipdev = ipdev; 225 ap->client.adapter = bus; 226 ap->client.addr = address; 227 228 i2c_set_clientdata(&ap->client, ap); 229 230 err = i2c_attach_client(&ap->client); 231 if (err) 232 goto out2; 233 234 err = i2c_smbus_write_word_data(&ap->client, cmd, 0); 235 if (err) { 236 dev_warn(&ap->client.dev, APANEL ": smbus write error %d\n", 237 err); 238 goto out3; 239 } 240 241 ipdev->poll = apanel_poll; 242 ipdev->poll_interval = POLL_INTERVAL_DEFAULT; 243 ipdev->private = ap; 244 245 idev = ipdev->input; 246 idev->name = APANEL_NAME " buttons"; 247 idev->phys = "apanel/input0"; 248 idev->id.bustype = BUS_HOST; 249 idev->dev.parent = &ap->client.dev; 250 251 set_bit(EV_KEY, idev->evbit); 252 253 idev->keycode = ap->keymap; 254 idev->keycodesize = sizeof(ap->keymap[0]); 255 idev->keycodemax = (device_chip[APANEL_DEV_CDBTN] != CHIP_NONE) ? 12 : 4; 256 257 for (i = 0; i < idev->keycodemax; i++) 258 if (ap->keymap[i]) 259 set_bit(ap->keymap[i], idev->keybit); 260 261 err = input_register_polled_device(ipdev); 262 if (err) 263 goto out3; 264 265 INIT_WORK(&ap->led_work, led_update); 266 if (device_chip[APANEL_DEV_LED] != CHIP_NONE) { 267 err = led_classdev_register(&ap->client.dev, &ap->mail_led); 268 if (err) 269 goto out4; 270 } 271 272 return 0; 273out4: 274 input_unregister_polled_device(ipdev); 275out3: 276 i2c_detach_client(&ap->client); 277out2: 278 input_free_polled_device(ipdev); 279out1: 280 return err; 281} 282 283/* Scan the system ROM for the signature "FJKEYINF" */ 284static __init const void __iomem *bios_signature(const void __iomem *bios) 285{ 286 ssize_t offset; 287 const unsigned char signature[] = "FJKEYINF"; 288 289 for (offset = 0; offset < 0x10000; offset += 0x10) { 290 if (check_signature(bios + offset, signature, 291 sizeof(signature)-1)) 292 return bios + offset; 293 } 294 pr_notice(APANEL ": Fujitsu BIOS signature '%s' not found...\n", 295 signature); 296 return NULL; 297} 298 299static int __init apanel_init(void) 300{ 301 void __iomem *bios; 302 const void __iomem *p; 303 u8 devno; 304 int found = 0; 305 306 bios = ioremap(0xF0000, 0x10000); /* Can't fail */ 307 308 p = bios_signature(bios); 309 if (!p) { 310 iounmap(bios); 311 return -ENODEV; 312 } 313 314 /* just use the first address */ 315 p += 8; 316 normal_i2c[0] = readb(p+3) >> 1; 317 318 for ( ; (devno = readb(p)) & 0x7f; p += 4) { 319 unsigned char method, slave, chip; 320 321 method = readb(p + 1); 322 chip = readb(p + 2); 323 slave = readb(p + 3) >> 1; 324 325 if (slave != normal_i2c[0]) { 326 pr_notice(APANEL ": only one SMBus slave " 327 "address supported, skiping device...\n"); 328 continue; 329 } 330 331 /* translate alternative device numbers */ 332 switch (devno) { 333 case 6: 334 devno = APANEL_DEV_APPBTN; 335 break; 336 case 7: 337 devno = APANEL_DEV_LED; 338 break; 339 } 340 341 if (devno >= APANEL_DEV_MAX) 342 pr_notice(APANEL ": unknown device %u found\n", devno); 343 else if (device_chip[devno] != CHIP_NONE) 344 pr_warning(APANEL ": duplicate entry for devno %u\n", devno); 345 346 else if (method != 1 && method != 2 && method != 4) { 347 pr_notice(APANEL ": unknown method %u for devno %u\n", 348 method, devno); 349 } else { 350 device_chip[devno] = (enum apanel_chip) chip; 351 ++found; 352 } 353 } 354 iounmap(bios); 355 356 if (found == 0) { 357 pr_info(APANEL ": no input devices reported by BIOS\n"); 358 return -EIO; 359 } 360 361 return i2c_add_driver(&apanel_driver); 362} 363module_init(apanel_init); 364 365static void __exit apanel_cleanup(void) 366{ 367 i2c_del_driver(&apanel_driver); 368} 369module_exit(apanel_cleanup); 370 371MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>"); 372MODULE_DESCRIPTION(APANEL_NAME " driver"); 373MODULE_LICENSE("GPL"); 374MODULE_VERSION(APANEL_VERSION); 375 376MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*"); 377MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");