linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / arch / i386 / kernel / dmi_scan.c
index 6ed7e28..ca2a0cb 100644 (file)
@@ -1,62 +1,47 @@
 #include <linux/types.h>
-#include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/acpi.h>
-#include <asm/io.h>
-#include <linux/pm.h>
-#include <asm/system.h>
 #include <linux/dmi.h>
 #include <linux/bootmem.h>
+#include <linux/slab.h>
 
-
-struct dmi_header
+static char * __init dmi_string(struct dmi_header *dm, u8 s)
 {
-       u8      type;
-       u8      length;
-       u16     handle;
-};
+       u8 *bp = ((u8 *) dm) + dm->length;
+       char *str = "";
 
-#undef DMI_DEBUG
+       if (s) {
+               s--;
+               while (s > 0 && *bp) {
+                       bp += strlen(bp) + 1;
+                       s--;
+               }
 
-#ifdef DMI_DEBUG
-#define dmi_printk(x) printk x
-#else
-#define dmi_printk(x)
-#endif
+               if (*bp != 0) {
+                       str = dmi_alloc(strlen(bp) + 1);
+                       if (str != NULL)
+                               strcpy(str, bp);
+                       else
+                               printk(KERN_ERR "dmi_string: out of memory.\n");
+               }
+       }
 
-static char * __init dmi_string(struct dmi_header *dm, u8 s)
-{
-       u8 *bp=(u8 *)dm;
-       bp+=dm->length;
-       if(!s)
-               return "";
-       s--;
-       while(s>0 && *bp)
-       {
-               bp+=strlen(bp);
-               bp++;
-               s--;
-       }
-       return bp;
+       return str;
 }
 
 /*
  *     We have to be cautious here. We have seen BIOSes with DMI pointers
  *     pointing to completely the wrong place for example
  */
-static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dmi_header *))
+static int __init dmi_table(u32 base, int len, int num,
+                           void (*decode)(struct dmi_header *))
 {
-       u8 *buf;
-       struct dmi_header *dm;
-       u8 *data;
-       int i=0;
+       u8 *buf, *data;
+       int i = 0;
                
-       buf = bt_ioremap(base, len);
-       if(buf==NULL)
+       buf = dmi_ioremap(base, len);
+       if (buf == NULL)
                return -1;
 
        data = buf;
@@ -65,377 +50,178 @@ static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dm
         *      Stop when we see all the items the table claimed to have
         *      OR we run off the end of the table (also happens)
         */
-       while(i<num && data-buf+sizeof(struct dmi_header)<=len)
-       {
-               dm=(struct dmi_header *)data;
+       while ((i < num) && (data - buf + sizeof(struct dmi_header)) <= len) {
+               struct dmi_header *dm = (struct dmi_header *)data;
                /*
                 *  We want to know the total length (formated area and strings)
                 *  before decoding to make sure we won't run off the table in
                 *  dmi_decode or dmi_string
                 */
-               data+=dm->length;
-               while(data-buf<len-1 && (data[0] || data[1]))
+               data += dm->length;
+               while ((data - buf < len - 1) && (data[0] || data[1]))
                        data++;
-               if(data-buf<len-1)
+               if (data - buf < len - 1)
                        decode(dm);
-               data+=2;
+               data += 2;
                i++;
        }
-       bt_iounmap(buf, len);
+       dmi_iounmap(buf, len);
        return 0;
 }
 
-
-inline static int __init dmi_checksum(u8 *buf)
+static int __init dmi_checksum(u8 *buf)
 {
-       u8 sum=0;
+       u8 sum = 0;
        int a;
        
-       for(a=0; a<15; a++)
-               sum+=buf[a];
-       return (sum==0);
-}
-
-static int __init dmi_iterate(void (*decode)(struct dmi_header *))
-{
-       u8 buf[15];
-       char __iomem *p, *q;
+       for (a = 0; a < 15; a++)
+               sum += buf[a];
 
-       /*
-        * no iounmap() for that ioremap(); it would be a no-op, but it's
-        * so early in setup that sucker gets confused into doing what
-        * it shouldn't if we actually call it.
-        */
-       p = ioremap(0xF0000, 0x10000);
-       if (p == NULL)
-               return -1;
-       for (q = p; q < p + 0x10000; q += 16) {
-               memcpy_fromio(buf, q, 15);
-               if(memcmp(buf, "_DMI_", 5)==0 && dmi_checksum(buf))
-               {
-                       u16 num=buf[13]<<8|buf[12];
-                       u16 len=buf[7]<<8|buf[6];
-                       u32 base=buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8];
-
-                       /*
-                        * DMI version 0.0 means that the real version is taken from
-                        * the SMBIOS version, which we don't know at this point.
-                        */
-                       if(buf[14]!=0)
-                               printk(KERN_INFO "DMI %d.%d present.\n",
-                                       buf[14]>>4, buf[14]&0x0F);
-                       else
-                               printk(KERN_INFO "DMI present.\n");
-                       dmi_printk((KERN_INFO "%d structures occupying %d bytes.\n",
-                               num, len));
-                       dmi_printk((KERN_INFO "DMI table at 0x%08X.\n",
-                               base));
-                       if(dmi_table(base,len, num, decode)==0)
-                               return 0;
-               }
-       }
-       return -1;
+       return sum == 0;
 }
 
 static char *dmi_ident[DMI_STRING_MAX];
+static LIST_HEAD(dmi_devices);
 
 /*
  *     Save a DMI string
  */
 static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string)
 {
-       char *d = (char*)dm;
-       char *p = dmi_string(dm, d[string]);
-       if(p==NULL || *p == 0)
-               return;
+       char *p, *d = (char*) dm;
+
        if (dmi_ident[slot])
                return;
-       dmi_ident[slot] = alloc_bootmem(strlen(p)+1);
-       if(dmi_ident[slot])
-               strcpy(dmi_ident[slot], p);
-       else
-               printk(KERN_ERR "dmi_save_ident: out of memory.\n");
-}
 
-/*
- * Ugly compatibility crap.
- */
-#define dmi_blacklist  dmi_system_id
-#define NO_MATCH       { DMI_NONE, NULL}
-#define MATCH          DMI_MATCH
-
-/*
- * Toshiba keyboard likes to repeat keys when they are not repeated.
- */
+       p = dmi_string(dm, d[string]);
+       if (p == NULL)
+               return;
 
-static __init int broken_toshiba_keyboard(struct dmi_blacklist *d)
-{
-       printk(KERN_WARNING "Toshiba with broken keyboard detected. If your keyboard sometimes generates 3 keypresses instead of one, see http://davyd.ucc.asn.au/projects/toshiba/README\n");
-       return 0;
+       dmi_ident[slot] = p;
 }
 
-
-#ifdef CONFIG_ACPI_SLEEP
-static __init int reset_videomode_after_s3(struct dmi_blacklist *d)
+static void __init dmi_save_devices(struct dmi_header *dm)
 {
-       /* See acpi_wakeup.S */
-       extern long acpi_video_flags;
-       acpi_video_flags |= 2;
-       return 0;
-}
-#endif
+       int i, count = (dm->length - sizeof(struct dmi_header)) / 2;
+       struct dmi_device *dev;
 
+       for (i = 0; i < count; i++) {
+               char *d = (char *)(dm + 1) + (i * 2);
 
-#ifdef CONFIG_ACPI_BOOT
-extern int acpi_force;
+               /* Skip disabled device */
+               if ((*d & 0x80) == 0)
+                       continue;
 
-static __init __attribute__((unused)) int dmi_disable_acpi(struct dmi_blacklist *d) 
-{ 
-       if (!acpi_force) { 
-               printk(KERN_NOTICE "%s detected: acpi off\n",d->ident); 
-               disable_acpi();
-       } else { 
-               printk(KERN_NOTICE 
-                      "Warning: DMI blacklist says broken, but acpi forced\n"); 
-       }
-       return 0;
-} 
+               dev = dmi_alloc(sizeof(*dev));
+               if (!dev) {
+                       printk(KERN_ERR "dmi_save_devices: out of memory.\n");
+                       break;
+               }
 
-/*
- * Limit ACPI to CPU enumeration for HT
- */
-static __init __attribute__((unused)) int force_acpi_ht(struct dmi_blacklist *d) 
-{ 
-       if (!acpi_force) { 
-               printk(KERN_NOTICE "%s detected: force use of acpi=ht\n", d->ident); 
-               disable_acpi();
-               acpi_ht = 1; 
-       } else { 
-               printk(KERN_NOTICE 
-                      "Warning: acpi=force overrules DMI blacklist: acpi=ht\n"); 
-       }
-       return 0;
-} 
-#endif
+               dev->type = *d++ & 0x7f;
+               dev->name = dmi_string(dm, *d);
+               dev->device_data = NULL;
 
-#ifdef CONFIG_ACPI_PCI
-static __init int disable_acpi_irq(struct dmi_blacklist *d) 
-{
-       if (!acpi_force) {
-               printk(KERN_NOTICE "%s detected: force use of acpi=noirq\n",
-                      d->ident);       
-               acpi_noirq_set();
+               list_add(&dev->list, &dmi_devices);
        }
-       return 0;
 }
-static __init int disable_acpi_pci(struct dmi_blacklist *d) 
-{
-       if (!acpi_force) {
-               printk(KERN_NOTICE "%s detected: force use of pci=noacpi\n",
-                      d->ident);       
-               acpi_disable_pci();
-       }
-       return 0;
-}  
-#endif
 
-/*
- *     Process the DMI blacklists
- */
-
-/*
- *     This will be expanded over time to force things like the APM 
- *     interrupt mask settings according to the laptop
- */
-static __initdata struct dmi_blacklist dmi_blacklist[]={
-
-       { broken_toshiba_keyboard, "Toshiba Satellite 4030cdt", { /* Keyboard generates spurious repeats */
-                       MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"),
-                       NO_MATCH, NO_MATCH, NO_MATCH
-                       } },
-#ifdef CONFIG_ACPI_SLEEP
-       { reset_videomode_after_s3, "Toshiba Satellite 4030cdt", { /* Reset video mode after returning from ACPI S3 sleep */
-                       MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"),
-                       NO_MATCH, NO_MATCH, NO_MATCH
-                       } },
-#endif
-
-#ifdef CONFIG_ACPI_BOOT
-       /*
-        * If your system is blacklisted here, but you find that acpi=force
-        * works for you, please contact acpi-devel@sourceforge.net
-        */
-
-       /*
-        *      Boxes that need ACPI disabled
-        */
-
-       { dmi_disable_acpi, "IBM Thinkpad", {
-                       MATCH(DMI_BOARD_VENDOR, "IBM"),
-                       MATCH(DMI_BOARD_NAME, "2629H1G"),
-                       NO_MATCH, NO_MATCH }},
-
-       /*
-        *      Boxes that need acpi=ht 
-        */
-
-       { force_acpi_ht, "FSC Primergy T850", {
-                       MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
-                       MATCH(DMI_PRODUCT_NAME, "PRIMERGY T850"),
-                       NO_MATCH, NO_MATCH }},
-
-       { force_acpi_ht, "DELL GX240", {
-                       MATCH(DMI_BOARD_VENDOR, "Dell Computer Corporation"),
-                       MATCH(DMI_BOARD_NAME, "OptiPlex GX240"),
-                       NO_MATCH, NO_MATCH }},
-
-       { force_acpi_ht, "HP VISUALIZE NT Workstation", {
-                       MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
-                       MATCH(DMI_PRODUCT_NAME, "HP VISUALIZE NT Workstation"),
-                       NO_MATCH, NO_MATCH }},
-
-       { force_acpi_ht, "Compaq Workstation W8000", {
-                       MATCH(DMI_SYS_VENDOR, "Compaq"),
-                       MATCH(DMI_PRODUCT_NAME, "Workstation W8000"),
-                       NO_MATCH, NO_MATCH }},
-
-       { force_acpi_ht, "ASUS P4B266", {
-                       MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
-                       MATCH(DMI_BOARD_NAME, "P4B266"),
-                       NO_MATCH, NO_MATCH }},
-
-       { force_acpi_ht, "ASUS P2B-DS", {
-                       MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
-                       MATCH(DMI_BOARD_NAME, "P2B-DS"),
-                       NO_MATCH, NO_MATCH }},
-
-       { force_acpi_ht, "ASUS CUR-DLS", {
-                       MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
-                       MATCH(DMI_BOARD_NAME, "CUR-DLS"),
-                       NO_MATCH, NO_MATCH }},
-
-       { force_acpi_ht, "ABIT i440BX-W83977", {
-                       MATCH(DMI_BOARD_VENDOR, "ABIT <http://www.abit.com>"),
-                       MATCH(DMI_BOARD_NAME, "i440BX-W83977 (BP6)"),
-                       NO_MATCH, NO_MATCH }},
-
-       { force_acpi_ht, "IBM Bladecenter", {
-                       MATCH(DMI_BOARD_VENDOR, "IBM"),
-                       MATCH(DMI_BOARD_NAME, "IBM eServer BladeCenter HS20"),
-                       NO_MATCH, NO_MATCH }},
-
-       { force_acpi_ht, "IBM eServer xSeries 360", {
-                       MATCH(DMI_BOARD_VENDOR, "IBM"),
-                       MATCH(DMI_BOARD_NAME, "eServer xSeries 360"),
-                       NO_MATCH, NO_MATCH }},
-
-       { force_acpi_ht, "IBM eserver xSeries 330", {
-                       MATCH(DMI_BOARD_VENDOR, "IBM"),
-                       MATCH(DMI_BOARD_NAME, "eserver xSeries 330"),
-                       NO_MATCH, NO_MATCH }},
-
-       { force_acpi_ht, "IBM eserver xSeries 440", {
-                       MATCH(DMI_BOARD_VENDOR, "IBM"),
-                       MATCH(DMI_PRODUCT_NAME, "eserver xSeries 440"),
-                       NO_MATCH, NO_MATCH }},
-
-#endif // CONFIG_ACPI_BOOT
-
-#ifdef CONFIG_ACPI_PCI
-       /*
-        *      Boxes that need ACPI PCI IRQ routing disabled
-        */
+static void __init dmi_save_ipmi_device(struct dmi_header *dm)
+{
+       struct dmi_device *dev;
+       void * data;
 
-       { disable_acpi_irq, "ASUS A7V", {
-                       MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC"),
-                       MATCH(DMI_BOARD_NAME, "<A7V>"),
-                       /* newer BIOS, Revision 1011, does work */
-                       MATCH(DMI_BIOS_VERSION, "ASUS A7V ACPI BIOS Revision 1007"),
-                       NO_MATCH }},
+       data = dmi_alloc(dm->length);
+       if (data == NULL) {
+               printk(KERN_ERR "dmi_save_ipmi_device: out of memory.\n");
+               return;
+       }
 
-       /*
-        *      Boxes that need ACPI PCI IRQ routing and PCI scan disabled
-        */
-       { disable_acpi_pci, "ASUS PR-DLS", {    /* _BBN 0 bug */
-                       MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
-                       MATCH(DMI_BOARD_NAME, "PR-DLS"),
-                       MATCH(DMI_BIOS_VERSION, "ASUS PR-DLS ACPI BIOS Revision 1010"),
-                       MATCH(DMI_BIOS_DATE, "03/21/2003") }},
+       memcpy(data, dm, dm->length);
 
-       { disable_acpi_pci, "Acer TravelMate 36x Laptop", {
-                       MATCH(DMI_SYS_VENDOR, "Acer"),
-                       MATCH(DMI_PRODUCT_NAME, "TravelMate 360"),
-                       NO_MATCH, NO_MATCH
-                       } },
+       dev = dmi_alloc(sizeof(*dev));
+       if (!dev) {
+               printk(KERN_ERR "dmi_save_ipmi_device: out of memory.\n");
+               return;
+       }
 
-#endif
+       dev->type = DMI_DEV_TYPE_IPMI;
+       dev->name = "IPMI controller";
+       dev->device_data = data;
 
-       { NULL, }
-};
+       list_add(&dev->list, &dmi_devices);
+}
 
 /*
  *     Process a DMI table entry. Right now all we care about are the BIOS
  *     and machine entries. For 2.5 we should pull the smbus controller info
  *     out of here.
  */
-
 static void __init dmi_decode(struct dmi_header *dm)
 {
-#ifdef DMI_DEBUG
-       u8 *data = (u8 *)dm;
-#endif
-       
-       switch(dm->type)
-       {
-               case  0:
-                       dmi_printk(("BIOS Vendor: %s\n",
-                               dmi_string(dm, data[4])));
-                       dmi_save_ident(dm, DMI_BIOS_VENDOR, 4);
-                       dmi_printk(("BIOS Version: %s\n", 
-                               dmi_string(dm, data[5])));
-                       dmi_save_ident(dm, DMI_BIOS_VERSION, 5);
-                       dmi_printk(("BIOS Release: %s\n",
-                               dmi_string(dm, data[8])));
-                       dmi_save_ident(dm, DMI_BIOS_DATE, 8);
-                       break;
-               case 1:
-                       dmi_printk(("System Vendor: %s\n",
-                               dmi_string(dm, data[4])));
-                       dmi_save_ident(dm, DMI_SYS_VENDOR, 4);
-                       dmi_printk(("Product Name: %s\n",
-                               dmi_string(dm, data[5])));
-                       dmi_save_ident(dm, DMI_PRODUCT_NAME, 5);
-                       dmi_printk(("Version: %s\n",
-                               dmi_string(dm, data[6])));
-                       dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6);
-                       dmi_printk(("Serial Number: %s\n",
-                               dmi_string(dm, data[7])));
-                       break;
-               case 2:
-                       dmi_printk(("Board Vendor: %s\n",
-                               dmi_string(dm, data[4])));
-                       dmi_save_ident(dm, DMI_BOARD_VENDOR, 4);
-                       dmi_printk(("Board Name: %s\n",
-                               dmi_string(dm, data[5])));
-                       dmi_save_ident(dm, DMI_BOARD_NAME, 5);
-                       dmi_printk(("Board Version: %s\n",
-                               dmi_string(dm, data[6])));
-                       dmi_save_ident(dm, DMI_BOARD_VERSION, 6);
-                       break;
+       switch(dm->type) {
+       case 0:         /* BIOS Information */
+               dmi_save_ident(dm, DMI_BIOS_VENDOR, 4);
+               dmi_save_ident(dm, DMI_BIOS_VERSION, 5);
+               dmi_save_ident(dm, DMI_BIOS_DATE, 8);
+               break;
+       case 1:         /* System Information */
+               dmi_save_ident(dm, DMI_SYS_VENDOR, 4);
+               dmi_save_ident(dm, DMI_PRODUCT_NAME, 5);
+               dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6);
+               dmi_save_ident(dm, DMI_PRODUCT_SERIAL, 7);
+               break;
+       case 2:         /* Base Board Information */
+               dmi_save_ident(dm, DMI_BOARD_VENDOR, 4);
+               dmi_save_ident(dm, DMI_BOARD_NAME, 5);
+               dmi_save_ident(dm, DMI_BOARD_VERSION, 6);
+               break;
+       case 10:        /* Onboard Devices Information */
+               dmi_save_devices(dm);
+               break;
+       case 38:        /* IPMI Device Information */
+               dmi_save_ipmi_device(dm);
        }
 }
 
 void __init dmi_scan_machine(void)
 {
-       int err = dmi_iterate(dmi_decode);
-       if(err == 0)
-               dmi_check_system(dmi_blacklist);
-       else
-               printk(KERN_INFO "DMI not present.\n");
+       u8 buf[15];
+       char __iomem *p, *q;
+
+       /*
+        * no iounmap() for that ioremap(); it would be a no-op, but it's
+        * so early in setup that sucker gets confused into doing what
+        * it shouldn't if we actually call it.
+        */
+       p = ioremap(0xF0000, 0x10000);
+       if (p == NULL)
+               goto out;
+
+       for (q = p; q < p + 0x10000; q += 16) {
+               memcpy_fromio(buf, q, 15);
+               if ((memcmp(buf, "_DMI_", 5) == 0) && dmi_checksum(buf)) {
+                       u16 num = (buf[13] << 8) | buf[12];
+                       u16 len = (buf[7] << 8) | buf[6];
+                       u32 base = (buf[11] << 24) | (buf[10] << 16) |
+                                  (buf[9] << 8) | buf[8];
+
+                       /*
+                        * DMI version 0.0 means that the real version is taken from
+                        * the SMBIOS version, which we don't know at this point.
+                        */
+                       if (buf[14] != 0)
+                               printk(KERN_INFO "DMI %d.%d present.\n",
+                                       buf[14] >> 4, buf[14] & 0xF);
+                       else
+                               printk(KERN_INFO "DMI present.\n");
+
+                       if (dmi_table(base,len, num, dmi_decode) == 0)
+                               return;
+               }
+       }
+
+out:   printk(KERN_INFO "DMI not present or invalid.\n");
 }
 
 
@@ -462,15 +248,14 @@ int dmi_check_system(struct dmi_system_id *list)
                        /* No match */
                        goto fail;
                }
+               count++;
                if (d->callback && d->callback(d))
                        break;
-               count++;
 fail:          d++;
        }
 
        return count;
 }
-
 EXPORT_SYMBOL(dmi_check_system);
 
 /**
@@ -480,8 +265,37 @@ EXPORT_SYMBOL(dmi_check_system);
  *     Returns one DMI data value, can be used to perform
  *     complex DMI data checks.
  */
-char * dmi_get_system_info(int field)
+char *dmi_get_system_info(int field)
 {
        return dmi_ident[field];
 }
+EXPORT_SYMBOL(dmi_get_system_info);
+
+/**
+ *     dmi_find_device - find onboard device by type/name
+ *     @type: device type or %DMI_DEV_TYPE_ANY to match all device types
+ *     @desc: device name string or %NULL to match all
+ *     @from: previous device found in search, or %NULL for new search.
+ *
+ *     Iterates through the list of known onboard devices. If a device is
+ *     found with a matching @vendor and @device, a pointer to its device
+ *     structure is returned.  Otherwise, %NULL is returned.
+ *     A new search is initiated by passing %NULL to the @from argument.
+ *     If @from is not %NULL, searches continue from next device.
+ */
+struct dmi_device * dmi_find_device(int type, const char *name,
+                                   struct dmi_device *from)
+{
+       struct list_head *d, *head = from ? &from->list : &dmi_devices;
 
+       for(d = head->next; d != &dmi_devices; d = d->next) {
+               struct dmi_device *dev = list_entry(d, struct dmi_device, list);
+
+               if (((type == DMI_DEV_TYPE_ANY) || (dev->type == type)) &&
+                   ((name == NULL) || (strcmp(dev->name, name) == 0)))
+                       return dev;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL(dmi_find_device);