aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/i8k.c
diff options
context:
space:
mode:
authorDmitry Torokhov <dtor_core@ameritech.net>2005-06-25 17:54:25 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-06-25 19:24:24 -0400
commite70c9d5e61c6cb2272c866fc1303e62975006752 (patch)
treefcac373d1cab6df2a6ad87c2575fb16d778282af /drivers/char/i8k.c
parentdec63ec32ea486ab915138e8790084c22a3f7bf6 (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/i8k.c')
-rw-r--r--drivers/char/i8k.c302
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" 55static char bios_version[4];
56
57static char *supported_models[] = {
58 "Inspiron",
59 "Latitude",
60 NULL
61};
62
63static char system_vendor[48] = "?";
64static char product_name[48] = "?";
65static char bios_version[4] = "?";
66static char serial_number[16] = "?";
67 56
68MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); 57MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
69MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops"); 58MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops");
@@ -73,6 +62,10 @@ static int force;
73module_param(force, bool, 0); 62module_param(force, bool, 0);
74MODULE_PARM_DESC(force, "Force loading without checking for supported models"); 63MODULE_PARM_DESC(force, "Force loading without checking for supported models");
75 64
65static int ignore_dmi;
66module_param(ignore_dmi, bool, 0);
67MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match");
68
76static int restricted; 69static int restricted;
77module_param(restricted, bool, 0); 70module_param(restricted, bool, 0);
78MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set"); 71MODULE_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
102typedef struct { 95static 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 */
168static 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 */
177static int i8k_get_fn_status(void) 160static int i8k_get_fn_status(void)
@@ -328,7 +311,7 @@ static int i8k_get_dell_signature(void)
328static int i8k_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, 311static 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
489static char *__init string_trim(char *s, int size) 472static 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 */
514static 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
533static 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
575static 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
619static 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 */
657static 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);