1/* 2 * Copyright (C) 2006-2012 Robert Gerlach <khnz@gmx.de> 3 * Copyright (C) 2005-2006 Jan Rychter <jan@rychter.com> 4 * 5 * You can redistribute and/or modify this program under the terms of the 6 * GNU General Public License version 2 as published by the Free Software 7 * Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 12 * Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License along 15 * with this program; if not, write to the Free Software Foundation, Inc., 16 * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA. 17 */ 18 19#include <linux/kernel.h> 20#include <linux/module.h> 21#include <linux/init.h> 22#include <linux/bitops.h> 23#include <linux/io.h> 24#include <linux/ioport.h> 25#include <linux/acpi.h> 26#include <linux/device.h> 27#include <linux/interrupt.h> 28#include <linux/input.h> 29#include <linux/delay.h> 30#include <linux/dmi.h> 31 32#define MODULENAME "fujitsu-tablet" 33 34#define ACPI_FUJITSU_CLASS "fujitsu" 35 36#define INVERT_TABLET_MODE_BIT 0x01 37#define FORCE_TABLET_MODE_IF_UNDOCK 0x02 38 39#define KEYMAP_LEN 16 40 41static const struct acpi_device_id fujitsu_ids[] = { 42 { .id = "FUJ02BD" }, 43 { .id = "FUJ02BF" }, 44 { .id = "" } 45}; 46 47struct fujitsu_config { 48 unsigned short keymap[KEYMAP_LEN]; 49 unsigned int quirks; 50}; 51 52static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initconst = { 53 KEY_RESERVED, 54 KEY_RESERVED, 55 KEY_RESERVED, 56 KEY_RESERVED, 57 KEY_SCROLLDOWN, 58 KEY_SCROLLUP, 59 KEY_DIRECTION, 60 KEY_LEFTCTRL, 61 KEY_BRIGHTNESSUP, 62 KEY_BRIGHTNESSDOWN, 63 KEY_BRIGHTNESS_ZERO, 64 KEY_RESERVED, 65 KEY_RESERVED, 66 KEY_RESERVED, 67 KEY_RESERVED, 68 KEY_LEFTALT 69}; 70 71static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initconst = { 72 KEY_RESERVED, 73 KEY_RESERVED, 74 KEY_RESERVED, 75 KEY_RESERVED, 76 KEY_PROG1, 77 KEY_PROG2, 78 KEY_DIRECTION, 79 KEY_RESERVED, 80 KEY_RESERVED, 81 KEY_RESERVED, 82 KEY_UP, 83 KEY_DOWN, 84 KEY_RESERVED, 85 KEY_RESERVED, 86 KEY_LEFTCTRL, 87 KEY_LEFTALT 88}; 89 90static unsigned short keymap_Stylistic_Tseries[KEYMAP_LEN] __initconst = { 91 KEY_RESERVED, 92 KEY_RESERVED, 93 KEY_RESERVED, 94 KEY_RESERVED, 95 KEY_PRINT, 96 KEY_BACKSPACE, 97 KEY_SPACE, 98 KEY_ENTER, 99 KEY_BRIGHTNESSUP, 100 KEY_BRIGHTNESSDOWN, 101 KEY_DOWN, 102 KEY_UP, 103 KEY_SCROLLUP, 104 KEY_SCROLLDOWN, 105 KEY_LEFTCTRL, 106 KEY_LEFTALT 107}; 108 109static unsigned short keymap_Stylistic_ST5xxx[KEYMAP_LEN] __initconst = { 110 KEY_RESERVED, 111 KEY_RESERVED, 112 KEY_RESERVED, 113 KEY_RESERVED, 114 KEY_MAIL, 115 KEY_DIRECTION, 116 KEY_ESC, 117 KEY_ENTER, 118 KEY_BRIGHTNESSUP, 119 KEY_BRIGHTNESSDOWN, 120 KEY_DOWN, 121 KEY_UP, 122 KEY_SCROLLUP, 123 KEY_SCROLLDOWN, 124 KEY_LEFTCTRL, 125 KEY_LEFTALT 126}; 127 128static struct { 129 struct input_dev *idev; 130 struct fujitsu_config config; 131 unsigned long prev_keymask; 132 133 char phys[21]; 134 135 int irq; 136 int io_base; 137 int io_length; 138} fujitsu; 139 140static u8 fujitsu_ack(void) 141{ 142 return inb(fujitsu.io_base + 2); 143} 144 145static u8 fujitsu_status(void) 146{ 147 return inb(fujitsu.io_base + 6); 148} 149 150static u8 fujitsu_read_register(const u8 addr) 151{ 152 outb(addr, fujitsu.io_base); 153 return inb(fujitsu.io_base + 4); 154} 155 156static void fujitsu_send_state(void) 157{ 158 int state; 159 int dock, tablet_mode; 160 161 state = fujitsu_read_register(0xdd); 162 163 dock = state & 0x02; 164 165 if ((fujitsu.config.quirks & FORCE_TABLET_MODE_IF_UNDOCK) && (!dock)) { 166 tablet_mode = 1; 167 } else{ 168 tablet_mode = state & 0x01; 169 if (fujitsu.config.quirks & INVERT_TABLET_MODE_BIT) 170 tablet_mode = !tablet_mode; 171 } 172 173 input_report_switch(fujitsu.idev, SW_DOCK, dock); 174 input_report_switch(fujitsu.idev, SW_TABLET_MODE, tablet_mode); 175 input_sync(fujitsu.idev); 176} 177 178static void fujitsu_reset(void) 179{ 180 int timeout = 50; 181 182 fujitsu_ack(); 183 184 while ((fujitsu_status() & 0x02) && (--timeout)) 185 msleep(20); 186 187 fujitsu_send_state(); 188} 189 190static int __devinit input_fujitsu_setup(struct device *parent, 191 const char *name, const char *phys) 192{ 193 struct input_dev *idev; 194 int error; 195 int i; 196 197 idev = input_allocate_device(); 198 if (!idev) 199 return -ENOMEM; 200 201 idev->dev.parent = parent; 202 idev->phys = phys; 203 idev->name = name; 204 idev->id.bustype = BUS_HOST; 205 idev->id.vendor = 0x1734; /* Fujitsu Siemens Computer GmbH */ 206 idev->id.product = 0x0001; 207 idev->id.version = 0x0101; 208 209 idev->keycode = fujitsu.config.keymap; 210 idev->keycodesize = sizeof(fujitsu.config.keymap[0]); 211 idev->keycodemax = ARRAY_SIZE(fujitsu.config.keymap); 212 213 __set_bit(EV_REP, idev->evbit); 214 215 for (i = 0; i < ARRAY_SIZE(fujitsu.config.keymap); i++) 216 if (fujitsu.config.keymap[i]) 217 input_set_capability(idev, EV_KEY, fujitsu.config.keymap[i]); 218 219 input_set_capability(idev, EV_MSC, MSC_SCAN); 220 221 input_set_capability(idev, EV_SW, SW_DOCK); 222 input_set_capability(idev, EV_SW, SW_TABLET_MODE); 223 224 input_set_capability(idev, EV_SW, SW_DOCK); 225 input_set_capability(idev, EV_SW, SW_TABLET_MODE); 226 227 error = input_register_device(idev); 228 if (error) { 229 input_free_device(idev); 230 return error; 231 } 232 233 fujitsu.idev = idev; 234 return 0; 235} 236 237static void input_fujitsu_remove(void) 238{ 239 input_unregister_device(fujitsu.idev); 240} 241 242static irqreturn_t fujitsu_interrupt(int irq, void *dev_id) 243{ 244 unsigned long keymask, changed; 245 unsigned int keycode; 246 int pressed; 247 int i; 248 249 if (unlikely(!(fujitsu_status() & 0x01))) 250 return IRQ_NONE; 251 252 fujitsu_send_state(); 253 254 keymask = fujitsu_read_register(0xde); 255 keymask |= fujitsu_read_register(0xdf) << 8; 256 keymask ^= 0xffff; 257 258 changed = keymask ^ fujitsu.prev_keymask; 259 if (changed) { 260 fujitsu.prev_keymask = keymask; 261 262 for_each_set_bit(i, &changed, KEYMAP_LEN) { 263 keycode = fujitsu.config.keymap[i]; 264 pressed = keymask & changed & BIT(i); 265 266 if (pressed) 267 input_event(fujitsu.idev, EV_MSC, MSC_SCAN, i); 268 269 input_report_key(fujitsu.idev, keycode, pressed); 270 input_sync(fujitsu.idev); 271 } 272 } 273 274 fujitsu_ack(); 275 return IRQ_HANDLED; 276} 277 278static int __devinit fujitsu_dmi_default(const struct dmi_system_id *dmi) 279{ 280 printk(KERN_INFO MODULENAME ": %s\n", dmi->ident); 281 memcpy(fujitsu.config.keymap, dmi->driver_data, 282 sizeof(fujitsu.config.keymap)); 283 return 1; 284} 285 286static int __devinit fujitsu_dmi_stylistic(const struct dmi_system_id *dmi) 287{ 288 fujitsu_dmi_default(dmi); 289 fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK; 290 fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT; 291 return 1; 292} 293 294static struct dmi_system_id dmi_ids[] __initconst = { 295 { 296 .callback = fujitsu_dmi_default, 297 .ident = "Fujitsu Siemens P/T Series", 298 .matches = { 299 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 300 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK") 301 }, 302 .driver_data = keymap_Lifebook_Tseries 303 }, 304 { 305 .callback = fujitsu_dmi_default, 306 .ident = "Fujitsu Lifebook T Series", 307 .matches = { 308 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 309 DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T") 310 }, 311 .driver_data = keymap_Lifebook_Tseries 312 }, 313 { 314 .callback = fujitsu_dmi_stylistic, 315 .ident = "Fujitsu Siemens Stylistic T Series", 316 .matches = { 317 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 318 DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic T") 319 }, 320 .driver_data = keymap_Stylistic_Tseries 321 }, 322 { 323 .callback = fujitsu_dmi_default, 324 .ident = "Fujitsu LifeBook U810", 325 .matches = { 326 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 327 DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook U810") 328 }, 329 .driver_data = keymap_Lifebook_U810 330 }, 331 { 332 .callback = fujitsu_dmi_stylistic, 333 .ident = "Fujitsu Siemens Stylistic ST5xxx Series", 334 .matches = { 335 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 336 DMI_MATCH(DMI_PRODUCT_NAME, "STYLISTIC ST5") 337 }, 338 .driver_data = keymap_Stylistic_ST5xxx 339 }, 340 { 341 .callback = fujitsu_dmi_stylistic, 342 .ident = "Fujitsu Siemens Stylistic ST5xxx Series", 343 .matches = { 344 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 345 DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic ST5") 346 }, 347 .driver_data = keymap_Stylistic_ST5xxx 348 }, 349 { 350 .callback = fujitsu_dmi_default, 351 .ident = "Unknown (using defaults)", 352 .matches = { 353 DMI_MATCH(DMI_SYS_VENDOR, ""), 354 DMI_MATCH(DMI_PRODUCT_NAME, "") 355 }, 356 .driver_data = keymap_Lifebook_Tseries 357 }, 358 { NULL } 359}; 360 361static acpi_status __devinit 362fujitsu_walk_resources(struct acpi_resource *res, void *data) 363{ 364 switch (res->type) { 365 case ACPI_RESOURCE_TYPE_IRQ: 366 fujitsu.irq = res->data.irq.interrupts[0]; 367 return AE_OK; 368 369 case ACPI_RESOURCE_TYPE_IO: 370 fujitsu.io_base = res->data.io.minimum; 371 fujitsu.io_length = res->data.io.address_length; 372 return AE_OK; 373 374 case ACPI_RESOURCE_TYPE_END_TAG: 375 if (fujitsu.irq && fujitsu.io_base) 376 return AE_OK; 377 else 378 return AE_NOT_FOUND; 379 380 default: 381 return AE_ERROR; 382 } 383} 384 385static int __devinit acpi_fujitsu_add(struct acpi_device *adev) 386{ 387 acpi_status status; 388 int error; 389 390 if (!adev) 391 return -EINVAL; 392 393 status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS, 394 fujitsu_walk_resources, NULL); 395 if (ACPI_FAILURE(status) || !fujitsu.irq || !fujitsu.io_base) 396 return -ENODEV; 397 398 sprintf(acpi_device_name(adev), "Fujitsu %s", acpi_device_hid(adev)); 399 sprintf(acpi_device_class(adev), "%s", ACPI_FUJITSU_CLASS); 400 401 snprintf(fujitsu.phys, sizeof(fujitsu.phys), 402 "%s/input0", acpi_device_hid(adev)); 403 404 error = input_fujitsu_setup(&adev->dev, 405 acpi_device_name(adev), fujitsu.phys); 406 if (error) 407 return error; 408 409 if (!request_region(fujitsu.io_base, fujitsu.io_length, MODULENAME)) { 410 input_fujitsu_remove(); 411 return -EBUSY; 412 } 413 414 fujitsu_reset(); 415 416 error = request_irq(fujitsu.irq, fujitsu_interrupt, 417 IRQF_SHARED, MODULENAME, fujitsu_interrupt); 418 if (error) { 419 release_region(fujitsu.io_base, fujitsu.io_length); 420 input_fujitsu_remove(); 421 return error; 422 } 423 424 return 0; 425} 426 427static int __devexit acpi_fujitsu_remove(struct acpi_device *adev, int type) 428{ 429 free_irq(fujitsu.irq, fujitsu_interrupt); 430 release_region(fujitsu.io_base, fujitsu.io_length); 431 input_fujitsu_remove(); 432 return 0; 433} 434 435static int acpi_fujitsu_resume(struct acpi_device *adev) 436{ 437 fujitsu_reset(); 438 return 0; 439} 440 441static struct acpi_driver acpi_fujitsu_driver = { 442 .name = MODULENAME, 443 .class = "hotkey", 444 .ids = fujitsu_ids, 445 .ops = { 446 .add = acpi_fujitsu_add, 447 .remove = acpi_fujitsu_remove, 448 .resume = acpi_fujitsu_resume, 449 } 450}; 451 452static int __init fujitsu_module_init(void) 453{ 454 int error; 455 456 dmi_check_system(dmi_ids); 457 458 error = acpi_bus_register_driver(&acpi_fujitsu_driver); 459 if (error) 460 return error; 461 462 return 0; 463} 464 465static void __exit fujitsu_module_exit(void) 466{ 467 acpi_bus_unregister_driver(&acpi_fujitsu_driver); 468} 469 470module_init(fujitsu_module_init); 471module_exit(fujitsu_module_exit); 472 473MODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>"); 474MODULE_DESCRIPTION("Fujitsu tablet pc extras driver"); 475MODULE_LICENSE("GPL"); 476MODULE_VERSION("2.4"); 477 478MODULE_DEVICE_TABLE(acpi, fujitsu_ids); 479