Advertisement
Guest User

Untitled

a guest
Jan 28th, 2013
122
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.47 KB | None | 0 0
  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/power_supply.h>
  25. #include <linux/acpi.h>
  26. #include <linux/mm.h>
  27. #include <linux/i8042.h>
  28. #include <linux/slab.h>
  29. #include <linux/debugfs.h>
  30. #include <linux/seq_file.h>
  31. #include "../../firmware/dcdbas.h"
  32.  
  33. #define BRIGHTNESS_TOKEN 0x7d
  34.  
  35. /* This structure will be modified by the firmware when we enter
  36. * system management mode, hence the volatiles */
  37.  
  38. struct calling_interface_buffer {
  39. u16 class;
  40. u16 select;
  41. volatile u32 input[4];
  42. volatile u32 output[4];
  43. } __packed;
  44.  
  45. struct calling_interface_token {
  46. u16 tokenID;
  47. u16 location;
  48. union {
  49. u16 value;
  50. u16 stringlength;
  51. };
  52. };
  53.  
  54. struct calling_interface_structure {
  55. struct dmi_header header;
  56. u16 cmdIOAddress;
  57. u8 cmdIOCode;
  58. u32 supportedCmds;
  59. struct calling_interface_token tokens[];
  60. } __packed;
  61.  
  62. struct quirk_entry {
  63. u8 touchpad_led;
  64. };
  65.  
  66. static struct quirk_entry *quirks;
  67.  
  68. static struct quirk_entry quirk_dell_vostro_v130 = {
  69. .touchpad_led = 1,
  70. };
  71.  
  72. static int dmi_matched(const struct dmi_system_id *dmi)
  73. {
  74. quirks = dmi->driver_data;
  75. return 1;
  76. }
  77.  
  78. static int da_command_address;
  79. static int da_command_code;
  80. static int da_num_tokens;
  81. static struct calling_interface_token *da_tokens;
  82.  
  83. static struct platform_driver platform_driver = {
  84. .driver = {
  85. .name = "dell-laptop",
  86. .owner = THIS_MODULE,
  87. }
  88. };
  89.  
  90. static struct platform_device *platform_device;
  91. static struct backlight_device *dell_backlight_device;
  92.  
  93. static const struct dmi_system_id dell_device_table[] __initconst = {
  94. {
  95. .ident = "Dell laptop",
  96. .matches = {
  97. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  98. DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
  99. },
  100. },
  101. {
  102. .matches = {
  103. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  104. DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
  105. },
  106. },
  107. {
  108. .ident = "Dell Computer Corporation",
  109. .matches = {
  110. DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
  111. DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
  112. },
  113. },
  114. { }
  115. };
  116. MODULE_DEVICE_TABLE(dmi, dell_device_table);
  117.  
  118. static struct dmi_system_id dell_quirks[] = {
  119. {
  120. .callback = dmi_matched,
  121. .ident = "Dell Vostro V130",
  122. .matches = {
  123. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  124. DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"),
  125. },
  126. .driver_data = &quirk_dell_vostro_v130,
  127. },
  128. {
  129. .callback = dmi_matched,
  130. .ident = "Dell Vostro V131",
  131. .matches = {
  132. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  133. DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
  134. },
  135. .driver_data = &quirk_dell_vostro_v130,
  136. },
  137. {
  138. .callback = dmi_matched,
  139. .ident = "Dell Vostro 3350",
  140. .matches = {
  141. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  142. DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3350"),
  143. },
  144. .driver_data = &quirk_dell_vostro_v130,
  145. },
  146. {
  147. .callback = dmi_matched,
  148. .ident = "Dell Vostro 3555",
  149. .matches = {
  150. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  151. DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"),
  152. },
  153. .driver_data = &quirk_dell_vostro_v130,
  154. },
  155. {
  156. .callback = dmi_matched,
  157. .ident = "Dell Inspiron N311z",
  158. .matches = {
  159. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  160. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"),
  161. },
  162. .driver_data = &quirk_dell_vostro_v130,
  163. },
  164. {
  165. .callback = dmi_matched,
  166. .ident = "Dell Inspiron M5110",
  167. .matches = {
  168. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  169. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"),
  170. },
  171. .driver_data = &quirk_dell_vostro_v130,
  172. },
  173. {
  174. .callback = dmi_matched,
  175. .ident = "Dell Vostro 3360",
  176. .matches = {
  177. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  178. DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"),
  179. },
  180. .driver_data = &quirk_dell_vostro_v130,
  181. },
  182. {
  183. .callback = dmi_matched,
  184. .ident = "Dell Vostro 3460",
  185. .matches = {
  186. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  187. DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3460"),
  188. },
  189. .driver_data = &quirk_dell_vostro_v130,
  190. },
  191. {
  192. .callback = dmi_matched,
  193. .ident = "Dell Vostro 3560",
  194. .matches = {
  195. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  196. DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3560"),
  197. },
  198. .driver_data = &quirk_dell_vostro_v130,
  199. },
  200. {
  201. .callback = dmi_matched,
  202. .ident = "Dell Vostro 3450",
  203. .matches = {
  204. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  205. DMI_MATCH(DMI_PRODUCT_NAME, "Dell System Vostro 3450"),
  206. },
  207. .driver_data = &quirk_dell_vostro_v130,
  208. },
  209. {
  210. .callback = dmi_matched,
  211. .ident = "Dell Inspiron 5420",
  212. .matches = {
  213. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  214. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5420"),
  215. },
  216. .driver_data = &quirk_dell_vostro_v130,
  217. },
  218. {
  219. .callback = dmi_matched,
  220. .ident = "Dell Inspiron 5520",
  221. .matches = {
  222. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  223. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5520"),
  224. },
  225. .driver_data = &quirk_dell_vostro_v130,
  226. },
  227. {
  228. .callback = dmi_matched,
  229. .ident = "Dell Inspiron 5720",
  230. .matches = {
  231. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  232. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5720"),
  233. },
  234. .driver_data = &quirk_dell_vostro_v130,
  235. },
  236. {
  237. .callback = dmi_matched,
  238. .ident = "Dell Inspiron 7420",
  239. .matches = {
  240. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  241. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7420"),
  242. },
  243. .driver_data = &quirk_dell_vostro_v130,
  244. },
  245. {
  246. .callback = dmi_matched,
  247. .ident = "Dell Inspiron 7520",
  248. .matches = {
  249. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  250. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7520"),
  251. },
  252. .driver_data = &quirk_dell_vostro_v130,
  253. },
  254. {
  255. .callback = dmi_matched,
  256. .ident = "Dell Inspiron 7720",
  257. .matches = {
  258. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  259. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"),
  260. },
  261. .driver_data = &quirk_dell_vostro_v130,
  262. },
  263. { }
  264. };
  265.  
  266. static struct calling_interface_buffer *buffer;
  267. static struct page *bufferpage;
  268. static DEFINE_MUTEX(buffer_mutex);
  269.  
  270. static int hwswitch_state;
  271.  
  272. static void get_buffer(void)
  273. {
  274. mutex_lock(&buffer_mutex);
  275. memset(buffer, 0, sizeof(struct calling_interface_buffer));
  276. }
  277.  
  278. static void release_buffer(void)
  279. {
  280. mutex_unlock(&buffer_mutex);
  281. }
  282.  
  283. static void __init parse_da_table(const struct dmi_header *dm)
  284. {
  285. /* Final token is a terminator, so we don't want to copy it */
  286. int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
  287. struct calling_interface_structure *table =
  288. container_of(dm, struct calling_interface_structure, header);
  289.  
  290. /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
  291. 6 bytes of entry */
  292.  
  293. if (dm->length < 17)
  294. return;
  295.  
  296. da_command_address = table->cmdIOAddress;
  297. da_command_code = table->cmdIOCode;
  298.  
  299. da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
  300. sizeof(struct calling_interface_token),
  301. GFP_KERNEL);
  302.  
  303. if (!da_tokens)
  304. return;
  305.  
  306. memcpy(da_tokens+da_num_tokens, table->tokens,
  307. sizeof(struct calling_interface_token) * tokens);
  308.  
  309. da_num_tokens += tokens;
  310. }
  311.  
  312. static void __init find_tokens(const struct dmi_header *dm, void *dummy)
  313. {
  314. switch (dm->type) {
  315. case 0xd4: /* Indexed IO */
  316. case 0xd5: /* Protected Area Type 1 */
  317. case 0xd6: /* Protected Area Type 2 */
  318. break;
  319. case 0xda: /* Calling interface */
  320. parse_da_table(dm);
  321. break;
  322. }
  323. }
  324.  
  325. static int find_token_location(int tokenid)
  326. {
  327. int i;
  328. for (i = 0; i < da_num_tokens; i++) {
  329. if (da_tokens[i].tokenID == tokenid)
  330. return da_tokens[i].location;
  331. }
  332.  
  333. return -1;
  334. }
  335.  
  336. static struct calling_interface_buffer *
  337. dell_send_request(struct calling_interface_buffer *buffer, int class,
  338. int select)
  339. {
  340. struct smi_cmd command;
  341.  
  342. command.magic = SMI_CMD_MAGIC;
  343. command.command_address = da_command_address;
  344. command.command_code = da_command_code;
  345. command.ebx = virt_to_phys(buffer);
  346. command.ecx = 0x42534931;
  347.  
  348. buffer->class = class;
  349. buffer->select = select;
  350.  
  351. dcdbas_smi_request(&command);
  352.  
  353. return buffer;
  354. }
  355.  
  356. static struct dentry *dell_laptop_dir;
  357.  
  358. static int dell_debugfs_show(struct seq_file *s, void *data)
  359. {
  360. int status;
  361.  
  362. get_buffer();
  363. dell_send_request(buffer, 17, 11);
  364. status = buffer->output[1];
  365. release_buffer();
  366.  
  367. seq_printf(s, "status:\t0x%X\n", status);
  368. seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n",
  369. status & BIT(0));
  370. seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n",
  371. (status & BIT(1)) >> 1);
  372. seq_printf(s, "Bit 2 : Wifi is supported: %lu\n",
  373. (status & BIT(2)) >> 2);
  374. seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n",
  375. (status & BIT(3)) >> 3);
  376. seq_printf(s, "Bit 4 : WWAN is supported: %lu\n",
  377. (status & BIT(4)) >> 4);
  378. seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n",
  379. (status & BIT(5)) >> 5);
  380. seq_printf(s, "Bit 8 : Wifi is installed: %lu\n",
  381. (status & BIT(8)) >> 8);
  382. seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n",
  383. (status & BIT(9)) >> 9);
  384. seq_printf(s, "Bit 10: WWAN is installed: %lu\n",
  385. (status & BIT(10)) >> 10);
  386. seq_printf(s, "Bit 16: Hardware switch is on: %lu\n",
  387. (status & BIT(16)) >> 16);
  388. seq_printf(s, "Bit 17: Wifi is blocked: %lu\n",
  389. (status & BIT(17)) >> 17);
  390. seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n",
  391. (status & BIT(18)) >> 18);
  392. seq_printf(s, "Bit 19: WWAN is blocked: %lu\n",
  393. (status & BIT(19)) >> 19);
  394.  
  395. seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state);
  396. seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n",
  397. hwswitch_state & BIT(0));
  398. seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n",
  399. (hwswitch_state & BIT(1)) >> 1);
  400. seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n",
  401. (hwswitch_state & BIT(2)) >> 2);
  402. seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n",
  403. (hwswitch_state & BIT(7)) >> 7);
  404. seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n",
  405. (hwswitch_state & BIT(8)) >> 8);
  406. seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n",
  407. (hwswitch_state & BIT(15)) >> 15);
  408.  
  409. return 0;
  410. }
  411.  
  412. static int dell_debugfs_open(struct inode *inode, struct file *file)
  413. {
  414. return single_open(file, dell_debugfs_show, inode->i_private);
  415. }
  416.  
  417. static const struct file_operations dell_debugfs_fops = {
  418. .owner = THIS_MODULE,
  419. .open = dell_debugfs_open,
  420. .read = seq_read,
  421. .llseek = seq_lseek,
  422. .release = single_release,
  423. };
  424.  
  425. static int dell_send_intensity(struct backlight_device *bd)
  426. {
  427. int ret = 0;
  428.  
  429. get_buffer();
  430. buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
  431. buffer->input[1] = bd->props.brightness;
  432.  
  433. if (buffer->input[0] == -1) {
  434. ret = -ENODEV;
  435. goto out;
  436. }
  437.  
  438. if (power_supply_is_system_supplied() > 0)
  439. dell_send_request(buffer, 1, 2);
  440. else
  441. dell_send_request(buffer, 1, 1);
  442.  
  443. out:
  444. release_buffer();
  445. return 0;
  446. }
  447.  
  448. static int dell_get_intensity(struct backlight_device *bd)
  449. {
  450. int ret = 0;
  451.  
  452. get_buffer();
  453. buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
  454.  
  455. if (buffer->input[0] == -1) {
  456. ret = -ENODEV;
  457. goto out;
  458. }
  459.  
  460. if (power_supply_is_system_supplied() > 0)
  461. dell_send_request(buffer, 0, 2);
  462. else
  463. dell_send_request(buffer, 0, 1);
  464.  
  465. ret = buffer->output[1];
  466.  
  467. out:
  468. release_buffer();
  469. return ret;
  470. }
  471.  
  472. static const struct backlight_ops dell_ops = {
  473. .get_brightness = dell_get_intensity,
  474. .update_status = dell_send_intensity,
  475. };
  476.  
  477. static void touchpad_led_on(void)
  478. {
  479. int command = 0x97;
  480. char data = 1;
  481. i8042_command(&data, command | 1 << 12);
  482. }
  483.  
  484. static void touchpad_led_off(void)
  485. {
  486. int command = 0x97;
  487. char data = 2;
  488. i8042_command(&data, command | 1 << 12);
  489. }
  490.  
  491. static void touchpad_led_set(struct led_classdev *led_cdev,
  492. enum led_brightness value)
  493. {
  494. if (value > 0)
  495. touchpad_led_on();
  496. else
  497. touchpad_led_off();
  498. }
  499.  
  500. static struct led_classdev touchpad_led = {
  501. .name = "dell-laptop::touchpad",
  502. .brightness_set = touchpad_led_set,
  503. .flags = LED_CORE_SUSPENDRESUME,
  504. };
  505.  
  506. static int touchpad_led_init(struct device *dev)
  507. {
  508. return led_classdev_register(dev, &touchpad_led);
  509. }
  510.  
  511. static void touchpad_led_exit(void)
  512. {
  513. led_classdev_unregister(&touchpad_led);
  514. }
  515.  
  516. static int __init dell_init(void)
  517. {
  518. int max_intensity = 0;
  519. int ret;
  520.  
  521. if (!dmi_check_system(dell_device_table))
  522. return -ENODEV;
  523.  
  524. quirks = NULL;
  525. /* find if this machine support other functions */
  526. dmi_check_system(dell_quirks);
  527.  
  528. dmi_walk(find_tokens, NULL);
  529.  
  530. if (!da_tokens) {
  531. pr_info("Unable to find dmi tokens\n");
  532. return -ENODEV;
  533. }
  534.  
  535. ret = platform_driver_register(&platform_driver);
  536. if (ret)
  537. goto fail_platform_driver;
  538. platform_device = platform_device_alloc("dell-laptop", -1);
  539. if (!platform_device) {
  540. ret = -ENOMEM;
  541. goto fail_platform_device1;
  542. }
  543. ret = platform_device_add(platform_device);
  544. if (ret)
  545. goto fail_platform_device2;
  546.  
  547. /*
  548. * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
  549. * is passed to SMI handler.
  550. */
  551. bufferpage = alloc_page(GFP_KERNEL | GFP_DMA32);
  552.  
  553. if (!bufferpage)
  554. goto fail_buffer;
  555. buffer = page_address(bufferpage);
  556.  
  557. if (quirks && quirks->touchpad_led)
  558. touchpad_led_init(&platform_device->dev);
  559.  
  560. dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
  561.  
  562. #ifdef CONFIG_ACPI
  563. /* In the event of an ACPI backlight being available, don't
  564. * register the platform controller.
  565. */
  566. if (acpi_video_backlight_support())
  567. return 0;
  568. #endif
  569.  
  570. get_buffer();
  571. buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
  572. if (buffer->input[0] != -1) {
  573. dell_send_request(buffer, 0, 2);
  574. max_intensity = buffer->output[3];
  575. }
  576. release_buffer();
  577.  
  578. if (max_intensity) {
  579. struct backlight_properties props;
  580. memset(&props, 0, sizeof(struct backlight_properties));
  581. props.type = BACKLIGHT_PLATFORM;
  582. props.max_brightness = max_intensity;
  583. dell_backlight_device = backlight_device_register("dell_backlight",
  584. &platform_device->dev,
  585. NULL,
  586. &dell_ops,
  587. &props);
  588.  
  589. if (IS_ERR(dell_backlight_device)) {
  590. ret = PTR_ERR(dell_backlight_device);
  591. dell_backlight_device = NULL;
  592. goto fail_backlight;
  593. }
  594.  
  595. dell_backlight_device->props.brightness =
  596. dell_get_intensity(dell_backlight_device);
  597. backlight_update_status(dell_backlight_device);
  598. }
  599.  
  600. return 0;
  601.  
  602. fail_backlight:
  603. free_page((unsigned long)bufferpage);
  604. fail_buffer:
  605. platform_device_del(platform_device);
  606. fail_platform_device2:
  607. platform_device_put(platform_device);
  608. fail_platform_device1:
  609. platform_driver_unregister(&platform_driver);
  610. fail_platform_driver:
  611. kfree(da_tokens);
  612. return ret;
  613. }
  614.  
  615. static void __exit dell_exit(void)
  616. {
  617. debugfs_remove_recursive(dell_laptop_dir);
  618. if (quirks && quirks->touchpad_led)
  619. touchpad_led_exit();
  620. backlight_device_unregister(dell_backlight_device);
  621. if (platform_device) {
  622. platform_device_unregister(platform_device);
  623. platform_driver_unregister(&platform_driver);
  624. }
  625. kfree(da_tokens);
  626. free_page((unsigned long)buffer);
  627. }
  628.  
  629. module_init(dell_init);
  630. module_exit(dell_exit);
  631.  
  632. MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
  633. MODULE_DESCRIPTION("Dell laptop driver");
  634. MODULE_LICENSE("GPL");
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement