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 v3.1-rc2 715 lines 17 kB view raw
1/* 2 * Driver for Dell laptop extras 3 * 4 * Copyright (c) Red Hat <mjg@redhat.com> 5 * 6 * Based on documentation in the libsmbios package, Copyright (C) 2005 Dell 7 * Inc. 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13 14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 15 16#include <linux/module.h> 17#include <linux/kernel.h> 18#include <linux/init.h> 19#include <linux/platform_device.h> 20#include <linux/backlight.h> 21#include <linux/err.h> 22#include <linux/dmi.h> 23#include <linux/io.h> 24#include <linux/rfkill.h> 25#include <linux/power_supply.h> 26#include <linux/acpi.h> 27#include <linux/mm.h> 28#include <linux/i8042.h> 29#include <linux/slab.h> 30#include <linux/debugfs.h> 31#include <linux/seq_file.h> 32#include "../../firmware/dcdbas.h" 33 34#define BRIGHTNESS_TOKEN 0x7d 35 36/* This structure will be modified by the firmware when we enter 37 * system management mode, hence the volatiles */ 38 39struct calling_interface_buffer { 40 u16 class; 41 u16 select; 42 volatile u32 input[4]; 43 volatile u32 output[4]; 44} __packed; 45 46struct calling_interface_token { 47 u16 tokenID; 48 u16 location; 49 union { 50 u16 value; 51 u16 stringlength; 52 }; 53}; 54 55struct calling_interface_structure { 56 struct dmi_header header; 57 u16 cmdIOAddress; 58 u8 cmdIOCode; 59 u32 supportedCmds; 60 struct calling_interface_token tokens[]; 61} __packed; 62 63static int da_command_address; 64static int da_command_code; 65static int da_num_tokens; 66static struct calling_interface_token *da_tokens; 67 68static struct platform_driver platform_driver = { 69 .driver = { 70 .name = "dell-laptop", 71 .owner = THIS_MODULE, 72 } 73}; 74 75static struct platform_device *platform_device; 76static struct backlight_device *dell_backlight_device; 77static struct rfkill *wifi_rfkill; 78static struct rfkill *bluetooth_rfkill; 79static struct rfkill *wwan_rfkill; 80 81static const struct dmi_system_id __initdata dell_device_table[] = { 82 { 83 .ident = "Dell laptop", 84 .matches = { 85 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 86 DMI_MATCH(DMI_CHASSIS_TYPE, "8"), 87 }, 88 }, 89 { 90 .matches = { 91 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 92 DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/ 93 }, 94 }, 95 { 96 .ident = "Dell Computer Corporation", 97 .matches = { 98 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), 99 DMI_MATCH(DMI_CHASSIS_TYPE, "8"), 100 }, 101 }, 102 { } 103}; 104 105static struct dmi_system_id __devinitdata dell_blacklist[] = { 106 /* Supported by compal-laptop */ 107 { 108 .ident = "Dell Mini 9", 109 .matches = { 110 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 111 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"), 112 }, 113 }, 114 { 115 .ident = "Dell Mini 10", 116 .matches = { 117 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 118 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"), 119 }, 120 }, 121 { 122 .ident = "Dell Mini 10v", 123 .matches = { 124 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 125 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"), 126 }, 127 }, 128 { 129 .ident = "Dell Mini 1012", 130 .matches = { 131 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 132 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"), 133 }, 134 }, 135 { 136 .ident = "Dell Inspiron 11z", 137 .matches = { 138 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 139 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"), 140 }, 141 }, 142 { 143 .ident = "Dell Mini 12", 144 .matches = { 145 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 146 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"), 147 }, 148 }, 149 {} 150}; 151 152static struct calling_interface_buffer *buffer; 153static struct page *bufferpage; 154static DEFINE_MUTEX(buffer_mutex); 155 156static int hwswitch_state; 157 158static void get_buffer(void) 159{ 160 mutex_lock(&buffer_mutex); 161 memset(buffer, 0, sizeof(struct calling_interface_buffer)); 162} 163 164static void release_buffer(void) 165{ 166 mutex_unlock(&buffer_mutex); 167} 168 169static void __init parse_da_table(const struct dmi_header *dm) 170{ 171 /* Final token is a terminator, so we don't want to copy it */ 172 int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1; 173 struct calling_interface_structure *table = 174 container_of(dm, struct calling_interface_structure, header); 175 176 /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least 177 6 bytes of entry */ 178 179 if (dm->length < 17) 180 return; 181 182 da_command_address = table->cmdIOAddress; 183 da_command_code = table->cmdIOCode; 184 185 da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) * 186 sizeof(struct calling_interface_token), 187 GFP_KERNEL); 188 189 if (!da_tokens) 190 return; 191 192 memcpy(da_tokens+da_num_tokens, table->tokens, 193 sizeof(struct calling_interface_token) * tokens); 194 195 da_num_tokens += tokens; 196} 197 198static void __init find_tokens(const struct dmi_header *dm, void *dummy) 199{ 200 switch (dm->type) { 201 case 0xd4: /* Indexed IO */ 202 break; 203 case 0xd5: /* Protected Area Type 1 */ 204 break; 205 case 0xd6: /* Protected Area Type 2 */ 206 break; 207 case 0xda: /* Calling interface */ 208 parse_da_table(dm); 209 break; 210 } 211} 212 213static int find_token_location(int tokenid) 214{ 215 int i; 216 for (i = 0; i < da_num_tokens; i++) { 217 if (da_tokens[i].tokenID == tokenid) 218 return da_tokens[i].location; 219 } 220 221 return -1; 222} 223 224static struct calling_interface_buffer * 225dell_send_request(struct calling_interface_buffer *buffer, int class, 226 int select) 227{ 228 struct smi_cmd command; 229 230 command.magic = SMI_CMD_MAGIC; 231 command.command_address = da_command_address; 232 command.command_code = da_command_code; 233 command.ebx = virt_to_phys(buffer); 234 command.ecx = 0x42534931; 235 236 buffer->class = class; 237 buffer->select = select; 238 239 dcdbas_smi_request(&command); 240 241 return buffer; 242} 243 244/* Derived from information in DellWirelessCtl.cpp: 245 Class 17, select 11 is radio control. It returns an array of 32-bit values. 246 247 Input byte 0 = 0: Wireless information 248 249 result[0]: return code 250 result[1]: 251 Bit 0: Hardware switch supported 252 Bit 1: Wifi locator supported 253 Bit 2: Wifi is supported 254 Bit 3: Bluetooth is supported 255 Bit 4: WWAN is supported 256 Bit 5: Wireless keyboard supported 257 Bits 6-7: Reserved 258 Bit 8: Wifi is installed 259 Bit 9: Bluetooth is installed 260 Bit 10: WWAN is installed 261 Bits 11-15: Reserved 262 Bit 16: Hardware switch is on 263 Bit 17: Wifi is blocked 264 Bit 18: Bluetooth is blocked 265 Bit 19: WWAN is blocked 266 Bits 20-31: Reserved 267 result[2]: NVRAM size in bytes 268 result[3]: NVRAM format version number 269 270 Input byte 0 = 2: Wireless switch configuration 271 result[0]: return code 272 result[1]: 273 Bit 0: Wifi controlled by switch 274 Bit 1: Bluetooth controlled by switch 275 Bit 2: WWAN controlled by switch 276 Bits 3-6: Reserved 277 Bit 7: Wireless switch config locked 278 Bit 8: Wifi locator enabled 279 Bits 9-14: Reserved 280 Bit 15: Wifi locator setting locked 281 Bits 16-31: Reserved 282*/ 283 284static int dell_rfkill_set(void *data, bool blocked) 285{ 286 int disable = blocked ? 1 : 0; 287 unsigned long radio = (unsigned long)data; 288 int hwswitch_bit = (unsigned long)data - 1; 289 int ret = 0; 290 291 get_buffer(); 292 dell_send_request(buffer, 17, 11); 293 294 /* If the hardware switch controls this radio, and the hardware 295 switch is disabled, don't allow changing the software state */ 296 if ((hwswitch_state & BIT(hwswitch_bit)) && 297 !(buffer->output[1] & BIT(16))) { 298 ret = -EINVAL; 299 goto out; 300 } 301 302 buffer->input[0] = (1 | (radio<<8) | (disable << 16)); 303 dell_send_request(buffer, 17, 11); 304 305out: 306 release_buffer(); 307 return ret; 308} 309 310static void dell_rfkill_query(struct rfkill *rfkill, void *data) 311{ 312 int status; 313 int bit = (unsigned long)data + 16; 314 int hwswitch_bit = (unsigned long)data - 1; 315 316 get_buffer(); 317 dell_send_request(buffer, 17, 11); 318 status = buffer->output[1]; 319 release_buffer(); 320 321 rfkill_set_sw_state(rfkill, !!(status & BIT(bit))); 322 323 if (hwswitch_state & (BIT(hwswitch_bit))) 324 rfkill_set_hw_state(rfkill, !(status & BIT(16))); 325} 326 327static const struct rfkill_ops dell_rfkill_ops = { 328 .set_block = dell_rfkill_set, 329 .query = dell_rfkill_query, 330}; 331 332static struct dentry *dell_laptop_dir; 333 334static int dell_debugfs_show(struct seq_file *s, void *data) 335{ 336 int status; 337 338 get_buffer(); 339 dell_send_request(buffer, 17, 11); 340 status = buffer->output[1]; 341 release_buffer(); 342 343 seq_printf(s, "status:\t0x%X\n", status); 344 seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n", 345 status & BIT(0)); 346 seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n", 347 (status & BIT(1)) >> 1); 348 seq_printf(s, "Bit 2 : Wifi is supported: %lu\n", 349 (status & BIT(2)) >> 2); 350 seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n", 351 (status & BIT(3)) >> 3); 352 seq_printf(s, "Bit 4 : WWAN is supported: %lu\n", 353 (status & BIT(4)) >> 4); 354 seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n", 355 (status & BIT(5)) >> 5); 356 seq_printf(s, "Bit 8 : Wifi is installed: %lu\n", 357 (status & BIT(8)) >> 8); 358 seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n", 359 (status & BIT(9)) >> 9); 360 seq_printf(s, "Bit 10: WWAN is installed: %lu\n", 361 (status & BIT(10)) >> 10); 362 seq_printf(s, "Bit 16: Hardware switch is on: %lu\n", 363 (status & BIT(16)) >> 16); 364 seq_printf(s, "Bit 17: Wifi is blocked: %lu\n", 365 (status & BIT(17)) >> 17); 366 seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n", 367 (status & BIT(18)) >> 18); 368 seq_printf(s, "Bit 19: WWAN is blocked: %lu\n", 369 (status & BIT(19)) >> 19); 370 371 seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state); 372 seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n", 373 hwswitch_state & BIT(0)); 374 seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n", 375 (hwswitch_state & BIT(1)) >> 1); 376 seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n", 377 (hwswitch_state & BIT(2)) >> 2); 378 seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n", 379 (hwswitch_state & BIT(7)) >> 7); 380 seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n", 381 (hwswitch_state & BIT(8)) >> 8); 382 seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n", 383 (hwswitch_state & BIT(15)) >> 15); 384 385 return 0; 386} 387 388static int dell_debugfs_open(struct inode *inode, struct file *file) 389{ 390 return single_open(file, dell_debugfs_show, inode->i_private); 391} 392 393static const struct file_operations dell_debugfs_fops = { 394 .owner = THIS_MODULE, 395 .open = dell_debugfs_open, 396 .read = seq_read, 397 .llseek = seq_lseek, 398 .release = single_release, 399}; 400 401static void dell_update_rfkill(struct work_struct *ignored) 402{ 403 if (wifi_rfkill) 404 dell_rfkill_query(wifi_rfkill, (void *)1); 405 if (bluetooth_rfkill) 406 dell_rfkill_query(bluetooth_rfkill, (void *)2); 407 if (wwan_rfkill) 408 dell_rfkill_query(wwan_rfkill, (void *)3); 409} 410static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); 411 412 413static int __init dell_setup_rfkill(void) 414{ 415 int status; 416 int ret; 417 418 if (dmi_check_system(dell_blacklist)) { 419 pr_info("Blacklisted hardware detected - not enabling rfkill\n"); 420 return 0; 421 } 422 423 get_buffer(); 424 dell_send_request(buffer, 17, 11); 425 status = buffer->output[1]; 426 buffer->input[0] = 0x2; 427 dell_send_request(buffer, 17, 11); 428 hwswitch_state = buffer->output[1]; 429 release_buffer(); 430 431 if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { 432 wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev, 433 RFKILL_TYPE_WLAN, 434 &dell_rfkill_ops, (void *) 1); 435 if (!wifi_rfkill) { 436 ret = -ENOMEM; 437 goto err_wifi; 438 } 439 ret = rfkill_register(wifi_rfkill); 440 if (ret) 441 goto err_wifi; 442 } 443 444 if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) { 445 bluetooth_rfkill = rfkill_alloc("dell-bluetooth", 446 &platform_device->dev, 447 RFKILL_TYPE_BLUETOOTH, 448 &dell_rfkill_ops, (void *) 2); 449 if (!bluetooth_rfkill) { 450 ret = -ENOMEM; 451 goto err_bluetooth; 452 } 453 ret = rfkill_register(bluetooth_rfkill); 454 if (ret) 455 goto err_bluetooth; 456 } 457 458 if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) { 459 wwan_rfkill = rfkill_alloc("dell-wwan", 460 &platform_device->dev, 461 RFKILL_TYPE_WWAN, 462 &dell_rfkill_ops, (void *) 3); 463 if (!wwan_rfkill) { 464 ret = -ENOMEM; 465 goto err_wwan; 466 } 467 ret = rfkill_register(wwan_rfkill); 468 if (ret) 469 goto err_wwan; 470 } 471 472 return 0; 473err_wwan: 474 rfkill_destroy(wwan_rfkill); 475 if (bluetooth_rfkill) 476 rfkill_unregister(bluetooth_rfkill); 477err_bluetooth: 478 rfkill_destroy(bluetooth_rfkill); 479 if (wifi_rfkill) 480 rfkill_unregister(wifi_rfkill); 481err_wifi: 482 rfkill_destroy(wifi_rfkill); 483 484 return ret; 485} 486 487static void dell_cleanup_rfkill(void) 488{ 489 if (wifi_rfkill) { 490 rfkill_unregister(wifi_rfkill); 491 rfkill_destroy(wifi_rfkill); 492 } 493 if (bluetooth_rfkill) { 494 rfkill_unregister(bluetooth_rfkill); 495 rfkill_destroy(bluetooth_rfkill); 496 } 497 if (wwan_rfkill) { 498 rfkill_unregister(wwan_rfkill); 499 rfkill_destroy(wwan_rfkill); 500 } 501} 502 503static int dell_send_intensity(struct backlight_device *bd) 504{ 505 int ret = 0; 506 507 get_buffer(); 508 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); 509 buffer->input[1] = bd->props.brightness; 510 511 if (buffer->input[0] == -1) { 512 ret = -ENODEV; 513 goto out; 514 } 515 516 if (power_supply_is_system_supplied() > 0) 517 dell_send_request(buffer, 1, 2); 518 else 519 dell_send_request(buffer, 1, 1); 520 521out: 522 release_buffer(); 523 return 0; 524} 525 526static int dell_get_intensity(struct backlight_device *bd) 527{ 528 int ret = 0; 529 530 get_buffer(); 531 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); 532 533 if (buffer->input[0] == -1) { 534 ret = -ENODEV; 535 goto out; 536 } 537 538 if (power_supply_is_system_supplied() > 0) 539 dell_send_request(buffer, 0, 2); 540 else 541 dell_send_request(buffer, 0, 1); 542 543 ret = buffer->output[1]; 544 545out: 546 release_buffer(); 547 return ret; 548} 549 550static const struct backlight_ops dell_ops = { 551 .get_brightness = dell_get_intensity, 552 .update_status = dell_send_intensity, 553}; 554 555static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, 556 struct serio *port) 557{ 558 static bool extended; 559 560 if (str & 0x20) 561 return false; 562 563 if (unlikely(data == 0xe0)) { 564 extended = true; 565 return false; 566 } else if (unlikely(extended)) { 567 switch (data) { 568 case 0x8: 569 schedule_delayed_work(&dell_rfkill_work, 570 round_jiffies_relative(HZ)); 571 break; 572 } 573 extended = false; 574 } 575 576 return false; 577} 578 579static int __init dell_init(void) 580{ 581 int max_intensity = 0; 582 int ret; 583 584 if (!dmi_check_system(dell_device_table)) 585 return -ENODEV; 586 587 dmi_walk(find_tokens, NULL); 588 589 if (!da_tokens) { 590 pr_info("Unable to find dmi tokens\n"); 591 return -ENODEV; 592 } 593 594 ret = platform_driver_register(&platform_driver); 595 if (ret) 596 goto fail_platform_driver; 597 platform_device = platform_device_alloc("dell-laptop", -1); 598 if (!platform_device) { 599 ret = -ENOMEM; 600 goto fail_platform_device1; 601 } 602 ret = platform_device_add(platform_device); 603 if (ret) 604 goto fail_platform_device2; 605 606 /* 607 * Allocate buffer below 4GB for SMI data--only 32-bit physical addr 608 * is passed to SMI handler. 609 */ 610 bufferpage = alloc_page(GFP_KERNEL | GFP_DMA32); 611 612 if (!bufferpage) 613 goto fail_buffer; 614 buffer = page_address(bufferpage); 615 616 ret = dell_setup_rfkill(); 617 618 if (ret) { 619 pr_warn("Unable to setup rfkill\n"); 620 goto fail_rfkill; 621 } 622 623 ret = i8042_install_filter(dell_laptop_i8042_filter); 624 if (ret) { 625 pr_warn("Unable to install key filter\n"); 626 goto fail_filter; 627 } 628 629 dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); 630 if (dell_laptop_dir != NULL) 631 debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, 632 &dell_debugfs_fops); 633 634#ifdef CONFIG_ACPI 635 /* In the event of an ACPI backlight being available, don't 636 * register the platform controller. 637 */ 638 if (acpi_video_backlight_support()) 639 return 0; 640#endif 641 642 get_buffer(); 643 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); 644 if (buffer->input[0] != -1) { 645 dell_send_request(buffer, 0, 2); 646 max_intensity = buffer->output[3]; 647 } 648 release_buffer(); 649 650 if (max_intensity) { 651 struct backlight_properties props; 652 memset(&props, 0, sizeof(struct backlight_properties)); 653 props.type = BACKLIGHT_PLATFORM; 654 props.max_brightness = max_intensity; 655 dell_backlight_device = backlight_device_register("dell_backlight", 656 &platform_device->dev, 657 NULL, 658 &dell_ops, 659 &props); 660 661 if (IS_ERR(dell_backlight_device)) { 662 ret = PTR_ERR(dell_backlight_device); 663 dell_backlight_device = NULL; 664 goto fail_backlight; 665 } 666 667 dell_backlight_device->props.brightness = 668 dell_get_intensity(dell_backlight_device); 669 backlight_update_status(dell_backlight_device); 670 } 671 672 return 0; 673 674fail_backlight: 675 i8042_remove_filter(dell_laptop_i8042_filter); 676 cancel_delayed_work_sync(&dell_rfkill_work); 677fail_filter: 678 dell_cleanup_rfkill(); 679fail_rfkill: 680 free_page((unsigned long)bufferpage); 681fail_buffer: 682 platform_device_del(platform_device); 683fail_platform_device2: 684 platform_device_put(platform_device); 685fail_platform_device1: 686 platform_driver_unregister(&platform_driver); 687fail_platform_driver: 688 kfree(da_tokens); 689 return ret; 690} 691 692static void __exit dell_exit(void) 693{ 694 debugfs_remove_recursive(dell_laptop_dir); 695 i8042_remove_filter(dell_laptop_i8042_filter); 696 cancel_delayed_work_sync(&dell_rfkill_work); 697 backlight_device_unregister(dell_backlight_device); 698 dell_cleanup_rfkill(); 699 if (platform_device) { 700 platform_device_unregister(platform_device); 701 platform_driver_unregister(&platform_driver); 702 } 703 kfree(da_tokens); 704 free_page((unsigned long)buffer); 705} 706 707module_init(dell_init); 708module_exit(dell_exit); 709 710MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); 711MODULE_DESCRIPTION("Dell laptop driver"); 712MODULE_LICENSE("GPL"); 713MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*"); 714MODULE_ALIAS("dmi:*svnDellInc.:*:ct9:*"); 715MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*");