1
2
3
4
5
6
7
8
9
10
11
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
37
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
63struct quirk_entry {
64 u8 touchpad_led;
65};
66
67static struct quirk_entry *quirks;
68
69static struct quirk_entry quirk_dell_vostro_v130 = {
70 .touchpad_led = 1,
71};
72
73static int dmi_matched(const struct dmi_system_id *dmi)
74{
75 quirks = dmi->driver_data;
76 return 1;
77}
78
79static int da_command_address;
80static int da_command_code;
81static int da_num_tokens;
82static struct calling_interface_token *da_tokens;
83
84static struct platform_driver platform_driver = {
85 .driver = {
86 .name = "dell-laptop",
87 .owner = THIS_MODULE,
88 }
89};
90
91static struct platform_device *platform_device;
92static struct backlight_device *dell_backlight_device;
93static struct rfkill *wifi_rfkill;
94static struct rfkill *bluetooth_rfkill;
95static struct rfkill *wwan_rfkill;
96
97static const struct dmi_system_id __initdata dell_device_table[] = {
98 {
99 .ident = "Dell laptop",
100 .matches = {
101 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
102 DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
103 },
104 },
105 {
106 .matches = {
107 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
108 DMI_MATCH(DMI_CHASSIS_TYPE, "9"),
109 },
110 },
111 {
112 .ident = "Dell Computer Corporation",
113 .matches = {
114 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
115 DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
116 },
117 },
118 { }
119};
120
121static struct dmi_system_id __devinitdata dell_blacklist[] = {
122
123 {
124 .ident = "Dell Mini 9",
125 .matches = {
126 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
127 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
128 },
129 },
130 {
131 .ident = "Dell Mini 10",
132 .matches = {
133 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
134 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"),
135 },
136 },
137 {
138 .ident = "Dell Mini 10v",
139 .matches = {
140 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
141 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"),
142 },
143 },
144 {
145 .ident = "Dell Mini 1012",
146 .matches = {
147 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
148 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"),
149 },
150 },
151 {
152 .ident = "Dell Inspiron 11z",
153 .matches = {
154 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
155 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"),
156 },
157 },
158 {
159 .ident = "Dell Mini 12",
160 .matches = {
161 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
162 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"),
163 },
164 },
165 {}
166};
167
168static struct dmi_system_id __devinitdata dell_quirks[] = {
169 {
170 .callback = dmi_matched,
171 .ident = "Dell Vostro V130",
172 .matches = {
173 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
174 DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"),
175 },
176 .driver_data = &quirk_dell_vostro_v130,
177 },
178 {
179 .callback = dmi_matched,
180 .ident = "Dell Vostro V131",
181 .matches = {
182 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
183 DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
184 },
185 .driver_data = &quirk_dell_vostro_v130,
186 },
187};
188
189static struct calling_interface_buffer *buffer;
190static struct page *bufferpage;
191static DEFINE_MUTEX(buffer_mutex);
192
193static int hwswitch_state;
194
195static void get_buffer(void)
196{
197 mutex_lock(&buffer_mutex);
198 memset(buffer, 0, sizeof(struct calling_interface_buffer));
199}
200
201static void release_buffer(void)
202{
203 mutex_unlock(&buffer_mutex);
204}
205
206static void __init parse_da_table(const struct dmi_header *dm)
207{
208
209 int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
210 struct calling_interface_structure *table =
211 container_of(dm, struct calling_interface_structure, header);
212
213
214
215
216 if (dm->length < 17)
217 return;
218
219 da_command_address = table->cmdIOAddress;
220 da_command_code = table->cmdIOCode;
221
222 da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
223 sizeof(struct calling_interface_token),
224 GFP_KERNEL);
225
226 if (!da_tokens)
227 return;
228
229 memcpy(da_tokens+da_num_tokens, table->tokens,
230 sizeof(struct calling_interface_token) * tokens);
231
232 da_num_tokens += tokens;
233}
234
235static void __init find_tokens(const struct dmi_header *dm, void *dummy)
236{
237 switch (dm->type) {
238 case 0xd4:
239 break;
240 case 0xd5:
241 break;
242 case 0xd6:
243 break;
244 case 0xda:
245 parse_da_table(dm);
246 break;
247 }
248}
249
250static int find_token_location(int tokenid)
251{
252 int i;
253 for (i = 0; i < da_num_tokens; i++) {
254 if (da_tokens[i].tokenID == tokenid)
255 return da_tokens[i].location;
256 }
257
258 return -1;
259}
260
261static struct calling_interface_buffer *
262dell_send_request(struct calling_interface_buffer *buffer, int class,
263 int select)
264{
265 struct smi_cmd command;
266
267 command.magic = SMI_CMD_MAGIC;
268 command.command_address = da_command_address;
269 command.command_code = da_command_code;
270 command.ebx = virt_to_phys(buffer);
271 command.ecx = 0x42534931;
272
273 buffer->class = class;
274 buffer->select = select;
275
276 dcdbas_smi_request(&command);
277
278 return buffer;
279}
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321static int dell_rfkill_set(void *data, bool blocked)
322{
323 int disable = blocked ? 1 : 0;
324 unsigned long radio = (unsigned long)data;
325 int hwswitch_bit = (unsigned long)data - 1;
326 int ret = 0;
327
328 get_buffer();
329 dell_send_request(buffer, 17, 11);
330
331
332
333 if ((hwswitch_state & BIT(hwswitch_bit)) &&
334 !(buffer->output[1] & BIT(16))) {
335 ret = -EINVAL;
336 goto out;
337 }
338
339 buffer->input[0] = (1 | (radio<<8) | (disable << 16));
340 dell_send_request(buffer, 17, 11);
341
342out:
343 release_buffer();
344 return ret;
345}
346
347static void dell_rfkill_query(struct rfkill *rfkill, void *data)
348{
349 int status;
350 int bit = (unsigned long)data + 16;
351 int hwswitch_bit = (unsigned long)data - 1;
352
353 get_buffer();
354 dell_send_request(buffer, 17, 11);
355 status = buffer->output[1];
356 release_buffer();
357
358 rfkill_set_sw_state(rfkill, !!(status & BIT(bit)));
359
360 if (hwswitch_state & (BIT(hwswitch_bit)))
361 rfkill_set_hw_state(rfkill, !(status & BIT(16)));
362}
363
364static const struct rfkill_ops dell_rfkill_ops = {
365 .set_block = dell_rfkill_set,
366 .query = dell_rfkill_query,
367};
368
369static struct dentry *dell_laptop_dir;
370
371static int dell_debugfs_show(struct seq_file *s, void *data)
372{
373 int status;
374
375 get_buffer();
376 dell_send_request(buffer, 17, 11);
377 status = buffer->output[1];
378 release_buffer();
379
380 seq_printf(s, "status:\t0x%X\n", status);
381 seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n",
382 status & BIT(0));
383 seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n",
384 (status & BIT(1)) >> 1);
385 seq_printf(s, "Bit 2 : Wifi is supported: %lu\n",
386 (status & BIT(2)) >> 2);
387 seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n",
388 (status & BIT(3)) >> 3);
389 seq_printf(s, "Bit 4 : WWAN is supported: %lu\n",
390 (status & BIT(4)) >> 4);
391 seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n",
392 (status & BIT(5)) >> 5);
393 seq_printf(s, "Bit 8 : Wifi is installed: %lu\n",
394 (status & BIT(8)) >> 8);
395 seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n",
396 (status & BIT(9)) >> 9);
397 seq_printf(s, "Bit 10: WWAN is installed: %lu\n",
398 (status & BIT(10)) >> 10);
399 seq_printf(s, "Bit 16: Hardware switch is on: %lu\n",
400 (status & BIT(16)) >> 16);
401 seq_printf(s, "Bit 17: Wifi is blocked: %lu\n",
402 (status & BIT(17)) >> 17);
403 seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n",
404 (status & BIT(18)) >> 18);
405 seq_printf(s, "Bit 19: WWAN is blocked: %lu\n",
406 (status & BIT(19)) >> 19);
407
408 seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state);
409 seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n",
410 hwswitch_state & BIT(0));
411 seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n",
412 (hwswitch_state & BIT(1)) >> 1);
413 seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n",
414 (hwswitch_state & BIT(2)) >> 2);
415 seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n",
416 (hwswitch_state & BIT(7)) >> 7);
417 seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n",
418 (hwswitch_state & BIT(8)) >> 8);
419 seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n",
420 (hwswitch_state & BIT(15)) >> 15);
421
422 return 0;
423}
424
425static int dell_debugfs_open(struct inode *inode, struct file *file)
426{
427 return single_open(file, dell_debugfs_show, inode->i_private);
428}
429
430static const struct file_operations dell_debugfs_fops = {
431 .owner = THIS_MODULE,
432 .open = dell_debugfs_open,
433 .read = seq_read,
434 .llseek = seq_lseek,
435 .release = single_release,
436};
437
438static void dell_update_rfkill(struct work_struct *ignored)
439{
440 if (wifi_rfkill)
441 dell_rfkill_query(wifi_rfkill, (void *)1);
442 if (bluetooth_rfkill)
443 dell_rfkill_query(bluetooth_rfkill, (void *)2);
444 if (wwan_rfkill)
445 dell_rfkill_query(wwan_rfkill, (void *)3);
446}
447static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
448
449
450static int __init dell_setup_rfkill(void)
451{
452 int status;
453 int ret;
454
455 if (dmi_check_system(dell_blacklist)) {
456 pr_info("Blacklisted hardware detected - not enabling rfkill\n");
457 return 0;
458 }
459
460 get_buffer();
461 dell_send_request(buffer, 17, 11);
462 status = buffer->output[1];
463 buffer->input[0] = 0x2;
464 dell_send_request(buffer, 17, 11);
465 hwswitch_state = buffer->output[1];
466 release_buffer();
467
468 if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
469 wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
470 RFKILL_TYPE_WLAN,
471 &dell_rfkill_ops, (void *) 1);
472 if (!wifi_rfkill) {
473 ret = -ENOMEM;
474 goto err_wifi;
475 }
476 ret = rfkill_register(wifi_rfkill);
477 if (ret)
478 goto err_wifi;
479 }
480
481 if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
482 bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
483 &platform_device->dev,
484 RFKILL_TYPE_BLUETOOTH,
485 &dell_rfkill_ops, (void *) 2);
486 if (!bluetooth_rfkill) {
487 ret = -ENOMEM;
488 goto err_bluetooth;
489 }
490 ret = rfkill_register(bluetooth_rfkill);
491 if (ret)
492 goto err_bluetooth;
493 }
494
495 if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
496 wwan_rfkill = rfkill_alloc("dell-wwan",
497 &platform_device->dev,
498 RFKILL_TYPE_WWAN,
499 &dell_rfkill_ops, (void *) 3);
500 if (!wwan_rfkill) {
501 ret = -ENOMEM;
502 goto err_wwan;
503 }
504 ret = rfkill_register(wwan_rfkill);
505 if (ret)
506 goto err_wwan;
507 }
508
509 return 0;
510err_wwan:
511 rfkill_destroy(wwan_rfkill);
512 if (bluetooth_rfkill)
513 rfkill_unregister(bluetooth_rfkill);
514err_bluetooth:
515 rfkill_destroy(bluetooth_rfkill);
516 if (wifi_rfkill)
517 rfkill_unregister(wifi_rfkill);
518err_wifi:
519 rfkill_destroy(wifi_rfkill);
520
521 return ret;
522}
523
524static void dell_cleanup_rfkill(void)
525{
526 if (wifi_rfkill) {
527 rfkill_unregister(wifi_rfkill);
528 rfkill_destroy(wifi_rfkill);
529 }
530 if (bluetooth_rfkill) {
531 rfkill_unregister(bluetooth_rfkill);
532 rfkill_destroy(bluetooth_rfkill);
533 }
534 if (wwan_rfkill) {
535 rfkill_unregister(wwan_rfkill);
536 rfkill_destroy(wwan_rfkill);
537 }
538}
539
540static int dell_send_intensity(struct backlight_device *bd)
541{
542 int ret = 0;
543
544 get_buffer();
545 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
546 buffer->input[1] = bd->props.brightness;
547
548 if (buffer->input[0] == -1) {
549 ret = -ENODEV;
550 goto out;
551 }
552
553 if (power_supply_is_system_supplied() > 0)
554 dell_send_request(buffer, 1, 2);
555 else
556 dell_send_request(buffer, 1, 1);
557
558out:
559 release_buffer();
560 return 0;
561}
562
563static int dell_get_intensity(struct backlight_device *bd)
564{
565 int ret = 0;
566
567 get_buffer();
568 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
569
570 if (buffer->input[0] == -1) {
571 ret = -ENODEV;
572 goto out;
573 }
574
575 if (power_supply_is_system_supplied() > 0)
576 dell_send_request(buffer, 0, 2);
577 else
578 dell_send_request(buffer, 0, 1);
579
580 ret = buffer->output[1];
581
582out:
583 release_buffer();
584 return ret;
585}
586
587static const struct backlight_ops dell_ops = {
588 .get_brightness = dell_get_intensity,
589 .update_status = dell_send_intensity,
590};
591
592static void touchpad_led_on(void)
593{
594 int command = 0x97;
595 char data = 1;
596 i8042_command(&data, command | 1 << 12);
597}
598
599static void touchpad_led_off(void)
600{
601 int command = 0x97;
602 char data = 2;
603 i8042_command(&data, command | 1 << 12);
604}
605
606static void touchpad_led_set(struct led_classdev *led_cdev,
607 enum led_brightness value)
608{
609 if (value > 0)
610 touchpad_led_on();
611 else
612 touchpad_led_off();
613}
614
615static struct led_classdev touchpad_led = {
616 .name = "dell-laptop::touchpad",
617 .brightness_set = touchpad_led_set,
618};
619
620static int __devinit touchpad_led_init(struct device *dev)
621{
622 return led_classdev_register(dev, &touchpad_led);
623}
624
625static void touchpad_led_exit(void)
626{
627 led_classdev_unregister(&touchpad_led);
628}
629
630static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
631 struct serio *port)
632{
633 static bool extended;
634
635 if (str & 0x20)
636 return false;
637
638 if (unlikely(data == 0xe0)) {
639 extended = true;
640 return false;
641 } else if (unlikely(extended)) {
642 switch (data) {
643 case 0x8:
644 schedule_delayed_work(&dell_rfkill_work,
645 round_jiffies_relative(HZ));
646 break;
647 }
648 extended = false;
649 }
650
651 return false;
652}
653
654static int __init dell_init(void)
655{
656 int max_intensity = 0;
657 int ret;
658
659 if (!dmi_check_system(dell_device_table))
660 return -ENODEV;
661
662 quirks = NULL;
663
664 dmi_check_system(dell_quirks);
665
666 dmi_walk(find_tokens, NULL);
667
668 if (!da_tokens) {
669 pr_info("Unable to find dmi tokens\n");
670 return -ENODEV;
671 }
672
673 ret = platform_driver_register(&platform_driver);
674 if (ret)
675 goto fail_platform_driver;
676 platform_device = platform_device_alloc("dell-laptop", -1);
677 if (!platform_device) {
678 ret = -ENOMEM;
679 goto fail_platform_device1;
680 }
681 ret = platform_device_add(platform_device);
682 if (ret)
683 goto fail_platform_device2;
684
685
686
687
688
689 bufferpage = alloc_page(GFP_KERNEL | GFP_DMA32);
690
691 if (!bufferpage)
692 goto fail_buffer;
693 buffer = page_address(bufferpage);
694
695 ret = dell_setup_rfkill();
696
697 if (ret) {
698 pr_warn("Unable to setup rfkill\n");
699 goto fail_rfkill;
700 }
701
702 ret = i8042_install_filter(dell_laptop_i8042_filter);
703 if (ret) {
704 pr_warn("Unable to install key filter\n");
705 goto fail_filter;
706 }
707
708 if (quirks && quirks->touchpad_led)
709 touchpad_led_init(&platform_device->dev);
710
711 dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
712 if (dell_laptop_dir != NULL)
713 debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
714 &dell_debugfs_fops);
715
716#ifdef CONFIG_ACPI
717
718
719
720 if (acpi_video_backlight_support())
721 return 0;
722#endif
723
724 get_buffer();
725 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
726 if (buffer->input[0] != -1) {
727 dell_send_request(buffer, 0, 2);
728 max_intensity = buffer->output[3];
729 }
730 release_buffer();
731
732 if (max_intensity) {
733 struct backlight_properties props;
734 memset(&props, 0, sizeof(struct backlight_properties));
735 props.type = BACKLIGHT_PLATFORM;
736 props.max_brightness = max_intensity;
737 dell_backlight_device = backlight_device_register("dell_backlight",
738 &platform_device->dev,
739 NULL,
740 &dell_ops,
741 &props);
742
743 if (IS_ERR(dell_backlight_device)) {
744 ret = PTR_ERR(dell_backlight_device);
745 dell_backlight_device = NULL;
746 goto fail_backlight;
747 }
748
749 dell_backlight_device->props.brightness =
750 dell_get_intensity(dell_backlight_device);
751 backlight_update_status(dell_backlight_device);
752 }
753
754 return 0;
755
756fail_backlight:
757 i8042_remove_filter(dell_laptop_i8042_filter);
758 cancel_delayed_work_sync(&dell_rfkill_work);
759fail_filter:
760 dell_cleanup_rfkill();
761fail_rfkill:
762 free_page((unsigned long)bufferpage);
763fail_buffer:
764 platform_device_del(platform_device);
765fail_platform_device2:
766 platform_device_put(platform_device);
767fail_platform_device1:
768 platform_driver_unregister(&platform_driver);
769fail_platform_driver:
770 kfree(da_tokens);
771 return ret;
772}
773
774static void __exit dell_exit(void)
775{
776 debugfs_remove_recursive(dell_laptop_dir);
777 if (quirks && quirks->touchpad_led)
778 touchpad_led_exit();
779 i8042_remove_filter(dell_laptop_i8042_filter);
780 cancel_delayed_work_sync(&dell_rfkill_work);
781 backlight_device_unregister(dell_backlight_device);
782 dell_cleanup_rfkill();
783 if (platform_device) {
784 platform_device_unregister(platform_device);
785 platform_driver_unregister(&platform_driver);
786 }
787 kfree(da_tokens);
788 free_page((unsigned long)buffer);
789}
790
791module_init(dell_init);
792module_exit(dell_exit);
793
794MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
795MODULE_DESCRIPTION("Dell laptop driver");
796MODULE_LICENSE("GPL");
797MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*");
798MODULE_ALIAS("dmi:*svnDellInc.:*:ct9:*");
799MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*");
800