File: /usr/src/linux/arch/i386/kernel/dmi_scan.c

1     #include <linux/config.h>
2     #include <linux/types.h>
3     #include <linux/kernel.h>
4     #include <linux/string.h>
5     #include <linux/init.h>
6     #include <linux/apm_bios.h>
7     #include <linux/slab.h>
8     #include <asm/io.h>
9     #include <linux/pm.h>
10     #include <linux/keyboard.h>
11     #include <asm/keyboard.h>
12     
13     struct dmi_header
14     {
15     	u8	type;
16     	u8	length;
17     	u16	handle;
18     };
19     
20     #define dmi_printk(x)
21     //#define dmi_printk(x) printk x
22     
23     static char * __init dmi_string(struct dmi_header *dm, u8 s)
24     {
25     	u8 *bp=(u8 *)dm;
26     	bp+=dm->length;
27     	if(!s)
28     		return "";
29     	s--;
30     	while(s>0)
31     	{
32     		bp+=strlen(bp);
33     		bp++;
34     		s--;
35     	}
36     	return bp;
37     }
38     
39     /*
40      *	We have to be cautious here. We have seen BIOSes with DMI pointers
41      *	pointing to completely the wrong place for example
42      */
43      
44     static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dmi_header *))
45     {
46     	u8 *buf;
47     	struct dmi_header *dm;
48     	u8 *data;
49     	int i=1;
50     		
51     	buf = ioremap(base, len);
52     	if(buf==NULL)
53     		return -1;
54     
55     	data = buf;
56     
57     	/*
58      	 *	Stop when we see al the items the table claimed to have
59      	 *	OR we run off the end of the table (also happens)
60      	 */
61      
62     	while(i<num && (data - buf) < len)
63     	{
64     		dm=(struct dmi_header *)data;
65     	
66     		/*
67     		 *	Avoid misparsing crud if the length of the last
68     	 	 *	record is crap 
69     		 */
70     		if((data-buf+dm->length) >= len)
71     			break;
72     		decode(dm);		
73     		data+=dm->length;
74     		/*
75     		 *	Don't go off the end of the data if there is
76     	 	 *	stuff looking like string fill past the end
77     	 	 */
78     		while((data-buf) < len && (*data || data[1]))
79     			data++;
80     		data+=2;
81     		i++;
82     	}
83     	iounmap(buf);
84     	return 0;
85     }
86     
87     
88     int __init dmi_iterate(void (*decode)(struct dmi_header *))
89     {
90     	unsigned char buf[20];
91     	long fp=0xE0000L;
92     	fp -= 16;
93     
94     #ifdef CONFIG_SIMNOW
95     	/*
96      	 *	Skip on x86/64 with simnow. Will eventually go away
97      	 *	If you see this ifdef in 2.6pre mail me !
98      	 */
99     	return;
100     #endif
101      	
102     	while( fp < 0xFFFFF)
103     	{
104     		fp+=16;
105     		isa_memcpy_fromio(buf, fp, 20);
106     		if(memcmp(buf, "_DMI_", 5)==0)
107     		{
108     			u16 num=buf[13]<<8|buf[12];
109     			u16 len=buf[7]<<8|buf[6];
110     			u32 base=buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8];
111     
112     			dmi_printk((KERN_INFO "DMI %d.%d present.\n",
113     				buf[14]>>4, buf[14]&0x0F));
114     			dmi_printk((KERN_INFO "%d structures occupying %d bytes.\n",
115     				buf[13]<<8|buf[12],
116     				buf[7]<<8|buf[6]));
117     			dmi_printk((KERN_INFO "DMI table at 0x%08X.\n",
118     				buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8]));
119     			if(dmi_table(base,len, num, decode)==0)
120     				return 0;
121     		}
122     	}
123     	return -1;
124     }
125     
126     
127     enum
128     {
129     	DMI_BIOS_VENDOR,
130     	DMI_BIOS_VERSION,
131     	DMI_BIOS_DATE,
132     	DMI_SYS_VENDOR,
133     	DMI_PRODUCT_NAME,
134     	DMI_PRODUCT_VERSION,
135     	DMI_BOARD_VENDOR,
136     	DMI_BOARD_NAME,
137     	DMI_BOARD_VERSION,
138     	DMI_STRING_MAX
139     };
140     
141     static char *dmi_ident[DMI_STRING_MAX];
142     
143     /*
144      *	Save a DMI string
145      */
146      
147     static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string)
148     {
149     	char *d = (char*)dm;
150     	char *p = dmi_string(dm, d[string]);
151     	if(p==NULL || *p == 0)
152     		return;
153     	if (dmi_ident[slot])
154     		return;
155     	dmi_ident[slot] = kmalloc(strlen(p)+1, GFP_KERNEL);
156     	if(dmi_ident[slot])
157     		strcpy(dmi_ident[slot], p);
158     	else
159     		printk(KERN_ERR "dmi_save_ident: out of memory.\n");
160     }
161     
162     /*
163      *	DMI callbacks for problem boards
164      */
165     
166     struct dmi_strmatch
167     {
168     	u8 slot;
169     	char *substr;
170     };
171     
172     #define NONE	255
173     
174     struct dmi_blacklist
175     {
176     	int (*callback)(struct dmi_blacklist *);
177     	char *ident;
178     	struct dmi_strmatch matches[4];
179     };
180     
181     #define NO_MATCH	{ NONE, NULL}
182     #define MATCH(a,b)	{ a, b }
183     
184     /*
185      *	We have problems with IDE DMA on some platforms. In paticular the
186      *	KT7 series. On these it seems the newer BIOS has fixed them. The
187      *	rule needs to be improved to match specific BIOS revisions with
188      *	corruption problems
189      */ 
190      
191     static __init int disable_ide_dma(struct dmi_blacklist *d)
192     {
193     #ifdef CONFIG_BLK_DEV_IDE
194     	extern int noautodma;
195     	if(noautodma == 0)
196     	{
197     		noautodma = 1;
198     		printk(KERN_INFO "%s series board detected. Disabling IDE DMA.\n", d->ident);
199     	}
200     #endif	
201     	return 0;
202     }
203     
204     /* 
205      * Reboot options and system auto-detection code provided by
206      * Dell Computer Corporation so their systems "just work". :-)
207      */
208     
209     /* 
210      * Some machines require the "reboot=b"  commandline option, this quirk makes that automatic.
211      */
212     static __init int set_bios_reboot(struct dmi_blacklist *d)
213     {
214     	extern int reboot_thru_bios;
215     	if (reboot_thru_bios == 0)
216     	{
217     		reboot_thru_bios = 1;
218     		printk(KERN_INFO "%s series board detected. Selecting BIOS-method for reboots.\n", d->ident);
219     	}
220     	return 0;
221     }
222     
223     /*
224      * Some machines require the "reboot=s"  commandline option, this quirk makes that automatic.
225      */
226     static __init int set_smp_reboot(struct dmi_blacklist *d)
227     {
228     #ifdef CONFIG_SMP
229     	extern int reboot_smp;
230     	if (reboot_smp == 0)
231     	{
232     		reboot_smp = 1;
233     		printk(KERN_INFO "%s series board detected. Selecting SMP-method for reboots.\n", d->ident);
234     	}
235     #endif
236     	return 0;
237     }
238     
239     /*
240      * Some machines require the "reboot=b,s"  commandline option, this quirk makes that automatic.
241      */
242     static __init int set_smp_bios_reboot(struct dmi_blacklist *d)
243     {
244     	set_smp_reboot(d);
245     	set_bios_reboot(d);
246     	return 0;
247     }
248     
249     /*
250      * Some bioses have a broken protected mode poweroff and need to use realmode
251      */
252     
253     static __init int set_realmode_power_off(struct dmi_blacklist *d)
254     {
255            if (apm_info.realmode_power_off == 0)
256            {
257                    apm_info.realmode_power_off = 1;
258                    printk(KERN_INFO "%s bios detected. Using realmode poweroff only.\n", d->ident);
259            }
260            return 0;
261     }
262     
263     
264     /* 
265      * Some laptops require interrupts to be enabled during APM calls 
266      */
267     
268     static __init int set_apm_ints(struct dmi_blacklist *d)
269     {
270     	if (apm_info.allow_ints == 0)
271     	{
272     		apm_info.allow_ints = 1;
273     		printk(KERN_INFO "%s machine detected. Enabling interrupts during APM calls.\n", d->ident);
274     	}
275     	return 0;
276     }
277     
278     /* 
279      * Some APM bioses corrupt memory or just plain do not work
280      */
281     
282     static __init int apm_is_horked(struct dmi_blacklist *d)
283     {
284     	if (apm_info.disabled == 0)
285     	{
286     		apm_info.disabled = 1;
287     		printk(KERN_INFO "%s machine detected. Disabling APM.\n", d->ident);
288     	}
289     	return 0;
290     }
291     
292     
293     /*
294      *  Check for clue free BIOS implementations who use
295      *  the following QA technique
296      *
297      *      [ Write BIOS Code ]<------
298      *               |                ^
299      *      < Does it Compile >----N--
300      *               |Y               ^
301      *	< Does it Boot Win98 >-N--
302      *               |Y
303      *           [Ship It]
304      *
305      *	Phoenix A04  08/24/2000 is known bad (Dell Inspiron 5000e)
306      *	Phoenix A07  09/29/2000 is known good (Dell Inspiron 5000)
307      */
308     
309     static __init int broken_apm_power(struct dmi_blacklist *d)
310     {
311     	apm_info.get_power_status_broken = 1;
312     	printk(KERN_WARNING "BIOS strings suggest APM bugs, disabling power status reporting.\n");
313     	return 0;
314     }		
315     
316     #if defined(CONFIG_SONYPI) || defined(CONFIG_SONYPI_MODULE)
317     /*
318      * Check for a Sony Vaio system in order to enable the use of
319      * the sonypi driver (we don't want this driver to be used on
320      * other systems, even if they have the good PCI IDs).
321      *
322      * This one isn't a bug detect for those who asked, we simply want to
323      * activate Sony specific goodies like the camera and jogdial..
324      */
325     int is_sony_vaio_laptop;
326     
327     static __init int sony_vaio_laptop(struct dmi_blacklist *d)
328     {
329     	if (is_sony_vaio_laptop == 0)
330     	{
331     		is_sony_vaio_laptop = 1;
332     		printk(KERN_INFO "%s laptop detected.\n", d->ident);
333     	}
334     	return 0;
335     }
336     #endif
337     
338     /*
339      * This bios swaps the APM minute reporting bytes over (Many sony laptops
340      * have this problem).
341      */
342      
343     static __init int swab_apm_power_in_minutes(struct dmi_blacklist *d)
344     {
345     	apm_info.get_power_status_swabinminutes = 1;
346     	printk(KERN_WARNING "BIOS strings suggest APM reports battery life in minutes and wrong byte order.\n");
347     	return 0;
348     }
349     
350     /*
351      * The Intel 440GX hall of shame. 
352      *
353      * On many (all we have checked) of these boxes the $PIRQ table is wrong.
354      * The MP1.4 table is right however and so SMP kernels tend to work. 
355      */
356      
357     static __init int broken_pirq(struct dmi_blacklist *d)
358     {
359     	printk(KERN_INFO " *** Possibly defective BIOS detected (irqtable)\n");
360     	printk(KERN_INFO " *** Many BIOSes matching this signature have incorrect IRQ routing tables.\n");
361     	printk(KERN_INFO " *** If you see IRQ problems, in paticular SCSI resets and hangs at boot\n");
362     	printk(KERN_INFO " *** contact your vendor and ask about updates.\n");
363     	printk(KERN_INFO " *** Building an SMP kernel may evade the bug some of the time.\n");
364     	return 0;
365     }
366     
367     /*
368      * Some Bioses enable the PS/2 mouse (touchpad) at resume, even if it
369      * was disabled before the suspend. Linux gets terribly confused by that.
370      */
371     
372     typedef void (pm_kbd_func) (void);
373     extern pm_kbd_func *pm_kbd_request_override;
374     
375     static __init int broken_ps2_resume(struct dmi_blacklist *d)
376     {
377     #ifdef CONFIG_VT
378     	if (pm_kbd_request_override == NULL)
379     	{
380     		pm_kbd_request_override = pckbd_pm_resume;
381     		printk(KERN_INFO "%s machine detected. Mousepad Resume Bug workaround enabled.\n", d->ident);
382     	}
383     #endif	
384     	return 0;
385     }
386     
387     
388     
389     /*
390      *	Process the DMI blacklists
391      */
392      
393     
394     /*
395      *	This will be expanded over time to force things like the APM 
396      *	interrupt mask settings according to the laptop
397      */
398      
399     static __initdata struct dmi_blacklist dmi_blacklist[]={
400     #if 0
401     	{ disable_ide_dma, "KT7", {	/* Overbroad right now - kill DMA on problem KT7 boards */
402     			MATCH(DMI_PRODUCT_NAME, "KT7-RAID"),
403     			NO_MATCH, NO_MATCH, NO_MATCH
404     			} },
405     #endif			
406     	{ broken_ps2_resume, "Dell Latitude C600", {	/* Handle problems with APM on the C600 */
407     		        MATCH(DMI_SYS_VENDOR, "Dell"),
408     			MATCH(DMI_PRODUCT_NAME, "Latitude C600"),
409     			NO_MATCH, NO_MATCH
410     	                } },
411     	{ broken_apm_power, "Dell Inspiron 5000e", {	/* Handle problems with APM on Inspiron 5000e */
412     			MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
413     			MATCH(DMI_BIOS_VERSION, "A04"),
414     			MATCH(DMI_BIOS_DATE, "08/24/2000"), NO_MATCH
415     			} },
416     	{ set_realmode_power_off, "Award Software v4.60 PGMA", {	/* broken PM poweroff bios */
417     			MATCH(DMI_BIOS_VENDOR, "Award Software International, Inc."),
418     			MATCH(DMI_BIOS_VERSION, "4.60 PGMA"),
419     			MATCH(DMI_BIOS_DATE, "134526184"), NO_MATCH
420     			} },
421     	{ set_smp_bios_reboot, "Dell PowerEdge 1300", {	/* Handle problems with rebooting on Dell 1300's */
422     			MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
423     			MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/"),
424     			NO_MATCH, NO_MATCH
425     			} },
426     	{ set_bios_reboot, "Dell PowerEdge 300", {	/* Handle problems with rebooting on Dell 1300's */
427     			MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
428     			MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"),
429     			NO_MATCH, NO_MATCH
430     			} },
431     	{ set_apm_ints, "IBM", {	/* Allow interrupts during suspend on IBM laptops */
432     			MATCH(DMI_SYS_VENDOR, "IBM"),
433     			NO_MATCH, NO_MATCH, NO_MATCH
434     			} },
435     	{ set_apm_ints, "Dell Inspiron", {	/* Allow interrupts during suspend on Dell Inspiron laptops*/
436     			MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
437     			MATCH(DMI_PRODUCT_NAME, "Inspiron 4000"),
438     			NO_MATCH, NO_MATCH
439     			} },
440     	{ set_apm_ints, "Compaq 12XL125", {	/* Allow interrupts during suspend on Compaq Laptops*/
441     			MATCH(DMI_SYS_VENDOR, "Compaq"),
442     			MATCH(DMI_PRODUCT_NAME, "Compaq PC"),
443     			MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
444     			MATCH(DMI_BIOS_VERSION,"4.06")
445     			} },
446     	{ set_apm_ints, "ASUSTeK", {   /* Allow interrupts during APM or the clock goes slow */
447     			MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
448     			MATCH(DMI_PRODUCT_NAME, "L8400K series Notebook PC"),
449     			NO_MATCH, NO_MATCH
450     			} },					
451     	{ apm_is_horked, "Trigem Delhi3", { /* APM crashes */
452     			MATCH(DMI_SYS_VENDOR, "TriGem Computer, Inc"),
453     			MATCH(DMI_PRODUCT_NAME, "Delhi3"),
454     			NO_MATCH, NO_MATCH,
455     			} },
456     	{ apm_is_horked, "Sharp PC-PJ/AX", { /* APM crashes */
457     			MATCH(DMI_SYS_VENDOR, "SHARP"),
458     			MATCH(DMI_PRODUCT_NAME, "PC-PJ/AX"),
459     			MATCH(DMI_BIOS_VENDOR,"SystemSoft"),
460     			MATCH(DMI_BIOS_VERSION,"Version R2.08")
461     			} },
462     #if defined(CONFIG_SONYPI) || defined(CONFIG_SONYPI_MODULE)
463     	{ sony_vaio_laptop, "Sony Vaio", { /* This is a Sony Vaio laptop */
464     			MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
465     			MATCH(DMI_PRODUCT_NAME, "PCG-"),
466     			NO_MATCH, NO_MATCH,
467     			} },
468     #endif
469     	{ swab_apm_power_in_minutes, "Sony VAIO", { /* Handle problems with APM on Sony Vaio PCG-N505X(DE) */
470     			MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
471     			MATCH(DMI_BIOS_VERSION, "R0206H"),
472     			MATCH(DMI_BIOS_DATE, "08/23/99"), NO_MATCH
473     	} },
474     
475     	{ swab_apm_power_in_minutes, "Sony VAIO", { /* Handle problems with APM on Sony Vaio PCG-N505VX */
476     			MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
477     			MATCH(DMI_BIOS_VERSION, "W2K06H0"),
478     			MATCH(DMI_BIOS_DATE, "02/03/00"), NO_MATCH
479     			} },
480     			
481     	{ swab_apm_power_in_minutes, "Sony VAIO", {	/* Handle problems with APM on Sony Vaio PCG-XG29 */
482     			MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
483     			MATCH(DMI_BIOS_VERSION, "R0117A0"),
484     			MATCH(DMI_BIOS_DATE, "04/25/00"), NO_MATCH
485     			} },
486     
487     	{ swab_apm_power_in_minutes, "Sony VAIO", {	/* Handle problems with APM on Sony Vaio PCG-Z600NE */
488     			MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
489     			MATCH(DMI_BIOS_VERSION, "R0121Z1"),
490     			MATCH(DMI_BIOS_DATE, "05/11/00"), NO_MATCH
491     			} },
492     
493     	{ swab_apm_power_in_minutes, "Sony VAIO", {	/* Handle problems with APM on Sony Vaio PCG-Z505LS */
494     			MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
495     			MATCH(DMI_BIOS_VERSION, "R0203D0"),
496     			MATCH(DMI_BIOS_DATE, "05/12/00"), NO_MATCH
497     			} },
498     
499     	{ swab_apm_power_in_minutes, "Sony VAIO", {	/* Handle problems with APM on Sony Vaio PCG-Z505LS */
500     			MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
501     			MATCH(DMI_BIOS_VERSION, "R0203Z3"),
502     			MATCH(DMI_BIOS_DATE, "08/25/00"), NO_MATCH
503     			} },
504     	
505     	{ swab_apm_power_in_minutes, "Sony VAIO", {	/* Handle problems with APM on Sony Vaio PCG-F104K */
506     			MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
507     			MATCH(DMI_BIOS_VERSION, "R0204K2"),
508     			MATCH(DMI_BIOS_DATE, "08/28/00"), NO_MATCH
509     			} },
510     	
511     	{ swab_apm_power_in_minutes, "Sony VAIO", {	/* Handle problems with APM on Sony Vaio PCG-C1VN/C1VE */
512     			MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
513     			MATCH(DMI_BIOS_VERSION, "R0208P1"),
514     			MATCH(DMI_BIOS_DATE, "11/09/00"), NO_MATCH
515     			} },
516     	{ swab_apm_power_in_minutes, "Sony VAIO", {	/* Handle problems with APM on Sony Vaio PCG-C1VE */
517     			MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
518     			MATCH(DMI_BIOS_VERSION, "R0204P1"),
519     			MATCH(DMI_BIOS_DATE, "09/12/00"), NO_MATCH
520     			} },
521     
522     	/* Problem Intel 440GX bioses */
523     
524     	{ broken_pirq, "SABR1 Bios", {			/* Bad $PIR */
525     			MATCH(DMI_BIOS_VENDOR, "Intel Corporation"),
526     			MATCH(DMI_BIOS_VERSION,"SABR1"),
527     			NO_MATCH, NO_MATCH
528     			} },
529     	{ broken_pirq, "l44GX Bios", {        		/* Bad $PIR */
530     			MATCH(DMI_BIOS_VENDOR, "Intel Corporation"),
531     			MATCH(DMI_BIOS_VERSION,"L440GX0.86B.0094.P10"),
532     			NO_MATCH, NO_MATCH
533                             } },
534     	{ broken_pirq, "l44GX Bios", {		/* Bad $PIR */
535     			MATCH(DMI_BIOS_VENDOR, "Intel Corporation"),
536     			MATCH(DMI_BIOS_VERSION,"L440GX0.86B.0125.P13"),
537     			NO_MATCH, NO_MATCH
538     			} },
539                             
540     	/* Intel in disgiuse - In this case they can't hide and they don't run
541     	   too well either... */
542     	{ broken_pirq, "Dell PowerEdge 8450", {		/* Bad $PIR */
543     			MATCH(DMI_PRODUCT_NAME, "Dell PowerEdge 8450"),
544     			NO_MATCH, NO_MATCH, NO_MATCH
545     			} },
546     
547     	{ NULL, }
548     };
549     	
550     	
551     /*
552      *	Walk the blacklist table running matching functions until someone 
553      *	returns 1 or we hit the end.
554      */
555      
556     static __init void dmi_check_blacklist(void)
557     {
558     	struct dmi_blacklist *d;
559     	int i;
560     		
561     	d=&dmi_blacklist[0];
562     	while(d->callback)
563     	{
564     		for(i=0;i<4;i++)
565     		{
566     			int s = d->matches[i].slot;
567     			if(s==NONE)
568     				continue;
569     			if(dmi_ident[s] && strstr(dmi_ident[s], d->matches[i].substr))
570     				continue;
571     			/* No match */
572     			goto fail;
573     		}
574     		if(d->callback(d))
575     			return;
576     fail:			
577     		d++;
578     	}
579     }
580     
581     	
582     
583     /*
584      *	Process a DMI table entry. Right now all we care about are the BIOS
585      *	and machine entries. For 2.5 we should pull the smbus controller info
586      *	out of here.
587      */
588     
589     static void __init dmi_decode(struct dmi_header *dm)
590     {
591     	u8 *data = (u8 *)dm;
592     	char *p;
593     	
594     	switch(dm->type)
595     	{
596     		case  0:
597     			p=dmi_string(dm,data[4]);
598     			if(*p)
599     			{
600     				dmi_printk(("BIOS Vendor: %s\n", p));
601     				dmi_save_ident(dm, DMI_BIOS_VENDOR, 4);
602     				dmi_printk(("BIOS Version: %s\n", 
603     					dmi_string(dm, data[5])));
604     				dmi_save_ident(dm, DMI_BIOS_VERSION, 5);
605     				dmi_printk(("BIOS Release: %s\n",
606     					dmi_string(dm, data[8])));
607     				dmi_save_ident(dm, DMI_BIOS_DATE, 8);
608     			}
609     			break;
610     			
611     		case 1:
612     			p=dmi_string(dm,data[4]);
613     			if(*p)
614     			{
615     				dmi_printk(("System Vendor: %s.\n",p));
616     				dmi_save_ident(dm, DMI_SYS_VENDOR, 4);
617     				dmi_printk(("Product Name: %s.\n",
618     					dmi_string(dm, data[5])));
619     				dmi_save_ident(dm, DMI_PRODUCT_NAME, 5);
620     				dmi_printk(("Version %s.\n",
621     					dmi_string(dm, data[6])));
622     				dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6);
623     				dmi_printk(("Serial Number %s.\n",
624     					dmi_string(dm, data[7])));
625     			}
626     			break;
627     		case 2:
628     			p=dmi_string(dm,data[4]);
629     			if(*p)
630     			{
631     				dmi_printk(("Board Vendor: %s.\n",p));
632     				dmi_save_ident(dm, DMI_BOARD_VENDOR, 4);
633     				dmi_printk(("Board Name: %s.\n",
634     					dmi_string(dm, data[5])));
635     				dmi_save_ident(dm, DMI_BOARD_NAME, 5);
636     				dmi_printk(("Board Version: %s.\n",
637     					dmi_string(dm, data[6])));
638     				dmi_save_ident(dm, DMI_BOARD_VERSION, 6);
639     			}
640     			break;
641     		case 3:
642     			p=dmi_string(dm,data[8]);
643     			if(*p && *p!=' ')
644     				dmi_printk(("Asset Tag: %s.\n", p));
645     			break;
646     	}
647     }
648     
649     static int __init dmi_scan_machine(void)
650     {
651     	int err = dmi_iterate(dmi_decode);
652     	if(err == 0)
653     		dmi_check_blacklist();
654     	return err;
655     }
656     
657     module_init(dmi_scan_machine);
658