diff options
author | Dmitry Torokhov <dtor_core@ameritech.net> | 2005-06-25 17:54:25 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-25 19:24:24 -0400 |
commit | e70c9d5e61c6cb2272c866fc1303e62975006752 (patch) | |
tree | fcac373d1cab6df2a6ad87c2575fb16d778282af /drivers/char | |
parent | dec63ec32ea486ab915138e8790084c22a3f7bf6 (diff) |
[PATCH] I8K: use standard DMI interface
I8K: Change to use stock dmi infrastructure instead of homegrown
parsing code. The driver now requires box's DMI data to match
list of supported models so driver can be safely compiled-in
by default without fear of it poking into random SMM BIOS
code. DMI checks can be ignored with i8k.ignore_dmi option.
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/i8k.c | 302 |
1 files changed, 53 insertions, 249 deletions
diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index bf5e43beca62..81d2f675fb77 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c | |||
@@ -20,7 +20,7 @@ | |||
20 | #include <linux/types.h> | 20 | #include <linux/types.h> |
21 | #include <linux/init.h> | 21 | #include <linux/init.h> |
22 | #include <linux/proc_fs.h> | 22 | #include <linux/proc_fs.h> |
23 | #include <linux/apm_bios.h> | 23 | #include <linux/dmi.h> |
24 | #include <asm/uaccess.h> | 24 | #include <asm/uaccess.h> |
25 | #include <asm/io.h> | 25 | #include <asm/io.h> |
26 | 26 | ||
@@ -52,18 +52,7 @@ | |||
52 | 52 | ||
53 | #define I8K_TEMPERATURE_BUG 1 | 53 | #define I8K_TEMPERATURE_BUG 1 |
54 | 54 | ||
55 | #define DELL_SIGNATURE "Dell Computer" | 55 | static char bios_version[4]; |
56 | |||
57 | static char *supported_models[] = { | ||
58 | "Inspiron", | ||
59 | "Latitude", | ||
60 | NULL | ||
61 | }; | ||
62 | |||
63 | static char system_vendor[48] = "?"; | ||
64 | static char product_name[48] = "?"; | ||
65 | static char bios_version[4] = "?"; | ||
66 | static char serial_number[16] = "?"; | ||
67 | 56 | ||
68 | MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); | 57 | MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); |
69 | MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops"); | 58 | MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops"); |
@@ -73,6 +62,10 @@ static int force; | |||
73 | module_param(force, bool, 0); | 62 | module_param(force, bool, 0); |
74 | MODULE_PARM_DESC(force, "Force loading without checking for supported models"); | 63 | MODULE_PARM_DESC(force, "Force loading without checking for supported models"); |
75 | 64 | ||
65 | static int ignore_dmi; | ||
66 | module_param(ignore_dmi, bool, 0); | ||
67 | MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match"); | ||
68 | |||
76 | static int restricted; | 69 | static int restricted; |
77 | module_param(restricted, bool, 0); | 70 | module_param(restricted, bool, 0); |
78 | MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set"); | 71 | MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set"); |
@@ -99,11 +92,10 @@ typedef struct { | |||
99 | unsigned int edi __attribute__ ((packed)); | 92 | unsigned int edi __attribute__ ((packed)); |
100 | } SMMRegisters; | 93 | } SMMRegisters; |
101 | 94 | ||
102 | typedef struct { | 95 | static inline char *i8k_get_dmi_data(int field) |
103 | u8 type; | 96 | { |
104 | u8 length; | 97 | return dmi_get_system_info(field) ? : "N/A"; |
105 | u16 handle; | 98 | } |
106 | } DMIHeader; | ||
107 | 99 | ||
108 | /* | 100 | /* |
109 | * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard. | 101 | * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard. |
@@ -163,15 +155,6 @@ static int i8k_get_bios_version(void) | |||
163 | } | 155 | } |
164 | 156 | ||
165 | /* | 157 | /* |
166 | * Read the machine id. | ||
167 | */ | ||
168 | static int i8k_get_serial_number(unsigned char *buff) | ||
169 | { | ||
170 | strlcpy(buff, serial_number, sizeof(serial_number)); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * Read the Fn key status. | 158 | * Read the Fn key status. |
176 | */ | 159 | */ |
177 | static int i8k_get_fn_status(void) | 160 | static int i8k_get_fn_status(void) |
@@ -328,7 +311,7 @@ static int i8k_get_dell_signature(void) | |||
328 | static int i8k_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, | 311 | static int i8k_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, |
329 | unsigned long arg) | 312 | unsigned long arg) |
330 | { | 313 | { |
331 | int val; | 314 | int val = 0; |
332 | int speed; | 315 | int speed; |
333 | unsigned char buff[16]; | 316 | unsigned char buff[16]; |
334 | int __user *argp = (int __user *)arg; | 317 | int __user *argp = (int __user *)arg; |
@@ -343,7 +326,7 @@ static int i8k_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, | |||
343 | 326 | ||
344 | case I8K_MACHINE_ID: | 327 | case I8K_MACHINE_ID: |
345 | memset(buff, 0, 16); | 328 | memset(buff, 0, 16); |
346 | val = i8k_get_serial_number(buff); | 329 | strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), sizeof(buff)); |
347 | break; | 330 | break; |
348 | 331 | ||
349 | case I8K_FN_STATUS: | 332 | case I8K_FN_STATUS: |
@@ -451,10 +434,10 @@ static int i8k_get_info(char *buffer, char **start, off_t fpos, int length) | |||
451 | n = sprintf(buffer, "%s %s %s %d %d %d %d %d %d %d\n", | 434 | n = sprintf(buffer, "%s %s %s %d %d %d %d %d %d %d\n", |
452 | I8K_PROC_FMT, | 435 | I8K_PROC_FMT, |
453 | bios_version, | 436 | bios_version, |
454 | serial_number, | 437 | dmi_get_system_info(DMI_PRODUCT_SERIAL) ? : "N/A", |
455 | cpu_temp, | 438 | cpu_temp, |
456 | left_fan, | 439 | left_fan, right_fan, left_speed, right_speed, |
457 | right_fan, left_speed, right_speed, ac_power, fn_key); | 440 | ac_power, fn_key); |
458 | 441 | ||
459 | return n; | 442 | return n; |
460 | } | 443 | } |
@@ -486,201 +469,23 @@ static ssize_t i8k_read(struct file *f, char __user * buffer, size_t len, | |||
486 | return len; | 469 | return len; |
487 | } | 470 | } |
488 | 471 | ||
489 | static char *__init string_trim(char *s, int size) | 472 | static struct dmi_system_id __initdata i8k_dmi_table[] = { |
490 | { | 473 | { |
491 | int len; | 474 | .ident = "Dell Inspiron", |
492 | char *p; | 475 | .matches = { |
493 | 476 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), | |
494 | if ((len = strlen(s)) > size) { | 477 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"), |
495 | len = size; | 478 | }, |
496 | } | 479 | }, |
497 | 480 | { | |
498 | for (p = s + len - 1; len && (*p == ' '); len--, p--) { | 481 | .ident = "Dell Latitude", |
499 | *p = '\0'; | 482 | .matches = { |
500 | } | 483 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), |
501 | 484 | DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), | |
502 | return s; | 485 | }, |
503 | } | 486 | }, |
504 | 487 | { } | |
505 | /* DMI code, stolen from arch/i386/kernel/dmi_scan.c */ | 488 | }; |
506 | |||
507 | /* | ||
508 | * |<-- dmi->length -->| | ||
509 | * | | | ||
510 | * |dmi header s=N | string1,\0, ..., stringN,\0, ..., \0 | ||
511 | * | | | ||
512 | * +-----------------------+ | ||
513 | */ | ||
514 | static char *__init dmi_string(DMIHeader * dmi, u8 s) | ||
515 | { | ||
516 | u8 *p; | ||
517 | |||
518 | if (!s) { | ||
519 | return ""; | ||
520 | } | ||
521 | s--; | ||
522 | |||
523 | p = (u8 *) dmi + dmi->length; | ||
524 | while (s > 0) { | ||
525 | p += strlen(p); | ||
526 | p++; | ||
527 | s--; | ||
528 | } | ||
529 | |||
530 | return p; | ||
531 | } | ||
532 | |||
533 | static void __init dmi_decode(DMIHeader * dmi) | ||
534 | { | ||
535 | u8 *data = (u8 *) dmi; | ||
536 | char *p; | ||
537 | |||
538 | #ifdef I8K_DEBUG | ||
539 | int i; | ||
540 | printk("%08x ", (int)data); | ||
541 | for (i = 0; i < data[1] && i < 64; i++) { | ||
542 | printk("%02x ", data[i]); | ||
543 | } | ||
544 | printk("\n"); | ||
545 | #endif | ||
546 | |||
547 | switch (dmi->type) { | ||
548 | case 0: /* BIOS Information */ | ||
549 | p = dmi_string(dmi, data[5]); | ||
550 | if (*p) { | ||
551 | strlcpy(bios_version, p, sizeof(bios_version)); | ||
552 | string_trim(bios_version, sizeof(bios_version)); | ||
553 | } | ||
554 | break; | ||
555 | case 1: /* System Information */ | ||
556 | p = dmi_string(dmi, data[4]); | ||
557 | if (*p) { | ||
558 | strlcpy(system_vendor, p, sizeof(system_vendor)); | ||
559 | string_trim(system_vendor, sizeof(system_vendor)); | ||
560 | } | ||
561 | p = dmi_string(dmi, data[5]); | ||
562 | if (*p) { | ||
563 | strlcpy(product_name, p, sizeof(product_name)); | ||
564 | string_trim(product_name, sizeof(product_name)); | ||
565 | } | ||
566 | p = dmi_string(dmi, data[7]); | ||
567 | if (*p) { | ||
568 | strlcpy(serial_number, p, sizeof(serial_number)); | ||
569 | string_trim(serial_number, sizeof(serial_number)); | ||
570 | } | ||
571 | break; | ||
572 | } | ||
573 | } | ||
574 | |||
575 | static int __init dmi_table(u32 base, int len, int num, | ||
576 | void (*fn) (DMIHeader *)) | ||
577 | { | ||
578 | u8 *buf; | ||
579 | u8 *data; | ||
580 | DMIHeader *dmi; | ||
581 | int i = 1; | ||
582 | |||
583 | buf = ioremap(base, len); | ||
584 | if (buf == NULL) { | ||
585 | return -1; | ||
586 | } | ||
587 | data = buf; | ||
588 | |||
589 | /* | ||
590 | * Stop when we see al the items the table claimed to have | ||
591 | * or we run off the end of the table (also happens) | ||
592 | */ | ||
593 | while ((i < num) && ((data - buf) < len)) { | ||
594 | dmi = (DMIHeader *) data; | ||
595 | /* | ||
596 | * Avoid misparsing crud if the length of the last | ||
597 | * record is crap | ||
598 | */ | ||
599 | if ((data - buf + dmi->length) >= len) { | ||
600 | break; | ||
601 | } | ||
602 | fn(dmi); | ||
603 | data += dmi->length; | ||
604 | /* | ||
605 | * Don't go off the end of the data if there is | ||
606 | * stuff looking like string fill past the end | ||
607 | */ | ||
608 | while (((data - buf) < len) && (*data || data[1])) { | ||
609 | data++; | ||
610 | } | ||
611 | data += 2; | ||
612 | i++; | ||
613 | } | ||
614 | iounmap(buf); | ||
615 | |||
616 | return 0; | ||
617 | } | ||
618 | |||
619 | static int __init dmi_iterate(void (*decode) (DMIHeader *)) | ||
620 | { | ||
621 | unsigned char buf[20]; | ||
622 | void __iomem *p = ioremap(0xe0000, 0x20000), *q; | ||
623 | |||
624 | if (!p) | ||
625 | return -1; | ||
626 | |||
627 | for (q = p; q < p + 0x20000; q += 16) { | ||
628 | memcpy_fromio(buf, q, 20); | ||
629 | if (memcmp(buf, "_DMI_", 5) == 0) { | ||
630 | u16 num = buf[13] << 8 | buf[12]; | ||
631 | u16 len = buf[7] << 8 | buf[6]; | ||
632 | u32 base = buf[11] << 24 | buf[10] << 16 | buf[9] << 8 | buf[8]; | ||
633 | #ifdef I8K_DEBUG | ||
634 | printk(KERN_INFO "DMI %d.%d present.\n", | ||
635 | buf[14] >> 4, buf[14] & 0x0F); | ||
636 | printk(KERN_INFO "%d structures occupying %d bytes.\n", | ||
637 | buf[13] << 8 | buf[12], buf[7] << 8 | buf[6]); | ||
638 | printk(KERN_INFO "DMI table at 0x%08X.\n", | ||
639 | buf[11] << 24 | buf[10] << 16 | buf[9] << 8 | | ||
640 | buf[8]); | ||
641 | #endif | ||
642 | if (dmi_table(base, len, num, decode) == 0) { | ||
643 | iounmap(p); | ||
644 | return 0; | ||
645 | } | ||
646 | } | ||
647 | } | ||
648 | iounmap(p); | ||
649 | return -1; | ||
650 | } | ||
651 | |||
652 | /* end of DMI code */ | ||
653 | |||
654 | /* | ||
655 | * Get DMI information. | ||
656 | */ | ||
657 | static int __init i8k_dmi_probe(void) | ||
658 | { | ||
659 | char **p; | ||
660 | |||
661 | if (dmi_iterate(dmi_decode) != 0) { | ||
662 | printk(KERN_INFO "i8k: unable to get DMI information\n"); | ||
663 | return -ENODEV; | ||
664 | } | ||
665 | |||
666 | if (strncmp(system_vendor, DELL_SIGNATURE, strlen(DELL_SIGNATURE)) != 0) { | ||
667 | printk(KERN_INFO "i8k: not running on a Dell system\n"); | ||
668 | return -ENODEV; | ||
669 | } | ||
670 | |||
671 | for (p = supported_models;; p++) { | ||
672 | if (!*p) { | ||
673 | printk(KERN_INFO "i8k: unsupported model: %s\n", | ||
674 | product_name); | ||
675 | return -ENODEV; | ||
676 | } | ||
677 | if (strncmp(product_name, *p, strlen(*p)) == 0) { | ||
678 | break; | ||
679 | } | ||
680 | } | ||
681 | |||
682 | return 0; | ||
683 | } | ||
684 | 489 | ||
685 | /* | 490 | /* |
686 | * Probe for the presence of a supported laptop. | 491 | * Probe for the presence of a supported laptop. |
@@ -689,23 +494,30 @@ static int __init i8k_probe(void) | |||
689 | { | 494 | { |
690 | char buff[4]; | 495 | char buff[4]; |
691 | int version; | 496 | int version; |
692 | int smm_found = 0; | ||
693 | 497 | ||
694 | /* | 498 | /* |
695 | * Get DMI information | 499 | * Get DMI information |
696 | */ | 500 | */ |
697 | if (i8k_dmi_probe() != 0) { | 501 | if (!dmi_check_system(i8k_dmi_table)) { |
502 | if (!ignore_dmi && !force) | ||
503 | return -ENODEV; | ||
504 | |||
505 | printk(KERN_INFO "i8k: not running on a supported Dell system.\n"); | ||
698 | printk(KERN_INFO "i8k: vendor=%s, model=%s, version=%s\n", | 506 | printk(KERN_INFO "i8k: vendor=%s, model=%s, version=%s\n", |
699 | system_vendor, product_name, bios_version); | 507 | i8k_get_dmi_data(DMI_SYS_VENDOR), |
508 | i8k_get_dmi_data(DMI_PRODUCT_NAME), | ||
509 | i8k_get_dmi_data(DMI_BIOS_VERSION)); | ||
700 | } | 510 | } |
701 | 511 | ||
512 | strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), sizeof(bios_version)); | ||
513 | |||
702 | /* | 514 | /* |
703 | * Get SMM Dell signature | 515 | * Get SMM Dell signature |
704 | */ | 516 | */ |
705 | if (i8k_get_dell_signature() != 0) { | 517 | if (i8k_get_dell_signature() != 0) { |
706 | printk(KERN_INFO "i8k: unable to get SMM Dell signature\n"); | 518 | printk(KERN_ERR "i8k: unable to get SMM Dell signature\n"); |
707 | } else { | 519 | if (!force) |
708 | smm_found = 1; | 520 | return -ENODEV; |
709 | } | 521 | } |
710 | 522 | ||
711 | /* | 523 | /* |
@@ -713,9 +525,8 @@ static int __init i8k_probe(void) | |||
713 | */ | 525 | */ |
714 | version = i8k_get_bios_version(); | 526 | version = i8k_get_bios_version(); |
715 | if (version <= 0) { | 527 | if (version <= 0) { |
716 | printk(KERN_INFO "i8k: unable to get SMM BIOS version\n"); | 528 | printk(KERN_WARNING "i8k: unable to get SMM BIOS version\n"); |
717 | } else { | 529 | } else { |
718 | smm_found = 1; | ||
719 | buff[0] = (version >> 16) & 0xff; | 530 | buff[0] = (version >> 16) & 0xff; |
720 | buff[1] = (version >> 8) & 0xff; | 531 | buff[1] = (version >> 8) & 0xff; |
721 | buff[2] = (version) & 0xff; | 532 | buff[2] = (version) & 0xff; |
@@ -723,21 +534,15 @@ static int __init i8k_probe(void) | |||
723 | /* | 534 | /* |
724 | * If DMI BIOS version is unknown use SMM BIOS version. | 535 | * If DMI BIOS version is unknown use SMM BIOS version. |
725 | */ | 536 | */ |
726 | if (bios_version[0] == '?') { | 537 | if (!dmi_get_system_info(DMI_BIOS_VERSION)) |
727 | strcpy(bios_version, buff); | 538 | strlcpy(bios_version, buff, sizeof(bios_version)); |
728 | } | 539 | |
729 | /* | 540 | /* |
730 | * Check if the two versions match. | 541 | * Check if the two versions match. |
731 | */ | 542 | */ |
732 | if (strncmp(buff, bios_version, sizeof(bios_version)) != 0) { | 543 | if (strncmp(buff, bios_version, sizeof(bios_version)) != 0) |
733 | printk(KERN_INFO | 544 | printk(KERN_WARNING "i8k: BIOS version mismatch: %s != %s\n", |
734 | "i8k: BIOS version mismatch: %s != %s\n", buff, | 545 | buff, bios_version); |
735 | bios_version); | ||
736 | } | ||
737 | } | ||
738 | |||
739 | if (!smm_found && !force) { | ||
740 | return -ENODEV; | ||
741 | } | 546 | } |
742 | 547 | ||
743 | return 0; | 548 | return 0; |
@@ -751,9 +556,8 @@ int __init i8k_init(void) | |||
751 | struct proc_dir_entry *proc_i8k; | 556 | struct proc_dir_entry *proc_i8k; |
752 | 557 | ||
753 | /* Are we running on an supported laptop? */ | 558 | /* Are we running on an supported laptop? */ |
754 | if (i8k_probe() != 0) { | 559 | if (i8k_probe()) |
755 | return -ENODEV; | 560 | return -ENODEV; |
756 | } | ||
757 | 561 | ||
758 | /* Register the proc entry */ | 562 | /* Register the proc entry */ |
759 | proc_i8k = create_proc_info_entry("i8k", 0, NULL, i8k_get_info); | 563 | proc_i8k = create_proc_info_entry("i8k", 0, NULL, i8k_get_info); |