1 /*-*-linux-c-*-*/ 2 3 /* 4 Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@physics.adelaide.edu.au> 5 Copyright (C) 2008 Peter Gruber <nokos@gmx.net> 6 Copyright (C) 2008 Tony Vroon <tony@linx.net> 7 Based on earlier work: 8 Copyright (C) 2003 Shane Spencer <shane@bogomip.com> 9 Adrian Yee <brewt-fujitsu@brewt.org> 10 11 Templated from msi-laptop.c and thinkpad_acpi.c which is copyright 12 by its respective authors. 13 14 This program is free software; you can redistribute it and/or modify 15 it under the terms of the GNU General Public License as published by 16 the Free Software Foundation; either version 2 of the License, or 17 (at your option) any later version. 18 19 This program is distributed in the hope that it will be useful, but 20 WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 General Public License for more details. 23 24 You should have received a copy of the GNU General Public License 25 along with this program; if not, write to the Free Software 26 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 27 02110-1301, USA. 28 */ 29 30 /* 31 * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional 32 * features made available on a range of Fujitsu laptops including the 33 * P2xxx/P5xxx/S6xxx/S7xxx series. 34 * 35 * This driver exports a few files in /sys/devices/platform/fujitsu-laptop/; 36 * others may be added at a later date. 37 * 38 * lcd_level - Screen brightness: contains a single integer in the 39 * range 0..7. (rw) 40 * 41 * In addition to these platform device attributes the driver 42 * registers itself in the Linux backlight control subsystem and is 43 * available to userspace under /sys/class/backlight/fujitsu-laptop/. 44 * 45 * Hotkeys present on certain Fujitsu laptops (eg: the S6xxx series) are 46 * also supported by this driver. 47 * 48 * This driver has been tested on a Fujitsu Lifebook S6410, S7020 and 49 * P8010. It should work on most P-series and S-series Lifebooks, but 50 * YMMV. 51 * 52 * The module parameter use_alt_lcd_levels switches between different ACPI 53 * brightness controls which are used by different Fujitsu laptops. In most 54 * cases the correct method is automatically detected. "use_alt_lcd_levels=1" 55 * is applicable for a Fujitsu Lifebook S6410 if autodetection fails. 56 * 57 */ 58 59 #include <linux/module.h> 60 #include <linux/kernel.h> 61 #include <linux/init.h> 62 #include <linux/acpi.h> 63 #include <linux/dmi.h> 64 #include <linux/backlight.h> 65 #include <linux/input.h> 66 #include <linux/kfifo.h> 67 #include <linux/video_output.h> 68 #include <linux/platform_device.h> 69 #ifdef CONFIG_LEDS_CLASS 70 #include <linux/leds.h> 71 #endif 72 73 #define FUJITSU_DRIVER_VERSION "0.5.0" 74 75 #define FUJITSU_LCD_N_LEVELS 8 76 77 #define ACPI_FUJITSU_CLASS "fujitsu" 78 #define ACPI_FUJITSU_HID "FUJ02B1" 79 #define ACPI_FUJITSU_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI brightness driver" 80 #define ACPI_FUJITSU_DEVICE_NAME "Fujitsu FUJ02B1" 81 #define ACPI_FUJITSU_HOTKEY_HID "FUJ02E3" 82 #define ACPI_FUJITSU_HOTKEY_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver" 83 #define ACPI_FUJITSU_HOTKEY_DEVICE_NAME "Fujitsu FUJ02E3" 84 85 #define ACPI_FUJITSU_NOTIFY_CODE1 0x80 86 87 #define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86 88 #define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87 89 90 /* FUNC interface - command values */ 91 #define FUNC_RFKILL 0x1000 92 #define FUNC_LEDS 0x1001 93 #define FUNC_BUTTONS 0x1002 94 #define FUNC_BACKLIGHT 0x1004 95 96 /* FUNC interface - responses */ 97 #define UNSUPPORTED_CMD 0x80000000 98 99 #ifdef CONFIG_LEDS_CLASS 100 /* FUNC interface - LED control */ 101 #define FUNC_LED_OFF 0x1 102 #define FUNC_LED_ON 0x30001 103 #define KEYBOARD_LAMPS 0x100 104 #define LOGOLAMP_POWERON 0x2000 105 #define LOGOLAMP_ALWAYS 0x4000 106 #endif 107 108 /* Hotkey details */ 109 #define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */ 110 #define KEY2_CODE 0x411 111 #define KEY3_CODE 0x412 112 #define KEY4_CODE 0x413 113 114 #define MAX_HOTKEY_RINGBUFFER_SIZE 100 115 #define RINGBUFFERSIZE 40 116 117 /* Debugging */ 118 #define FUJLAPTOP_LOG ACPI_FUJITSU_HID ": " 119 #define FUJLAPTOP_ERR KERN_ERR FUJLAPTOP_LOG 120 #define FUJLAPTOP_NOTICE KERN_NOTICE FUJLAPTOP_LOG 121 #define FUJLAPTOP_INFO KERN_INFO FUJLAPTOP_LOG 122 #define FUJLAPTOP_DEBUG KERN_DEBUG FUJLAPTOP_LOG 123 124 #define FUJLAPTOP_DBG_ALL 0xffff 125 #define FUJLAPTOP_DBG_ERROR 0x0001 126 #define FUJLAPTOP_DBG_WARN 0x0002 127 #define FUJLAPTOP_DBG_INFO 0x0004 128 #define FUJLAPTOP_DBG_TRACE 0x0008 129 130 #define dbg_printk(a_dbg_level, format, arg...) \ 131 do { if (dbg_level & a_dbg_level) \ 132 printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \ 133 } while (0) 134 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG 135 #define vdbg_printk(a_dbg_level, format, arg...) \ 136 dbg_printk(a_dbg_level, format, ## arg) 137 #else 138 #define vdbg_printk(a_dbg_level, format, arg...) 139 #endif 140 141 /* Device controlling the backlight and associated keys */ 142 struct fujitsu_t { 143 acpi_handle acpi_handle; 144 struct acpi_device *dev; 145 struct input_dev *input; 146 char phys[32]; 147 struct backlight_device *bl_device; 148 struct platform_device *pf_device; 149 int keycode1, keycode2, keycode3, keycode4; 150 151 unsigned int max_brightness; 152 unsigned int brightness_changed; 153 unsigned int brightness_level; 154 }; 155 156 static struct fujitsu_t *fujitsu; 157 static int use_alt_lcd_levels = -1; 158 static int disable_brightness_adjust = -1; 159 160 /* Device used to access other hotkeys on the laptop */ 161 struct fujitsu_hotkey_t { 162 acpi_handle acpi_handle; 163 struct acpi_device *dev; 164 struct input_dev *input; 165 char phys[32]; 166 struct platform_device *pf_device; 167 struct kfifo *fifo; 168 spinlock_t fifo_lock; 169 int rfkill_supported; 170 int rfkill_state; 171 int logolamp_registered; 172 int kblamps_registered; 173 }; 174 175 static struct fujitsu_hotkey_t *fujitsu_hotkey; 176 177 static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event, 178 void *data); 179 180 #ifdef CONFIG_LEDS_CLASS 181 static enum led_brightness logolamp_get(struct led_classdev *cdev); 182 static void logolamp_set(struct led_classdev *cdev, 183 enum led_brightness brightness); 184 185 struct led_classdev logolamp_led = { 186 .name = "fujitsu::logolamp", 187 .brightness_get = logolamp_get, 188 .brightness_set = logolamp_set 189 }; 190 191 static enum led_brightness kblamps_get(struct led_classdev *cdev); 192 static void kblamps_set(struct led_classdev *cdev, 193 enum led_brightness brightness); 194 195 struct led_classdev kblamps_led = { 196 .name = "fujitsu::kblamps", 197 .brightness_get = kblamps_get, 198 .brightness_set = kblamps_set 199 }; 200 #endif 201 202 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG 203 static u32 dbg_level = 0x03; 204 #endif 205 206 static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data); 207 208 /* Fujitsu ACPI interface function */ 209 210 static int call_fext_func(int cmd, int arg0, int arg1, int arg2) 211 { 212 acpi_status status = AE_OK; 213 union acpi_object params[4] = { 214 { .type = ACPI_TYPE_INTEGER }, 215 { .type = ACPI_TYPE_INTEGER }, 216 { .type = ACPI_TYPE_INTEGER }, 217 { .type = ACPI_TYPE_INTEGER } 218 }; 219 struct acpi_object_list arg_list = { 4, ¶ms[0] }; 220 struct acpi_buffer output; 221 union acpi_object out_obj; 222 acpi_handle handle = NULL; 223 224 status = acpi_get_handle(fujitsu_hotkey->acpi_handle, "FUNC", &handle); 225 if (ACPI_FAILURE(status)) { 226 vdbg_printk(FUJLAPTOP_DBG_ERROR, 227 "FUNC interface is not present\n"); 228 return -ENODEV; 229 } 230 231 params[0].integer.value = cmd; 232 params[1].integer.value = arg0; 233 params[2].integer.value = arg1; 234 params[3].integer.value = arg2; 235 236 output.length = sizeof(out_obj); 237 output.pointer = &out_obj; 238 239 status = acpi_evaluate_object(handle, NULL, &arg_list, &output); 240 if (ACPI_FAILURE(status)) { 241 vdbg_printk(FUJLAPTOP_DBG_WARN, 242 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) call failed\n", 243 cmd, arg0, arg1, arg2); 244 return -ENODEV; 245 } 246 247 if (out_obj.type != ACPI_TYPE_INTEGER) { 248 vdbg_printk(FUJLAPTOP_DBG_WARN, 249 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) did not " 250 "return an integer\n", 251 cmd, arg0, arg1, arg2); 252 return -ENODEV; 253 } 254 255 vdbg_printk(FUJLAPTOP_DBG_TRACE, 256 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", 257 cmd, arg0, arg1, arg2, (int)out_obj.integer.value); 258 return out_obj.integer.value; 259 } 260 261 #ifdef CONFIG_LEDS_CLASS 262 /* LED class callbacks */ 263 264 static void logolamp_set(struct led_classdev *cdev, 265 enum led_brightness brightness) 266 { 267 if (brightness >= LED_FULL) { 268 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); 269 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON); 270 } else if (brightness >= LED_HALF) { 271 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); 272 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF); 273 } else { 274 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF); 275 } 276 } 277 278 static void kblamps_set(struct led_classdev *cdev, 279 enum led_brightness brightness) 280 { 281 if (brightness >= LED_FULL) 282 call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON); 283 else 284 call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF); 285 } 286 287 static enum led_brightness logolamp_get(struct led_classdev *cdev) 288 { 289 enum led_brightness brightness = LED_OFF; 290 int poweron, always; 291 292 poweron = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); 293 if (poweron == FUNC_LED_ON) { 294 brightness = LED_HALF; 295 always = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); 296 if (always == FUNC_LED_ON) 297 brightness = LED_FULL; 298 } 299 return brightness; 300 } 301 302 static enum led_brightness kblamps_get(struct led_classdev *cdev) 303 { 304 enum led_brightness brightness = LED_OFF; 305 306 if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON) 307 brightness = LED_FULL; 308 309 return brightness; 310 } 311 #endif 312 313 /* Hardware access for LCD brightness control */ 314 315 static int set_lcd_level(int level) 316 { 317 acpi_status status = AE_OK; 318 union acpi_object arg0 = { ACPI_TYPE_INTEGER }; 319 struct acpi_object_list arg_list = { 1, &arg0 }; 320 acpi_handle handle = NULL; 321 322 vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n", 323 level); 324 325 if (level < 0 || level >= fujitsu->max_brightness) 326 return -EINVAL; 327 328 if (!fujitsu) 329 return -EINVAL; 330 331 status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle); 332 if (ACPI_FAILURE(status)) { 333 vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n"); 334 return -ENODEV; 335 } 336 337 arg0.integer.value = level; 338 339 status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); 340 if (ACPI_FAILURE(status)) 341 return -ENODEV; 342 343 return 0; 344 } 345 346 static int set_lcd_level_alt(int level) 347 { 348 acpi_status status = AE_OK; 349 union acpi_object arg0 = { ACPI_TYPE_INTEGER }; 350 struct acpi_object_list arg_list = { 1, &arg0 }; 351 acpi_handle handle = NULL; 352 353 vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n", 354 level); 355 356 if (level < 0 || level >= fujitsu->max_brightness) 357 return -EINVAL; 358 359 if (!fujitsu) 360 return -EINVAL; 361 362 status = acpi_get_handle(fujitsu->acpi_handle, "SBL2", &handle); 363 if (ACPI_FAILURE(status)) { 364 vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n"); 365 return -ENODEV; 366 } 367 368 arg0.integer.value = level; 369 370 status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); 371 if (ACPI_FAILURE(status)) 372 return -ENODEV; 373 374 return 0; 375 } 376 377 static int get_lcd_level(void) 378 { 379 unsigned long long state = 0; 380 acpi_status status = AE_OK; 381 382 vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n"); 383 384 status = 385 acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state); 386 if (status < 0) 387 return status; 388 389 fujitsu->brightness_level = state & 0x0fffffff; 390 391 if (state & 0x80000000) 392 fujitsu->brightness_changed = 1; 393 else 394 fujitsu->brightness_changed = 0; 395 396 return fujitsu->brightness_level; 397 } 398 399 static int get_max_brightness(void) 400 { 401 unsigned long long state = 0; 402 acpi_status status = AE_OK; 403 404 vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n"); 405 406 status = 407 acpi_evaluate_integer(fujitsu->acpi_handle, "RBLL", NULL, &state); 408 if (status < 0) 409 return status; 410 411 fujitsu->max_brightness = state; 412 413 return fujitsu->max_brightness; 414 } 415 416 /* Backlight device stuff */ 417 418 static int bl_get_brightness(struct backlight_device *b) 419 { 420 return get_lcd_level(); 421 } 422 423 static int bl_update_status(struct backlight_device *b) 424 { 425 int ret; 426 if (b->props.power == 4) 427 ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3); 428 else 429 ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0); 430 if (ret != 0) 431 vdbg_printk(FUJLAPTOP_DBG_ERROR, 432 "Unable to adjust backlight power, error code %i\n", 433 ret); 434 435 if (use_alt_lcd_levels) 436 ret = set_lcd_level_alt(b->props.brightness); 437 else 438 ret = set_lcd_level(b->props.brightness); 439 if (ret != 0) 440 vdbg_printk(FUJLAPTOP_DBG_ERROR, 441 "Unable to adjust LCD brightness, error code %i\n", 442 ret); 443 return ret; 444 } 445 446 static struct backlight_ops fujitsubl_ops = { 447 .get_brightness = bl_get_brightness, 448 .update_status = bl_update_status, 449 }; 450 451 /* Platform LCD brightness device */ 452 453 static ssize_t 454 show_max_brightness(struct device *dev, 455 struct device_attribute *attr, char *buf) 456 { 457 458 int ret; 459 460 ret = get_max_brightness(); 461 if (ret < 0) 462 return ret; 463 464 return sprintf(buf, "%i\n", ret); 465 } 466 467 static ssize_t 468 show_brightness_changed(struct device *dev, 469 struct device_attribute *attr, char *buf) 470 { 471 472 int ret; 473 474 ret = fujitsu->brightness_changed; 475 if (ret < 0) 476 return ret; 477 478 return sprintf(buf, "%i\n", ret); 479 } 480 481 static ssize_t show_lcd_level(struct device *dev, 482 struct device_attribute *attr, char *buf) 483 { 484 485 int ret; 486 487 ret = get_lcd_level(); 488 if (ret < 0) 489 return ret; 490 491 return sprintf(buf, "%i\n", ret); 492 } 493 494 static ssize_t store_lcd_level(struct device *dev, 495 struct device_attribute *attr, const char *buf, 496 size_t count) 497 { 498 499 int level, ret; 500 501 if (sscanf(buf, "%i", &level) != 1 502 || (level < 0 || level >= fujitsu->max_brightness)) 503 return -EINVAL; 504 505 if (use_alt_lcd_levels) 506 ret = set_lcd_level_alt(level); 507 else 508 ret = set_lcd_level(level); 509 if (ret < 0) 510 return ret; 511 512 ret = get_lcd_level(); 513 if (ret < 0) 514 return ret; 515 516 return count; 517 } 518 519 static ssize_t 520 ignore_store(struct device *dev, 521 struct device_attribute *attr, const char *buf, size_t count) 522 { 523 return count; 524 } 525 526 static ssize_t 527 show_lid_state(struct device *dev, 528 struct device_attribute *attr, char *buf) 529 { 530 if (!(fujitsu_hotkey->rfkill_supported & 0x100)) 531 return sprintf(buf, "unknown\n"); 532 if (fujitsu_hotkey->rfkill_state & 0x100) 533 return sprintf(buf, "open\n"); 534 else 535 return sprintf(buf, "closed\n"); 536 } 537 538 static ssize_t 539 show_dock_state(struct device *dev, 540 struct device_attribute *attr, char *buf) 541 { 542 if (!(fujitsu_hotkey->rfkill_supported & 0x200)) 543 return sprintf(buf, "unknown\n"); 544 if (fujitsu_hotkey->rfkill_state & 0x200) 545 return sprintf(buf, "docked\n"); 546 else 547 return sprintf(buf, "undocked\n"); 548 } 549 550 static ssize_t 551 show_radios_state(struct device *dev, 552 struct device_attribute *attr, char *buf) 553 { 554 if (!(fujitsu_hotkey->rfkill_supported & 0x20)) 555 return sprintf(buf, "unknown\n"); 556 if (fujitsu_hotkey->rfkill_state & 0x20) 557 return sprintf(buf, "on\n"); 558 else 559 return sprintf(buf, "killed\n"); 560 } 561 562 static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store); 563 static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed, 564 ignore_store); 565 static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); 566 static DEVICE_ATTR(lid, 0444, show_lid_state, ignore_store); 567 static DEVICE_ATTR(dock, 0444, show_dock_state, ignore_store); 568 static DEVICE_ATTR(radios, 0444, show_radios_state, ignore_store); 569 570 static struct attribute *fujitsupf_attributes[] = { 571 &dev_attr_brightness_changed.attr, 572 &dev_attr_max_brightness.attr, 573 &dev_attr_lcd_level.attr, 574 &dev_attr_lid.attr, 575 &dev_attr_dock.attr, 576 &dev_attr_radios.attr, 577 NULL 578 }; 579 580 static struct attribute_group fujitsupf_attribute_group = { 581 .attrs = fujitsupf_attributes 582 }; 583 584 static struct platform_driver fujitsupf_driver = { 585 .driver = { 586 .name = "fujitsu-laptop", 587 .owner = THIS_MODULE, 588 } 589 }; 590 591 static void dmi_check_cb_common(const struct dmi_system_id *id) 592 { 593 acpi_handle handle; 594 printk(KERN_INFO "fujitsu-laptop: Identified laptop model '%s'.\n", 595 id->ident); 596 if (use_alt_lcd_levels == -1) { 597 if (ACPI_SUCCESS(acpi_get_handle(NULL, 598 "\\_SB.PCI0.LPCB.FJEX.SBL2", &handle))) 599 use_alt_lcd_levels = 1; 600 else 601 use_alt_lcd_levels = 0; 602 vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detected usealt as " 603 "%i\n", use_alt_lcd_levels); 604 } 605 } 606 607 static int dmi_check_cb_s6410(const struct dmi_system_id *id) 608 { 609 dmi_check_cb_common(id); 610 fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ 611 fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */ 612 return 0; 613 } 614 615 static int dmi_check_cb_s6420(const struct dmi_system_id *id) 616 { 617 dmi_check_cb_common(id); 618 fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ 619 fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */ 620 return 0; 621 } 622 623 static int dmi_check_cb_p8010(const struct dmi_system_id *id) 624 { 625 dmi_check_cb_common(id); 626 fujitsu->keycode1 = KEY_HELP; /* "Support" */ 627 fujitsu->keycode3 = KEY_SWITCHVIDEOMODE; /* "Presentation" */ 628 fujitsu->keycode4 = KEY_WWW; /* "Internet" */ 629 return 0; 630 } 631 632 static struct dmi_system_id fujitsu_dmi_table[] = { 633 { 634 .ident = "Fujitsu Siemens S6410", 635 .matches = { 636 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 637 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"), 638 }, 639 .callback = dmi_check_cb_s6410}, 640 { 641 .ident = "Fujitsu Siemens S6420", 642 .matches = { 643 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 644 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"), 645 }, 646 .callback = dmi_check_cb_s6420}, 647 { 648 .ident = "Fujitsu LifeBook P8010", 649 .matches = { 650 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 651 DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"), 652 }, 653 .callback = dmi_check_cb_p8010}, 654 {} 655 }; 656 657 /* ACPI device for LCD brightness control */ 658 659 static int acpi_fujitsu_add(struct acpi_device *device) 660 { 661 acpi_status status; 662 acpi_handle handle; 663 int result = 0; 664 int state = 0; 665 struct input_dev *input; 666 int error; 667 668 if (!device) 669 return -EINVAL; 670 671 fujitsu->acpi_handle = device->handle; 672 sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME); 673 sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); 674 device->driver_data = fujitsu; 675 676 status = acpi_install_notify_handler(device->handle, 677 ACPI_DEVICE_NOTIFY, 678 acpi_fujitsu_notify, fujitsu); 679 680 if (ACPI_FAILURE(status)) { 681 printk(KERN_ERR "Error installing notify handler\n"); 682 error = -ENODEV; 683 goto err_stop; 684 } 685 686 fujitsu->input = input = input_allocate_device(); 687 if (!input) { 688 error = -ENOMEM; 689 goto err_uninstall_notify; 690 } 691 692 snprintf(fujitsu->phys, sizeof(fujitsu->phys), 693 "%s/video/input0", acpi_device_hid(device)); 694 695 input->name = acpi_device_name(device); 696 input->phys = fujitsu->phys; 697 input->id.bustype = BUS_HOST; 698 input->id.product = 0x06; 699 input->dev.parent = &device->dev; 700 input->evbit[0] = BIT(EV_KEY); 701 set_bit(KEY_BRIGHTNESSUP, input->keybit); 702 set_bit(KEY_BRIGHTNESSDOWN, input->keybit); 703 set_bit(KEY_UNKNOWN, input->keybit); 704 705 error = input_register_device(input); 706 if (error) 707 goto err_free_input_dev; 708 709 result = acpi_bus_get_power(fujitsu->acpi_handle, &state); 710 if (result) { 711 printk(KERN_ERR "Error reading power state\n"); 712 goto end; 713 } 714 715 printk(KERN_INFO PREFIX "%s [%s] (%s)\n", 716 acpi_device_name(device), acpi_device_bid(device), 717 !device->power.state ? "on" : "off"); 718 719 fujitsu->dev = device; 720 721 if (ACPI_SUCCESS 722 (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) { 723 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n"); 724 if (ACPI_FAILURE 725 (acpi_evaluate_object 726 (device->handle, METHOD_NAME__INI, NULL, NULL))) 727 printk(KERN_ERR "_INI Method failed\n"); 728 } 729 730 /* do config (detect defaults) */ 731 use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0; 732 disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0; 733 vdbg_printk(FUJLAPTOP_DBG_INFO, 734 "config: [alt interface: %d], [adjust disable: %d]\n", 735 use_alt_lcd_levels, disable_brightness_adjust); 736 737 if (get_max_brightness() <= 0) 738 fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS; 739 get_lcd_level(); 740 741 return result; 742 743 end: 744 err_free_input_dev: 745 input_free_device(input); 746 err_uninstall_notify: 747 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, 748 acpi_fujitsu_notify); 749 err_stop: 750 751 return result; 752 } 753 754 static int acpi_fujitsu_remove(struct acpi_device *device, int type) 755 { 756 acpi_status status; 757 struct fujitsu_t *fujitsu = NULL; 758 759 if (!device || !acpi_driver_data(device)) 760 return -EINVAL; 761 762 fujitsu = acpi_driver_data(device); 763 764 status = acpi_remove_notify_handler(fujitsu->acpi_handle, 765 ACPI_DEVICE_NOTIFY, 766 acpi_fujitsu_notify); 767 768 if (!device || !acpi_driver_data(device)) 769 return -EINVAL; 770 771 fujitsu->acpi_handle = NULL; 772 773 return 0; 774 } 775 776 /* Brightness notify */ 777 778 static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data) 779 { 780 struct input_dev *input; 781 int keycode; 782 int oldb, newb; 783 784 input = fujitsu->input; 785 786 switch (event) { 787 case ACPI_FUJITSU_NOTIFY_CODE1: 788 keycode = 0; 789 oldb = fujitsu->brightness_level; 790 get_lcd_level(); 791 newb = fujitsu->brightness_level; 792 793 vdbg_printk(FUJLAPTOP_DBG_TRACE, 794 "brightness button event [%i -> %i (%i)]\n", 795 oldb, newb, fujitsu->brightness_changed); 796 797 if (oldb < newb) { 798 if (disable_brightness_adjust != 1) { 799 if (use_alt_lcd_levels) 800 set_lcd_level_alt(newb); 801 else 802 set_lcd_level(newb); 803 } 804 acpi_bus_generate_proc_event(fujitsu->dev, 805 ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0); 806 keycode = KEY_BRIGHTNESSUP; 807 } else if (oldb > newb) { 808 if (disable_brightness_adjust != 1) { 809 if (use_alt_lcd_levels) 810 set_lcd_level_alt(newb); 811 else 812 set_lcd_level(newb); 813 } 814 acpi_bus_generate_proc_event(fujitsu->dev, 815 ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0); 816 keycode = KEY_BRIGHTNESSDOWN; 817 } 818 break; 819 default: 820 keycode = KEY_UNKNOWN; 821 vdbg_printk(FUJLAPTOP_DBG_WARN, 822 "unsupported event [0x%x]\n", event); 823 break; 824 } 825 826 if (keycode != 0) { 827 input_report_key(input, keycode, 1); 828 input_sync(input); 829 input_report_key(input, keycode, 0); 830 input_sync(input); 831 } 832 833 return; 834 } 835 836 /* ACPI device for hotkey handling */ 837 838 static int acpi_fujitsu_hotkey_add(struct acpi_device *device) 839 { 840 acpi_status status; 841 acpi_handle handle; 842 int result = 0; 843 int state = 0; 844 struct input_dev *input; 845 int error; 846 int i; 847 848 if (!device) 849 return -EINVAL; 850 851 fujitsu_hotkey->acpi_handle = device->handle; 852 sprintf(acpi_device_name(device), "%s", 853 ACPI_FUJITSU_HOTKEY_DEVICE_NAME); 854 sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); 855 device->driver_data = fujitsu_hotkey; 856 857 status = acpi_install_notify_handler(device->handle, 858 ACPI_DEVICE_NOTIFY, 859 acpi_fujitsu_hotkey_notify, 860 fujitsu_hotkey); 861 862 if (ACPI_FAILURE(status)) { 863 printk(KERN_ERR "Error installing notify handler\n"); 864 error = -ENODEV; 865 goto err_stop; 866 } 867 868 /* kfifo */ 869 spin_lock_init(&fujitsu_hotkey->fifo_lock); 870 fujitsu_hotkey->fifo = 871 kfifo_alloc(RINGBUFFERSIZE * sizeof(int), GFP_KERNEL, 872 &fujitsu_hotkey->fifo_lock); 873 if (IS_ERR(fujitsu_hotkey->fifo)) { 874 printk(KERN_ERR "kfifo_alloc failed\n"); 875 error = PTR_ERR(fujitsu_hotkey->fifo); 876 goto err_stop; 877 } 878 879 fujitsu_hotkey->input = input = input_allocate_device(); 880 if (!input) { 881 error = -ENOMEM; 882 goto err_uninstall_notify; 883 } 884 885 snprintf(fujitsu_hotkey->phys, sizeof(fujitsu_hotkey->phys), 886 "%s/video/input0", acpi_device_hid(device)); 887 888 input->name = acpi_device_name(device); 889 input->phys = fujitsu_hotkey->phys; 890 input->id.bustype = BUS_HOST; 891 input->id.product = 0x06; 892 input->dev.parent = &device->dev; 893 894 set_bit(EV_KEY, input->evbit); 895 set_bit(fujitsu->keycode1, input->keybit); 896 set_bit(fujitsu->keycode2, input->keybit); 897 set_bit(fujitsu->keycode3, input->keybit); 898 set_bit(fujitsu->keycode4, input->keybit); 899 set_bit(KEY_UNKNOWN, input->keybit); 900 901 error = input_register_device(input); 902 if (error) 903 goto err_free_input_dev; 904 905 result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state); 906 if (result) { 907 printk(KERN_ERR "Error reading power state\n"); 908 goto end; 909 } 910 911 printk(KERN_INFO PREFIX "%s [%s] (%s)\n", 912 acpi_device_name(device), acpi_device_bid(device), 913 !device->power.state ? "on" : "off"); 914 915 fujitsu_hotkey->dev = device; 916 917 if (ACPI_SUCCESS 918 (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) { 919 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n"); 920 if (ACPI_FAILURE 921 (acpi_evaluate_object 922 (device->handle, METHOD_NAME__INI, NULL, NULL))) 923 printk(KERN_ERR "_INI Method failed\n"); 924 } 925 926 i = 0; 927 while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 928 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) 929 ; /* No action, result is discarded */ 930 vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i); 931 932 fujitsu_hotkey->rfkill_supported = 933 call_fext_func(FUNC_RFKILL, 0x0, 0x0, 0x0); 934 935 /* Make sure our bitmask of supported functions is cleared if the 936 RFKILL function block is not implemented, like on the S7020. */ 937 if (fujitsu_hotkey->rfkill_supported == UNSUPPORTED_CMD) 938 fujitsu_hotkey->rfkill_supported = 0; 939 940 if (fujitsu_hotkey->rfkill_supported) 941 fujitsu_hotkey->rfkill_state = 942 call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); 943 944 /* Suspect this is a keymap of the application panel, print it */ 945 printk(KERN_INFO "fujitsu-laptop: BTNI: [0x%x]\n", 946 call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0)); 947 948 #ifdef CONFIG_LEDS_CLASS 949 if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { 950 result = led_classdev_register(&fujitsu->pf_device->dev, 951 &logolamp_led); 952 if (result == 0) { 953 fujitsu_hotkey->logolamp_registered = 1; 954 } else { 955 printk(KERN_ERR "fujitsu-laptop: Could not register " 956 "LED handler for logo lamp, error %i\n", result); 957 } 958 } 959 960 if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && 961 (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) { 962 result = led_classdev_register(&fujitsu->pf_device->dev, 963 &kblamps_led); 964 if (result == 0) { 965 fujitsu_hotkey->kblamps_registered = 1; 966 } else { 967 printk(KERN_ERR "fujitsu-laptop: Could not register " 968 "LED handler for keyboard lamps, error %i\n", result); 969 } 970 } 971 #endif 972 973 return result; 974 975 end: 976 err_free_input_dev: 977 input_free_device(input); 978 err_uninstall_notify: 979 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, 980 acpi_fujitsu_hotkey_notify); 981 kfifo_free(fujitsu_hotkey->fifo); 982 err_stop: 983 984 return result; 985 } 986 987 static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type) 988 { 989 acpi_status status; 990 struct fujitsu_hotkey_t *fujitsu_hotkey = NULL; 991 992 if (!device || !acpi_driver_data(device)) 993 return -EINVAL; 994 995 fujitsu_hotkey = acpi_driver_data(device); 996 997 status = acpi_remove_notify_handler(fujitsu_hotkey->acpi_handle, 998 ACPI_DEVICE_NOTIFY, 999 acpi_fujitsu_hotkey_notify); 1000 1001 fujitsu_hotkey->acpi_handle = NULL; 1002 1003 kfifo_free(fujitsu_hotkey->fifo); 1004 1005 return 0; 1006 } 1007 1008 static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event, 1009 void *data) 1010 { 1011 struct input_dev *input; 1012 int keycode, keycode_r; 1013 unsigned int irb = 1; 1014 int i, status; 1015 1016 input = fujitsu_hotkey->input; 1017 1018 if (fujitsu_hotkey->rfkill_supported) 1019 fujitsu_hotkey->rfkill_state = 1020 call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); 1021 1022 switch (event) { 1023 case ACPI_FUJITSU_NOTIFY_CODE1: 1024 i = 0; 1025 while ((irb = 1026 call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 1027 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { 1028 switch (irb & 0x4ff) { 1029 case KEY1_CODE: 1030 keycode = fujitsu->keycode1; 1031 break; 1032 case KEY2_CODE: 1033 keycode = fujitsu->keycode2; 1034 break; 1035 case KEY3_CODE: 1036 keycode = fujitsu->keycode3; 1037 break; 1038 case KEY4_CODE: 1039 keycode = fujitsu->keycode4; 1040 break; 1041 case 0: 1042 keycode = 0; 1043 break; 1044 default: 1045 vdbg_printk(FUJLAPTOP_DBG_WARN, 1046 "Unknown GIRB result [%x]\n", irb); 1047 keycode = -1; 1048 break; 1049 } 1050 if (keycode > 0) { 1051 vdbg_printk(FUJLAPTOP_DBG_TRACE, 1052 "Push keycode into ringbuffer [%d]\n", 1053 keycode); 1054 status = kfifo_put(fujitsu_hotkey->fifo, 1055 (unsigned char *)&keycode, 1056 sizeof(keycode)); 1057 if (status != sizeof(keycode)) { 1058 vdbg_printk(FUJLAPTOP_DBG_WARN, 1059 "Could not push keycode [0x%x]\n", 1060 keycode); 1061 } else { 1062 input_report_key(input, keycode, 1); 1063 input_sync(input); 1064 } 1065 } else if (keycode == 0) { 1066 while ((status = 1067 kfifo_get 1068 (fujitsu_hotkey->fifo, (unsigned char *) 1069 &keycode_r, 1070 sizeof 1071 (keycode_r))) == sizeof(keycode_r)) { 1072 input_report_key(input, keycode_r, 0); 1073 input_sync(input); 1074 vdbg_printk(FUJLAPTOP_DBG_TRACE, 1075 "Pop keycode from ringbuffer [%d]\n", 1076 keycode_r); 1077 } 1078 } 1079 } 1080 1081 break; 1082 default: 1083 keycode = KEY_UNKNOWN; 1084 vdbg_printk(FUJLAPTOP_DBG_WARN, 1085 "Unsupported event [0x%x]\n", event); 1086 input_report_key(input, keycode, 1); 1087 input_sync(input); 1088 input_report_key(input, keycode, 0); 1089 input_sync(input); 1090 break; 1091 } 1092 1093 return; 1094 } 1095 1096 /* Initialization */ 1097 1098 static const struct acpi_device_id fujitsu_device_ids[] = { 1099 {ACPI_FUJITSU_HID, 0}, 1100 {"", 0}, 1101 }; 1102 1103 static struct acpi_driver acpi_fujitsu_driver = { 1104 .name = ACPI_FUJITSU_DRIVER_NAME, 1105 .class = ACPI_FUJITSU_CLASS, 1106 .ids = fujitsu_device_ids, 1107 .ops = { 1108 .add = acpi_fujitsu_add, 1109 .remove = acpi_fujitsu_remove, 1110 }, 1111 }; 1112 1113 static const struct acpi_device_id fujitsu_hotkey_device_ids[] = { 1114 {ACPI_FUJITSU_HOTKEY_HID, 0}, 1115 {"", 0}, 1116 }; 1117 1118 static struct acpi_driver acpi_fujitsu_hotkey_driver = { 1119 .name = ACPI_FUJITSU_HOTKEY_DRIVER_NAME, 1120 .class = ACPI_FUJITSU_CLASS, 1121 .ids = fujitsu_hotkey_device_ids, 1122 .ops = { 1123 .add = acpi_fujitsu_hotkey_add, 1124 .remove = acpi_fujitsu_hotkey_remove, 1125 }, 1126 }; 1127 1128 static int __init fujitsu_init(void) 1129 { 1130 int ret, result, max_brightness; 1131 1132 if (acpi_disabled) 1133 return -ENODEV; 1134 1135 fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL); 1136 if (!fujitsu) 1137 return -ENOMEM; 1138 memset(fujitsu, 0, sizeof(struct fujitsu_t)); 1139 fujitsu->keycode1 = KEY_PROG1; 1140 fujitsu->keycode2 = KEY_PROG2; 1141 fujitsu->keycode3 = KEY_PROG3; 1142 fujitsu->keycode4 = KEY_PROG4; 1143 dmi_check_system(fujitsu_dmi_table); 1144 1145 result = acpi_bus_register_driver(&acpi_fujitsu_driver); 1146 if (result < 0) { 1147 ret = -ENODEV; 1148 goto fail_acpi; 1149 } 1150 1151 /* Register platform stuff */ 1152 1153 fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1); 1154 if (!fujitsu->pf_device) { 1155 ret = -ENOMEM; 1156 goto fail_platform_driver; 1157 } 1158 1159 ret = platform_device_add(fujitsu->pf_device); 1160 if (ret) 1161 goto fail_platform_device1; 1162 1163 ret = 1164 sysfs_create_group(&fujitsu->pf_device->dev.kobj, 1165 &fujitsupf_attribute_group); 1166 if (ret) 1167 goto fail_platform_device2; 1168 1169 /* Register backlight stuff */ 1170 1171 if (!acpi_video_backlight_support()) { 1172 fujitsu->bl_device = 1173 backlight_device_register("fujitsu-laptop", NULL, NULL, 1174 &fujitsubl_ops); 1175 if (IS_ERR(fujitsu->bl_device)) 1176 return PTR_ERR(fujitsu->bl_device); 1177 max_brightness = fujitsu->max_brightness; 1178 fujitsu->bl_device->props.max_brightness = max_brightness - 1; 1179 fujitsu->bl_device->props.brightness = fujitsu->brightness_level; 1180 } 1181 1182 ret = platform_driver_register(&fujitsupf_driver); 1183 if (ret) 1184 goto fail_backlight; 1185 1186 /* Register hotkey driver */ 1187 1188 fujitsu_hotkey = kmalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL); 1189 if (!fujitsu_hotkey) { 1190 ret = -ENOMEM; 1191 goto fail_hotkey; 1192 } 1193 memset(fujitsu_hotkey, 0, sizeof(struct fujitsu_hotkey_t)); 1194 1195 result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver); 1196 if (result < 0) { 1197 ret = -ENODEV; 1198 goto fail_hotkey1; 1199 } 1200 1201 /* Sync backlight power status (needs FUJ02E3 device, hence deferred) */ 1202 1203 if (!acpi_video_backlight_support()) { 1204 if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3) 1205 fujitsu->bl_device->props.power = 4; 1206 else 1207 fujitsu->bl_device->props.power = 0; 1208 } 1209 1210 printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION 1211 " successfully loaded.\n"); 1212 1213 return 0; 1214 1215 fail_hotkey1: 1216 1217 kfree(fujitsu_hotkey); 1218 1219 fail_hotkey: 1220 1221 platform_driver_unregister(&fujitsupf_driver); 1222 1223 fail_backlight: 1224 1225 if (fujitsu->bl_device) 1226 backlight_device_unregister(fujitsu->bl_device); 1227 1228 fail_platform_device2: 1229 1230 platform_device_del(fujitsu->pf_device); 1231 1232 fail_platform_device1: 1233 1234 platform_device_put(fujitsu->pf_device); 1235 1236 fail_platform_driver: 1237 1238 acpi_bus_unregister_driver(&acpi_fujitsu_driver); 1239 1240 fail_acpi: 1241 1242 kfree(fujitsu); 1243 1244 return ret; 1245 } 1246 1247 static void __exit fujitsu_cleanup(void) 1248 { 1249 #ifdef CONFIG_LEDS_CLASS 1250 if (fujitsu_hotkey->logolamp_registered != 0) 1251 led_classdev_unregister(&logolamp_led); 1252 1253 if (fujitsu_hotkey->kblamps_registered != 0) 1254 led_classdev_unregister(&kblamps_led); 1255 #endif 1256 1257 sysfs_remove_group(&fujitsu->pf_device->dev.kobj, 1258 &fujitsupf_attribute_group); 1259 platform_device_unregister(fujitsu->pf_device); 1260 platform_driver_unregister(&fujitsupf_driver); 1261 if (fujitsu->bl_device) 1262 backlight_device_unregister(fujitsu->bl_device); 1263 1264 acpi_bus_unregister_driver(&acpi_fujitsu_driver); 1265 1266 kfree(fujitsu); 1267 1268 acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver); 1269 1270 kfree(fujitsu_hotkey); 1271 1272 printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n"); 1273 } 1274 1275 module_init(fujitsu_init); 1276 module_exit(fujitsu_cleanup); 1277 1278 module_param(use_alt_lcd_levels, uint, 0644); 1279 MODULE_PARM_DESC(use_alt_lcd_levels, 1280 "Use alternative interface for lcd_levels (needed for Lifebook s6410)."); 1281 module_param(disable_brightness_adjust, uint, 0644); 1282 MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment ."); 1283 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG 1284 module_param_named(debug, dbg_level, uint, 0644); 1285 MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); 1286 #endif 1287 1288 MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon"); 1289 MODULE_DESCRIPTION("Fujitsu laptop extras support"); 1290 MODULE_VERSION(FUJITSU_DRIVER_VERSION); 1291 MODULE_LICENSE("GPL"); 1292 1293 MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*"); 1294 MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*"); 1295 MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*"); 1296 1297 static struct pnp_device_id pnp_ids[] = { 1298 {.id = "FUJ02bf"}, 1299 {.id = "FUJ02B1"}, 1300 {.id = "FUJ02E3"}, 1301 {.id = ""} 1302 }; 1303 1304 MODULE_DEVICE_TABLE(pnp, pnp_ids); 1305