|
From: James S. <jsi...@us...> - 2001-10-06 16:27:55
|
Update of /cvsroot/linuxconsole/ruby/linux/arch/i386/kernel
In directory usw-pr-cvs1:/tmp/cvs-serv29140
Added Files:
dmi_scan.c
Log Message:
Hm. On DELL PS/2 and PM suspend don't get along. For now I just ignore the problem but we will have to think about handling PM on a input level.
--- NEW FILE: dmi_scan.c ---
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/apm_bios.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <linux/pm.h>
#include <linux/keyboard.h>
struct dmi_header
{
u8 type;
u8 length;
u16 handle;
};
#define dmi_printk(x)
//#define dmi_printk(x) printk x
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+=strlen(bp);
bp++;
s--;
}
return bp;
}
/*
* 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 *))
{
u8 *buf;
struct dmi_header *dm;
u8 *data;
int i=1;
buf = ioremap(base, len);
if(buf==NULL)
return -1;
data = buf;
/*
* Stop when we see al the items the table claimed to have
* OR we run off the end of the table (also happens)
*/
while(i<num && (data - buf) < len)
{
dm=(struct dmi_header *)data;
/*
* Avoid misparsing crud if the length of the last
* record is crap
*/
if((data-buf+dm->length) >= len)
break;
decode(dm);
data+=dm->length;
/*
* Don't go off the end of the data if there is
* stuff looking like string fill past the end
*/
while((data-buf) < len && (*data || data[1]))
data++;
data+=2;
i++;
}
iounmap(buf);
return 0;
}
int __init dmi_iterate(void (*decode)(struct dmi_header *))
{
unsigned char buf[20];
long fp=0xE0000L;
fp -= 16;
#ifdef CONFIG_SIMNOW
/*
* Skip on x86/64 with simnow. Will eventually go away
* If you see this ifdef in 2.6pre mail me !
*/
return;
#endif
while( fp < 0xFFFFF)
{
fp+=16;
isa_memcpy_fromio(buf, fp, 20);
if(memcmp(buf, "_DMI_", 5)==0)
{
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_printk((KERN_INFO "DMI %d.%d present.\n",
buf[14]>>4, buf[14]&0x0F));
dmi_printk((KERN_INFO "%d structures occupying %d bytes.\n",
buf[13]<<8|buf[12],
buf[7]<<8|buf[6]));
dmi_printk((KERN_INFO "DMI table at 0x%08X.\n",
buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8]));
if(dmi_table(base,len, num, decode)==0)
return 0;
}
}
return -1;
}
enum
{
DMI_BIOS_VENDOR,
DMI_BIOS_VERSION,
DMI_BIOS_DATE,
DMI_SYS_VENDOR,
DMI_PRODUCT_NAME,
DMI_PRODUCT_VERSION,
DMI_BOARD_VENDOR,
DMI_BOARD_NAME,
DMI_BOARD_VERSION,
DMI_STRING_MAX
};
static char *dmi_ident[DMI_STRING_MAX];
/*
* 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;
if (dmi_ident[slot])
return;
dmi_ident[slot] = kmalloc(strlen(p)+1, GFP_KERNEL);
if(dmi_ident[slot])
strcpy(dmi_ident[slot], p);
else
printk(KERN_ERR "dmi_save_ident: out of memory.\n");
}
/*
* DMI callbacks for problem boards
*/
struct dmi_strmatch
{
u8 slot;
char *substr;
};
#define NONE 255
struct dmi_blacklist
{
int (*callback)(struct dmi_blacklist *);
char *ident;
struct dmi_strmatch matches[4];
};
#define NO_MATCH { NONE, NULL}
#define MATCH(a,b) { a, b }
/*
* We have problems with IDE DMA on some platforms. In paticular the
* KT7 series. On these it seems the newer BIOS has fixed them. The
* rule needs to be improved to match specific BIOS revisions with
* corruption problems
*/
static __init int disable_ide_dma(struct dmi_blacklist *d)
{
#ifdef CONFIG_BLK_DEV_IDE
extern int noautodma;
if(noautodma == 0)
{
noautodma = 1;
printk(KERN_INFO "%s series board detected. Disabling IDE DMA.\n", d->ident);
}
#endif
return 0;
}
/*
* Reboot options and system auto-detection code provided by
* Dell Computer Corporation so their systems "just work". :-)
*/
/*
* Some machines require the "reboot=b" commandline option, this quirk makes that automatic.
*/
static __init int set_bios_reboot(struct dmi_blacklist *d)
{
extern int reboot_thru_bios;
if (reboot_thru_bios == 0)
{
reboot_thru_bios = 1;
printk(KERN_INFO "%s series board detected. Selecting BIOS-method for reboots.\n", d->ident);
}
return 0;
}
/*
* Some machines require the "reboot=s" commandline option, this quirk makes that automatic.
*/
static __init int set_smp_reboot(struct dmi_blacklist *d)
{
#ifdef CONFIG_SMP
extern int reboot_smp;
if (reboot_smp == 0)
{
reboot_smp = 1;
printk(KERN_INFO "%s series board detected. Selecting SMP-method for reboots.\n", d->ident);
}
#endif
return 0;
}
/*
* Some machines require the "reboot=b,s" commandline option, this quirk makes that automatic.
*/
static __init int set_smp_bios_reboot(struct dmi_blacklist *d)
{
set_smp_reboot(d);
set_bios_reboot(d);
return 0;
}
/*
* Some bioses have a broken protected mode poweroff and need to use realmode
*/
static __init int set_realmode_power_off(struct dmi_blacklist *d)
{
if (apm_info.realmode_power_off == 0)
{
apm_info.realmode_power_off = 1;
printk(KERN_INFO "%s bios detected. Using realmode poweroff only.\n", d->ident);
}
return 0;
}
/*
* Some laptops require interrupts to be enabled during APM calls
*/
static __init int set_apm_ints(struct dmi_blacklist *d)
{
if (apm_info.allow_ints == 0)
{
apm_info.allow_ints = 1;
printk(KERN_INFO "%s machine detected. Enabling interrupts during APM calls.\n", d->ident);
}
return 0;
}
/*
* Some APM bioses corrupt memory or just plain do not work
*/
static __init int apm_is_horked(struct dmi_blacklist *d)
{
if (apm_info.disabled == 0)
{
apm_info.disabled = 1;
printk(KERN_INFO "%s machine detected. Disabling APM.\n", d->ident);
}
return 0;
}
/*
* Check for clue free BIOS implementations who use
* the following QA technique
*
* [ Write BIOS Code ]<------
* | ^
* < Does it Compile >----N--
* |Y ^
* < Does it Boot Win98 >-N--
* |Y
* [Ship It]
*
* Phoenix A04 08/24/2000 is known bad (Dell Inspiron 5000e)
* Phoenix A07 09/29/2000 is known good (Dell Inspiron 5000)
*/
static __init int broken_apm_power(struct dmi_blacklist *d)
{
apm_info.get_power_status_broken = 1;
printk(KERN_WARNING "BIOS strings suggest APM bugs, disabling power status reporting.\n");
return 0;
}
#if defined(CONFIG_SONYPI) || defined(CONFIG_SONYPI_MODULE)
/*
* Check for a Sony Vaio system in order to enable the use of
* the sonypi driver (we don't want this driver to be used on
* other systems, even if they have the good PCI IDs).
*
* This one isn't a bug detect for those who asked, we simply want to
* activate Sony specific goodies like the camera and jogdial..
*/
int is_sony_vaio_laptop;
static __init int sony_vaio_laptop(struct dmi_blacklist *d)
{
if (is_sony_vaio_laptop == 0)
{
is_sony_vaio_laptop = 1;
printk(KERN_INFO "%s laptop detected.\n", d->ident);
}
return 0;
}
#endif
/*
* This bios swaps the APM minute reporting bytes over (Many sony laptops
* have this problem).
*/
static __init int swab_apm_power_in_minutes(struct dmi_blacklist *d)
{
apm_info.get_power_status_swabinminutes = 1;
printk(KERN_WARNING "BIOS strings suggest APM reports battery life in minutes and wrong byte order.\n");
return 0;
}
/*
* The Intel 440GX hall of shame.
*
* On many (all we have checked) of these boxes the $PIRQ table is wrong.
* The MP1.4 table is right however and so SMP kernels tend to work.
*/
static __init int broken_pirq(struct dmi_blacklist *d)
{
printk(KERN_INFO " *** Possibly defective BIOS detected (irqtable)\n");
printk(KERN_INFO " *** Many BIOSes matching this signature have incorrect IRQ routing tables.\n");
printk(KERN_INFO " *** If you see IRQ problems, in paticular SCSI resets and hangs at boot\n");
printk(KERN_INFO " *** contact your vendor and ask about updates.\n");
printk(KERN_INFO " *** Building an SMP kernel may evade the bug some of the time.\n");
return 0;
}
/*
* 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[]={
#if 0
{ disable_ide_dma, "KT7", { /* Overbroad right now - kill DMA on problem KT7 boards */
MATCH(DMI_PRODUCT_NAME, "KT7-RAID"),
NO_MATCH, NO_MATCH, NO_MATCH
} },
#endif
{ broken_apm_power, "Dell Inspiron 5000e", { /* Handle problems with APM on Inspiron 5000e */
MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
MATCH(DMI_BIOS_VERSION, "A04"),
MATCH(DMI_BIOS_DATE, "08/24/2000"), NO_MATCH
} },
{ set_realmode_power_off, "Award Software v4.60 PGMA", { /* broken PM poweroff bios */
MATCH(DMI_BIOS_VENDOR, "Award Software International, Inc."),
MATCH(DMI_BIOS_VERSION, "4.60 PGMA"),
MATCH(DMI_BIOS_DATE, "134526184"), NO_MATCH
} },
{ set_smp_bios_reboot, "Dell PowerEdge 1300", { /* Handle problems with rebooting on Dell 1300's */
MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/"),
NO_MATCH, NO_MATCH
} },
{ set_bios_reboot, "Dell PowerEdge 300", { /* Handle problems with rebooting on Dell 1300's */
MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"),
NO_MATCH, NO_MATCH
} },
{ set_apm_ints, "IBM", { /* Allow interrupts during suspend on IBM laptops */
MATCH(DMI_SYS_VENDOR, "IBM"),
NO_MATCH, NO_MATCH, NO_MATCH
} },
{ set_apm_ints, "Dell Inspiron", { /* Allow interrupts during suspend on Dell Inspiron laptops*/
MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
MATCH(DMI_PRODUCT_NAME, "Inspiron 4000"),
NO_MATCH, NO_MATCH
} },
{ set_apm_ints, "Compaq 12XL125", { /* Allow interrupts during suspend on Compaq Laptops*/
MATCH(DMI_SYS_VENDOR, "Compaq"),
MATCH(DMI_PRODUCT_NAME, "Compaq PC"),
MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
MATCH(DMI_BIOS_VERSION,"4.06")
} },
{ set_apm_ints, "ASUSTeK", { /* Allow interrupts during APM or the clock goes slow */
MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
MATCH(DMI_PRODUCT_NAME, "L8400K series Notebook PC"),
NO_MATCH, NO_MATCH
} },
{ apm_is_horked, "Trigem Delhi3", { /* APM crashes */
MATCH(DMI_SYS_VENDOR, "TriGem Computer, Inc"),
MATCH(DMI_PRODUCT_NAME, "Delhi3"),
NO_MATCH, NO_MATCH,
} },
{ apm_is_horked, "Sharp PC-PJ/AX", { /* APM crashes */
MATCH(DMI_SYS_VENDOR, "SHARP"),
MATCH(DMI_PRODUCT_NAME, "PC-PJ/AX"),
MATCH(DMI_BIOS_VENDOR,"SystemSoft"),
MATCH(DMI_BIOS_VERSION,"Version R2.08")
} },
#if defined(CONFIG_SONYPI) || defined(CONFIG_SONYPI_MODULE)
{ sony_vaio_laptop, "Sony Vaio", { /* This is a Sony Vaio laptop */
MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
MATCH(DMI_PRODUCT_NAME, "PCG-"),
NO_MATCH, NO_MATCH,
} },
#endif
{ swab_apm_power_in_minutes, "Sony VAIO", { /* Handle problems with APM on Sony Vaio PCG-N505X(DE) */
MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
MATCH(DMI_BIOS_VERSION, "R0206H"),
MATCH(DMI_BIOS_DATE, "08/23/99"), NO_MATCH
} },
{ swab_apm_power_in_minutes, "Sony VAIO", { /* Handle problems with APM on Sony Vaio PCG-N505VX */
MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
MATCH(DMI_BIOS_VERSION, "W2K06H0"),
MATCH(DMI_BIOS_DATE, "02/03/00"), NO_MATCH
} },
{ swab_apm_power_in_minutes, "Sony VAIO", { /* Handle problems with APM on Sony Vaio PCG-XG29 */
MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
MATCH(DMI_BIOS_VERSION, "R0117A0"),
MATCH(DMI_BIOS_DATE, "04/25/00"), NO_MATCH
} },
{ swab_apm_power_in_minutes, "Sony VAIO", { /* Handle problems with APM on Sony Vaio PCG-Z600NE */
MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
MATCH(DMI_BIOS_VERSION, "R0121Z1"),
MATCH(DMI_BIOS_DATE, "05/11/00"), NO_MATCH
} },
{ swab_apm_power_in_minutes, "Sony VAIO", { /* Handle problems with APM on Sony Vaio PCG-Z505LS */
MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
MATCH(DMI_BIOS_VERSION, "R0203D0"),
MATCH(DMI_BIOS_DATE, "05/12/00"), NO_MATCH
} },
{ swab_apm_power_in_minutes, "Sony VAIO", { /* Handle problems with APM on Sony Vaio PCG-Z505LS */
MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
MATCH(DMI_BIOS_VERSION, "R0203Z3"),
MATCH(DMI_BIOS_DATE, "08/25/00"), NO_MATCH
} },
{ swab_apm_power_in_minutes, "Sony VAIO", { /* Handle problems with APM on Sony Vaio PCG-F104K */
MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
MATCH(DMI_BIOS_VERSION, "R0204K2"),
MATCH(DMI_BIOS_DATE, "08/28/00"), NO_MATCH
} },
{ swab_apm_power_in_minutes, "Sony VAIO", { /* Handle problems with APM on Sony Vaio PCG-C1VN/C1VE */
MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
MATCH(DMI_BIOS_VERSION, "R0208P1"),
MATCH(DMI_BIOS_DATE, "11/09/00"), NO_MATCH
} },
{ swab_apm_power_in_minutes, "Sony VAIO", { /* Handle problems with APM on Sony Vaio PCG-C1VE */
MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
MATCH(DMI_BIOS_VERSION, "R0204P1"),
MATCH(DMI_BIOS_DATE, "09/12/00"), NO_MATCH
} },
/* Problem Intel 440GX bioses */
{ broken_pirq, "SABR1 Bios", { /* Bad $PIR */
MATCH(DMI_BIOS_VENDOR, "Intel Corporation"),
MATCH(DMI_BIOS_VERSION,"SABR1"),
NO_MATCH, NO_MATCH
} },
{ broken_pirq, "l44GX Bios", { /* Bad $PIR */
MATCH(DMI_BIOS_VENDOR, "Intel Corporation"),
MATCH(DMI_BIOS_VERSION,"L440GX0.86B.0094.P10"),
NO_MATCH, NO_MATCH
} },
{ broken_pirq, "l44GX Bios", { /* Bad $PIR */
MATCH(DMI_BIOS_VENDOR, "Intel Corporation"),
MATCH(DMI_BIOS_VERSION,"L440GX0.86B.0125.P13"),
NO_MATCH, NO_MATCH
} },
/* Intel in disgiuse - In this case they can't hide and they don't run
too well either... */
{ broken_pirq, "Dell PowerEdge 8450", { /* Bad $PIR */
MATCH(DMI_PRODUCT_NAME, "Dell PowerEdge 8450"),
NO_MATCH, NO_MATCH, NO_MATCH
} },
{ NULL, }
};
/*
* Walk the blacklist table running matching functions until someone
* returns 1 or we hit the end.
*/
static __init void dmi_check_blacklist(void)
{
struct dmi_blacklist *d;
int i;
d=&dmi_blacklist[0];
while(d->callback)
{
for(i=0;i<4;i++)
{
int s = d->matches[i].slot;
if(s==NONE)
continue;
if(dmi_ident[s] && strstr(dmi_ident[s], d->matches[i].substr))
continue;
/* No match */
goto fail;
}
if(d->callback(d))
return;
fail:
d++;
}
}
/*
* 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)
{
u8 *data = (u8 *)dm;
char *p;
switch(dm->type)
{
case 0:
p=dmi_string(dm,data[4]);
if(*p)
{
dmi_printk(("BIOS Vendor: %s\n", p));
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:
p=dmi_string(dm,data[4]);
if(*p)
{
dmi_printk(("System Vendor: %s.\n",p));
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:
p=dmi_string(dm,data[4]);
if(*p)
{
dmi_printk(("Board Vendor: %s.\n",p));
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;
case 3:
p=dmi_string(dm,data[8]);
if(*p && *p!=' ')
dmi_printk(("Asset Tag: %s.\n", p));
break;
}
}
static int __init dmi_scan_machine(void)
{
int err = dmi_iterate(dmi_decode);
if(err == 0)
dmi_check_blacklist();
return err;
}
module_init(dmi_scan_machine);
|