#!/usr/bin/perl -w

# $Header: /CVSROOT/yahoo/ops/software/hwconfig/hwconfig,v 1.51.2.1 2008/11/06 01:01:10 filo Exp $

use strict 'vars';
use Getopt::Std;
use POSIX;

# constants
$ENV{PATH} = "/etc/bin:/sbin:/usr/sbin:/bin:/usr/bin:/home/y/bin:/usr/local/bin:/usr/local/sbin:/home/opt:/opt/MegaRAID/MegaCli:/usr/StorMan";

my($version) = '1.8.6';
my($timeout) = 120;
my($email) = 'hwconfig@yahoo-inc.com';

my($smbinfo) = 'smbinfo';
my($smbiosinfo) = 'smbiosinfo';
my($mptable) = 'mptable';
my($pciconf) = 'pciconf';
my($megarc) = 'megarc';
my($mfiutil) = 'mfiutil';
my($mptutil) = 'mptutil';
my($cissutil) = 'cissutil';
my($camcontrol) = 'camcontrol';
my($atacontrol) = 'atacontrol';
my($grub) = '/etc/grub.conf';
my($dmesg_file) = '/var/run/dmesg.boot';
my($dmesg_prog) = 'dmesg';
my($varrun) = '/home/y/var/run';
my($feature_htt_bit) = 28;
# linux constants
my($cpuinfo) = '/proc/cpuinfo';
my($meminfo) = '/proc/meminfo';
my($proc_ide) = '/proc/ide';
my($proc_scsi) = '/proc/scsi/scsi';
my($proc_cciss) = '/proc/driver/cciss';
my($sys_cciss) = '/sys/bus/pci/drivers/cciss';
my($sys_pci) = '/sys/bus/pci/devices';
my($proc_partitions) = '/proc/partitions';
my($proc_mdstat) = '/proc/mdstat';
my($proc_interrupts) = '/proc/interrupts';
my($proc_ioports) = '/proc/ioports';
my($proc_iomem) = '/proc/iomem';
my($lspci) = 'lspci';
my($lshal) = 'lshal';
my($ethtool) = 'ethtool';
my($dmidecode) = 'dmidecode';
my($hpacucli) = 'hpacucli';
my($twcli) = 'tw_cli';
my($arcconf) = 'arcconf';
my($freebsd_net_devs) = 'bge|em|fxp|xl|rl|nve|dc|bce|myk|msk|lnc|le';
my($perc_names) = 'PERC|ServeRAID-MR10M|SROMBSASFC';
my($lock) = '/var/tmp/hwconfig.lock';

my($need_megarc, $want_megarc, $have_megarc, $got_megarc) = (0, 0, 0, 0);
my($need_megacli, $want_megacli, $have_megacli, $got_megacli) = (0, 0, 0, 0);
my($need_mfiutil, $want_mfiutil, $have_mfiutil, $got_mfiutil) = (0, 0, 0, 0);
my($need_mptutil, $want_mptutil, $have_mptutil, $got_mptutil) = (0, 0, 0, 0);
my($need_arcconf, $want_arcconf, $have_arcconf, $got_arcconf) = (0, 0, 0, 0);
my($need_cissutil, $want_cissutil, $have_cissutil, $got_cissutil) = (0, 0, 0, 0);
my($need_hpacucli, $want_hpacucli, $have_hpacucli, $got_hpacucli) = (0, 0, 0, 0);
my($need_twcli, $want_twcli, $have_twcli, $got_twcli) = (0, 0, 0, 0);
my($need_omreport, $want_omreport, $have_omreport, $got_omreport) = (0, 0, 0, 0);

my($need_mdstat) = 0;
my($need_lvm) = 0;
my($need_ccdconfig) = 0;
my($need_rd) = 0;

my($dmesg_buf) = '';
my($output) = '';
my($stderr) = '';
my($perl_warn) = '';
my($debug) = '';
my($debug_pciconf) = '';
my($debug_lspci) = '';
my($debug_lshal) = '';
my($debug_sysctl_dev) = '';
my($debug_dmidecode) = '';
my($debug_storage) = '';
my($debug_network) = '';
my($debug_cpuinfo) = '';
my($debug_meminfo) = '';
my($sig_pci) = '';
my($os, $os_type, $os_date, $os_version, $os_up, $os_distro, $parent_os);
my($jailed, $jailer) = (0, '');
my($yroot) = '';
my($in) = 'IN';
my($line);
my($zero);
my($hostname) = '';
my(%opts);
my(%success);
my(%other_devices);
my(%smbios);
my(%dmi);
my(%sysctl_dev);

my($clean_smbios_regex) = '(\(null\)|N\/A|_?To Be Filled( By O\.E\.M\.)?_?|Not Specified|empty|<OUT OF SPEC>|System Name|System Manufacturer|NO DIMM|No Module Installed|MemUndefined|None|Unknown|<BAD INDEX>|ModulePartNumber\d+|Manufacturer\d+|SerNum\d+|AssetTagNum\d+|PartNum\d+|0000000000000000*|\.\.\.\.\.\.*)';

my($ignore_disk_models) = 'partitions|LSILogic-MegaRAID|3ware-Logical|COMPAQ-RAID|NETAPP-LUN|MegaRAID-LD|MFI-Logical-Disk|LSILOGIC-1030-IM|1030-IM|Adaptec-Mirror|ADAPTEC-RAID|LD-?\d+-RAID|Dell-VIRTUAL-DISK|DELL-PERC-|PERC-|Logical-Volume|DGC-RAID|VMware-Virtual|SRCSAS18E|RAID-0-Stripe|RAID-1-Mirror|U12U-G4020|AMCC-|LSILOGIC-Logical|Compaq-Logical|RAID-0-1|2120S-RAID|RAID-5|Dell-Virtual-Floppy|VIRTUALFLOPPY|Logical-Disk-\d+';

my(%chipsets) = (
	# http://balusc.xs4all.nl/srv/har-chi-int.php
	# http://en.wikipedia.org/wiki/List_of_Intel_chipsets

	# Intel South - use LPC bridge
	# http://en.wikipedia.org/wiki/I/O_Controller_Hub
	"0x70008086" => "SB: Intel 82371SB (PIIX3)",
	"0x71108086" => "SB: Intel 82371 (PIIX4)",
	"0x24108086" => "SB: Intel 82801AA (ICH)",
	"0x24208086" => "SB: Intel 82801AB (ICH0)",
	"0x24408086" => "SB: Intel 82801BA (ICH2)",
	"0x24808086" => "SB: Intel 82801CA (ICH3)",

	"0x24c08086" => "SB: Intel 82801DB (ICH4)",
	"0x24c08086,0x01" => "SB: Intel 82801DB A1 (ICH4)",
	"0x24c08086,0x02" => "SB: Intel 82801DB B0 (ICH4)",

	"0x24d08086" => "SB: Intel 82801E (ICH5)",		# two steppings A2/A3, both with same rev 0x02

	"0x25a18086" => "SB: Intel 6300ESB (ESB)",
	"0x25a18086,0x00" => "SB: Intel 6300ESB A0 (ESB)",
	"0x25a18086,0x01" => "SB: Intel 6300ESB A1/A2 (ESB)",
	"0x25a18086,0x02" => "SB: Intel 6300ESB A3 (ESB)",

	"0x26408086" => "SB: Intel 82801FB/FR (ICH6/R)",
	"0x26408086,0x03" => "SB: Intel 82801FB/FR B1 (ICH6/R)",
	"0x26408086,0x04" => "SB: Intel 82801FB/FR B2 (ICH6/R)",
	"0x26408086,0x05" => "SB: Intel 82801FB/FR C0 (ICH6/R)",
	"0x26418086" => "SB: Intel 82801FBM (ICH6M)",
	"0x26418086,0x03" => "SB: Intel 82801FBM B1 (ICH6M)",		# HP NC6220
	"0x26418086,0x04" => "SB: Intel 82801FBM B2 (ICH6M)",
	"0x26428086" => "SB: Intel 82801FW (ICH6W)",

	"0x26708086" => "SB: Intel 631xESB/632xESB (ESB2)",
	"0x26708086,0x00" => "IB: Intel 3100 A0 (Whitmore Lake)",
	"0x26708086,0x01" => "IB: Intel 3100 A1 (Whitmore Lake)",
	"0x26708086,0x08" => "SB: Intel 631xESB/632xESB A0 (ESB2)",
	"0x26708086,0x09" => "SB: Intel 631xESB/632xESB A1 (ESB2)",

	"0x27b08086" => "SB: Intel 82801GDH (ICH7DH)",
	"0x27b88086" => "SB: Intel 82801GB/GR (ICH7/R)",
	"0x27b98086" => "SB: Intel 82801GBM (ICH7M)",
	"0x27b98086,0x01" => "SB: Intel 82801GBM A1 (ICH7M)",
	"0x27b98086,0x02" => "SB: Intel 82801GBM B0 (ICH7M)",
	"0x27bd8086" => "SB: Intel 82801GHM (ICH7MDH)",
	"0x27bd8086,0x01" => "SB: Intel 82801GHM A1 (ICH7MDH)",
	"0x27bd8086,0x02" => "SB: Intel 82801GHM B0 (ICH7MDH)",

	"0x28108086" => "SB: Intel 82801HB/HR (ICH8/R)",
	"0x28118086" => "SB: Intel 82801HEM (ICH8M-E)",
	"0x28118086,0x02" => "SB: Intel 82801HEM B0 (ICH8M-E)",
	"0x28118086,0x03" => "SB: Intel 82801HEM B1 (ICH8M-E)",	# HP 6910p
	"0x28118086,0x04" => "SB: Intel 82801HEM B2 (ICH8M-E)",
	"0x28128086" => "SB: Intel 82801HH (ICH8DH)",
	"0x28148086" => "SB: Intel 82801HO (ICH8DO)",
	"0x28158086" => "SB: Intel 82801HBM (ICH8M)",
	"0x28158086,0x02" => "SB: Intel 82801HBM B0 (ICH8M)",
	"0x28158086,0x03" => "SB: Intel 82801HBM B1 (ICH8M)",	# MSI GM965
	"0x28158086,0x04" => "SB: Intel 82801HBM B2 (ICH8M)",

	# search for site:intel.com 2912h
	"0x29128086" => "SB: Intel 82801IH (ICH9H)",		# Home:		+AHCI -RAID
	"0x29148086" => "SB: Intel 82801IO (ICH9O)",		# Office:	+AHCI +RAID
	"0x29168086" => "SB: Intel 82801IR (ICH9R)",		# Raid:		+AHCI +RAID
	"0x29178086" => "SB: Intel 82801IBM (ICH9M)",		# Base Moble:
	"0x29178086,0x02" => "SB: Intel 82801IBM A2 (ICH9M)",
	"0x29178086,0x03" => "SB: Intel 82801IBM A3 (ICH9M)",
	"0x29188086" => "SB: Intel 82801IB (ICH9)",		# Base:		-AHCI -RAID
	"0x29198086" => "SB: Intel 82801IEM (ICH9M-E)",		# Enhanced Moble:
	"0x29198086,0x02" => "SB: Intel 82801IEM A2 (ICH9M-E)",
	"0x29198086,0x03" => "SB: Intel 82801IEM A3 (ICH9M-E)",

	"0x3a168086" => "SB: Intel 82801JIR (ICH10R)",		# Raid:
	"0x3a188086" => "SB: Intel 82801JIB (ICH10)",		# Base:

	# Intel North
	"0x12378086" => "NB: Intel 440FX (Natoma)",
	"0x71908086" => "NB: Intel 440BX",
	"0x71928086" => "NB: Intel 440BX",
	"0x71a08086" => "NB: Intel 440GX",
	"0x84ca8086" => "NB: Intel 450NX",				# Compaq 6400R
	"0x71248086" => "NB: Intel 810E (Whitney)",
	"0x11308086" => "NB: Intel 815E (Solano)",			# SM P3TSSR
	"0x25018086" => "NB: Intel 820 (Camino)",
	"0x1a308086" => "NB: Intel 845 (Brookdale)",			# HP D500
	"0x25608086" => "NB: Intel 845G (Brookdale)",			# HP D510
	"0x25708086" => "NB: Intel 865 (Springdale)",			# HP D530, HP DC5000, Dell GX270
	"0x25788086" => "NB: Intel 875P (Canterwood)",			# Intel S875WP1, PE750
	"0x25808086" => "NB: Intel 915 (Grantsdale)",			# HP DC5100, HP DX6120, HP DC7100
	"0x27708086" => "NB: Intel 945 (Lakeport)",			#
	"0x27708086,0x02" => "NB: Intel 945G (Lakeport)",		# Dell GX620, HP DC7600
	"0x27708086,0x81" => "NB: Intel 945P (Lakeport)",		#
	"0x27708086,0x82" => "NB: Intel 945PL (Lakeport)",		#
	"0x29708086" => "NB: Intel 946GZ (Lakeport G)",			# SM PDSBM
	"0x29908086" => "NB: Intel Q963/Q965 (Broadwater)",		# HP DC5700
	"0x29f08086" => "NB: Intel 3200 (Bigby)",			# Dell R200, DL120 G5
	"0x25888086" => "NB: Intel E7221 (Copper River)",		# DL320 G3
	"0x27788086" => "NB: Intel E7230 (Mukilteo)",			#
	"0x27788086,0x00" => "NB: Intel E7230 (Mukilteo)",		# PE850, DL320 G4, Intel S3000PT, DL320 G5, IR1250
	"0x27788086,0xc0" => "NB: Intel 3000 A0 (Mukilteo 2)",		# SM PDSML
	"0x25408086" => "NB: Intel E7500 (Plumas)",
	"0x25408086,0x02" => "NB: Intel E7500 A2 (Plumas)",		# SM P4DP6
	"0x25408086,0x03" => "NB: Intel E7500 A3 (Plumas)",		# SM P4DP6, Tyan i7500 (S2722)
	"0x254c8086" => "NB: Intel E7501 (Plumas)",			# Intel SE7501CW2
	"0x35908086" => "NB: Intel E7520 (Lindenhurst)",		# PE2850, DL360 G4, DL380 G4
	"0x35908086,0x09" => "NB: Intel E7520 C1 (Lindenhurst)",
	"0x35908086,0x0a" => "NB: Intel E7520 C2 (Lindenhurst)",
	"0x35908086,0x0c" => "NB: Intel E7520 C4 (Lindenhurst)",
	"0x35928086" => "NB: Intel E7320 (Lindenhurst VS)",
	"0x35928086,0x09" => "NB: Intel E7320 C1 (Lindenhurst VS)",
	"0x35928086,0x0a" => "NB: Intel E7320 C2 (Lindenhurst VS)",
	"0x35928086,0x0c" => "NB: Intel E7320 C4 (Lindenhurst VS)",	# Intel SE7320VP2
	"0x26008086" => "NB: Intel E8500 (Twin Castle)",
	"0x26008086,0x10" => "NB: Intel E8500 B0 (Twin Castle)",	# PE6850, DL580 G4
	"0x26008086,0x11" => "NB: Intel E8500 B1 (Twin Castle)",
	"0x25c08086" => "NB: Intel 5000X (Greencreek)",			# PE2950, PE1950, DL140 G3, SM X7DAL
	"0x25c08086,0x12" => "NB: Intel 5000X B2 (Greencreek)",
	"0x25c08086,0x13" => "NB: Intel 5000X B3 (Greencreek)",
	"0x25c08086,0x30" => "NB: Intel 5000X G0 (Greencreek)",
	"0x25c08086,0x31" => "NB: Intel 5000X G1 (Greencreek)",
	"0x25d08086" => "NB: Intel 5000Z (Blackford)",
	"0x25d08086,0x93" => "NB: Intel 5000Z B3 (Blackford)",
	"0x25d48086" => "NB: Intel 5000V (Blackford VS)",		# SM X7DVL-E, X7DVL-L, DL180
	"0x25d48086,0x92" => "NB: Intel 5000V B2 (Blackford VS)",
	"0x25d48086,0x93" => "NB: Intel 5000V B3 (Blackford VS)",
	"0x25d48086,0xb1" => "NB: Intel 5000V G1 (Blackford VS)",
	"0x25d88086" => "NB: Intel 5000P (Blackford)",			# Intel S5000PSL, PE1955, SM X7DBU, SM X7DBi+
	"0x25d88086,0x92" => "NB: Intel 5000P B2 (Blackford)",
	"0x25d88086,0x93" => "NB: Intel 5000P B3 (Blackford)",
	"0x25d88086,0xb1" => "NB: Intel 5000P G1 (Blackford)",
	"0x29c08086" => "NB: Intel G31/G33/P31/P35 (Bearlake)",		# Gigabyte P35-S3G, Gigabyte G33M-S2L
	"0x29d08086" => "NB: Intel Q33 (Bearlake)",			# HP DC5800
	"0x29e08086" => "NB: Intel X38/X48 (Bearlake)",			# Intel X38ML
	"0x2e208086" => "NB: Intel P45 (Eaglelake)",			# Gigabyte EP45-DS3R
	"0x65c08086" => "NB: Intel 5100 (San Clemente)",
	"0x65c08086,0x80" => "NB: Intel 5100 A0 (San Clemente)",	# DL180 G5
	"0x65c08086,0x90" => "NB: Intel 5100 B0 (San Clemente)",	# Dell R300, SM X7DCL, SM X7DCA-L, HP BL260c G5
	"0x40018086" => "NB: Intel 5400A (Seaburg)",
	"0x40018086,0x20" => "NB: Intel 5400A C0 (Seaburg)",
	"0x40038086" => "NB: Intel 5400B (Seaburg)",
	"0x40038086,0x20" => "NB: Intel 5400B C0 (Seaburg)",		# Intel S5400SF, DL160 G5
	"0x36008086" => "NB: Intel 7300 (Clarksboro)",
	"0x36008086,0x01" => "NB: Intel 7300 A1 (Clarksboro)",		# DL580 G5, Intel S7000FC4UR

	# ServerWorks Intel South
	# http://www.broadcom.com/products/Enterprise-Networking/SystemI-O-Products
	"0x02001166" => "SB: ServerWorks OSB4",
	"0x02011166" => "SB: ServerWorks CSB5",
	"0x02031166" => "SB: ServerWorks CSB6",

	# ServerWorks Intel North
	# http://www.broadcom.com/products/Enterprise-Networking/SystemI-O-Products
	"0x00081166" => "NB: ServerWorks HE-SL",			# DL360 G2, DL380 G2, PE2550
	"0x00091166" => "NB: ServerWorks LE",				# DL320, DL360
	"0x00111166" => "NB: ServerWorks GC-HE",			# DL580 G2, PE6650
	"0x00121166" => "NB: ServerWorks GC-LE",			# DL360 G3, DL380 G3 (rev 13), DL560 G1
	"0x00141166" => "NB: ServerWorks GC-LE",			# DL360 G3, DL380 G3 (rev 33)
	"0x00171166" => "NB: ServerWorks GC-SL",			# DL140, DL320 G2

	# AMD
	"0x59501002" => "SB: ATI Radeon Xpress 200",		# HP DX5150
	"0x74501022" => "NB: AMD 8131 PCI-X",			# DL145 G1, DL385 G1, DL585
	"0x74581022" => "NB: AMD 8132 PCI-X 2.0",		# DL585 G2
	"0x74681022" => "SB: AMD 8111 I/O Hub",			# Tyan S2875, DL585
	"0x005e10de" => "SB: nVIDIA nForce Professional 2200",	# DL145 G2, DL585 G2
	"0x036910de" => "SB: nVIDIA nForce Professional 3600",	# IR2400
	"0x075410de" => "SB: nVIDIA GeForce 8200",		# Asus M3N78-VM
	"0x06501039" => "SB: SiS 650GX",			# ASUS P4SGX-MX
	"0x03361106" => "NB: VIA K8M890",			# north - HP DX2255
	"0x31881106" => "NB: VIA K8T800",			# north
	"0x32271106" => "SB: VIA VT8237",			# south
	"0x33371106" => "SB: VIA VT8237A",			# south - HP DX2255

	# ServerWorks AMD
	# http://www.broadcom.com/products/Enterprise-Networking/SystemI-O-Products
	"0x02341166" => "ServerWorks HT-1000",			# PE6950, PE2970, DL145 G3, DL365 G1, DL385 G2

	# Intel Mobile
	"0x25908086" => "NB: Intel 915GM (Alviso)",		# NC6220
	"0x27a08086" => "NB: Intel 945GM (Calistoga)",		# NC6400
	"0x2a008086" => "NB: Intel PM965/GM965 (Crestline)",	# HP 6910p, MSI GM965, AOpen i965GMt-LA
);

my(%pci_classes) = (
	# pciutils-2.2.4/lib/header.h

	"0x0100" => "Disk-Control",	# SCSI
	"0x0101" => "Disk-Control",	# IDE
	"0x0104" => "Disk-Control",	# RAID
	"0x0105" => "Disk-Control",	# ATA
	"0x0106" => "Disk-Control",	# SATA
	"0x0107" => "Disk-Control",	# SAS
	"0x0180" => "Disk-Control",	# Other
	"0x0200" => "Network",		# Ethernet
	"0x0280" => "Network",		# Other (e.g. wireless)
	"0x0c04" => "Disk-Control",	# FC
#	"0x0c05" => "SMBus",		# SMBus
	"0x0c06" => "Network",		# InfiniBand
	"0x1000" => "Crypto",		# Crypt Network
);

my(%pci_devices) = (
	# http://www.pcidatabase.com/
	# http://www.pcidatabase.com/reports.php?type=tab-delimeted
	# http://pciids.sourceforge.net/pci.ids

	# 0e11 - Compaq
	# 1000 - Symbios/LSI - http://en.wikipedia.org/wiki/Symbios_Logic
	# 1002 - ATI
	# 100b - National Semiconductor
	# 1014 - IBM
	# 1022 - AMD
	# 1028 - Dell
	# 1033 - NEC
	# 103c - HP
	# 1044 - DPT/Adaptec
	# 1069 - Mylex
	# 1077 - QLogic
	# 107b - Gateway
	# 108e - Sun
	# 10b7 - 3com
	# 10df - Emulex
	# 10f1 - Tyan
	# 1095 - Silicon Image
	# 1148 - SysKonnect
	# 1166 - Serverworks
	# 1170 - Inventec
	# 11ab - Marvell
	# 11ad - Netgear
	# 13c1 - 3ware
	# 1458 - Gigabyte
	# 1462 - MSI
	# 14c1 - Myricom
	# 14e4 - Broadcom
	# 152d - Quanta
	# 15ad - VMware
	# 161f - Arima (Rioworks)
	# 17c2 - Newisys
	# 1749 - RLX
	# 8086 - Intel
	# 9004 - Adaptec
	# 9005 - Adaptec
	# a0a0 - AOpen

	'network' => {
		"0xf0041385,0x000211ad,0x20" =>	"Netgear FA310TX 10/100",			# Adapter

		"0x20001022,0x20001022,0x10" => "AMD 79C971 Lance/PCnet 10/100",		# VMware
		"0x20001022,0x20001022,0x40" => "AMD 79C971 Lance/PCnet 10/100",		# VirtualBox

		"0x000914c1,0x000814c1,0x00" => "Myricom Myri-10G",				# Adapter

		"0x00000000,0x0035100b,0x30" => "NatSemi DP83065 Gigabit",

		"0x21001148,0x9e001148,0x12" => "SysKonnect 9E21 Gigabit",			# SysKonnect 9E21 Server Adapter

		"0x811a1043,0x432011ab,0x14" => "Asus/Marvell Yukon 88E8001 Gigabit",		# Asus P5Q-Deluxe

		"0x502111ab,0x436111ab,0x16" => "Marvell Yukon 88E8050 Gigabit",		# Intel SE7320VP2
		"0x50218086,0x436111ab,0x17" => "Intel/Marvell Yukon 88E8050 Gigabit",		# Intel SE7320VP2, SE7520BD2
		"0x34398086,0x436111ab,0x18" => "Intel/Marvell Yukon 88E8050 Gigabit",		# Intel SE7520BB2
		"0x826e1043,0x436411ab,0x12" => "Asus/Marvell Yukon 88E8056 Gigabit",		# Asus P5BV-C
		"0x81f81043,0x436411ab,0x12" => "Asus/Marvell Yukon 88E8056 Gigabit",		# Asus P5Q-Deluxe

		"0xe0001458,0x813610ec,0x01" => "Gigabyte/RealTek RTL8101E 10/100",		# Gigabyte 945GCM-S2C
		"0x00018086,0x813610ec,0x02" => "Intel/RealTek RTL8102EL 10/100",		# Intel D945GCLF

		"0x????????,0x813910ec,0x10" => "RealTek 8139 10/100",
		"0x03231154,0x813910ec,0x10" => "RealTek 8139 10/100",
		"0x813910ec,0x813910ec,0x10" => "RealTek 8139 10/100",
		"0x81091043,0x813910ec,0x10" => "Asus/RealTek 8100C 10/100",			# Asus P4GE-MX
		"0x301e103c,0x813910ec,0x10" => "HP/RealTek 8100C 10/100",			# HP DX2180
		"0x3024103c,0x813910ec,0x10" => "HP/RealTek 8100C 10/100",			# HP DX2255

		"0x3022103c,0x816710ec,0x10" => "HP/RealTek RTL8110SX Gigabit",			# HP DX2280
		"0xe0001458,0x816810ec,0x01" => "Gigabyte/RealTek 8111C Gigabit",		# Gigabyte P35-S3G, Gigabyte G33M-S2L
		"0xe0001458,0x816810ec,0x02" => "Gigabyte/RealTek 8111C Gigabit",		# Gigabyte EP45-DS3R
		"0x063ea0a0,0x816810ec,0x02" => "AOpen/RealTek 8111C Gigabit",			# AOpen i965GMt-LA
		"0x3022103c,0x816910ec,0x10" => "HP/RealTek RTL8110SX Gigabit",			# HP DX2280
		"0x301f103c,0x816910ec,0x10" => "HP/RealTek RTL8110SX Gigabit",			# HP D290

		"0x100010b7,0x920010b7,0x74" => "3Com 3c905C-TX 10/100",
		"0x00fe1028,0x920010b7,0x78" => "Dell/3Com 3c905C-TX 10/100",			# Dell OptiPlex GX240
		"0x00b41028,0x920010b7,0x78" => "Dell/3Com 3c905C-TX 10/100",			#
		"0x00d81028,0x920010b7,0x78" => "Dell/3Com 3c905C-TX 10/100",			# Dell 530

		"0x00261170,0x037310de,0xa3" => "Inventec/nVIDIA MCP55 Gigabit",		# IR2400
		"0x286510f1,0x005710de,0xa3" => "Tyan/nVIDIA nForce CK8-04 Gigabit",		# Tyan S2865
		"0x5348108e,0x005710de,0xa3" => "Sun/nVIDIA nForce CK8-04 Gigabit",		# Sun X2100

		# Broadcom

		"0x3011103c,0x160014e4,0x01" => "HP/Broadcom BCM5752 Gigabit",			# HP DC7600

		"0x037c1014,0x163914e4,0x01" => "IBM/Broadcom BCM5709 Gigabit",			# IBM x3850 M2

		"0x00d11028,0x164414e4,0x10" => "Dell/Broadcom BCM5700 Gigabit",		# PE2550
		"0x00d11028,0x164414e4,0x12" => "Dell/Broadcom BCM5700 Gigabit",		# PE2550
		"0x01091028,0x164414e4,0x14" => "Dell/Broadcom BCM5700 Gigabit",		# PE6650

		"0x????????,0x164514e4,0x15" => "Broadcom BCM5701 Gigabit",
		"0x01211028,0x164514e4,0x15" => "Dell/Broadcom BCM5701 Gigabit",		# PE2650
		"0x007c0e11,0x164514e4,0x15" => "Compaq/Broadcom BCM5701 Gigabit",		# DL580 G2
		"0x00c10e11,0x164514e4,0x15" => "HP NC6770/Broadcom BCM5701 Gigabit",		# HP NC6770 Adapter
		"0x00850e11,0x164514e4,0x15" => "Compaq/Broadcom BCM5701 Gigabit",		# DL360 G2
		"0x100410b7,0x164514e4,0x15" => "3Com/Broadcom BCM5701 Gigabit-SX",		# 3Com 3C996-SX
		"0x100610b7,0x164514e4,0x15" => "3Com/Broadcom BCM5701 Gigabit",		# 3Com 3C996B-T

		"0x????????,0x164814e4,0x??" => "Broadcom BCM5704C Gigabit",
		"0x164814e4,0x164814e4,0x00" => "Broadcom BCM5704C Gigabit",			# DL380 G4
		"0x014a1028,0x164814e4,0x02" => "Dell/Broadcom BCM5704C Gigabit",		# PE1750
		"0x164814e4,0x164814e4,0x02" => "Broadcom BCM5704C Gigabit",			# DL140
		"0x36c01022,0x164814e4,0x02" => "AMD/Broadcom BCM5704C Gigabit",		# DL145 G1
		"0x001017c2,0x164814e4,0x03" => "Newisys/Broadcom BCM5704C Gigabit",		# V20z
		"0x164814e4,0x164814e4,0x03" => "Broadcom BCM5704C Gigabit",			# Tyan S4882, Tyan S4881
		"0x164414e4,0x164814e4,0x03" => "Broadcom BCM5704C Gigabit",			# Tyan S2882, Tyan S2881, Tyan S2880, Tyan S4882
		"0x36c01022,0x164814e4,0x03" => "AMD/Broadcom BCM5704C Gigabit",		# DL145 G1
		"0x16481022,0x164814e4,0x03" => "AMD/Broadcom BCM5704C Gigabit",		# DL145 G1
		"0x16101462,0x164814e4,0x03" => "MSI/Broadcom BCM5704C Gigabit",		# MSI ???
		"0x24501462,0x164814e4,0x03" => "MSI/Broadcom BCM5704C Gigabit",		# MSI 8480000
		"0x00d00e11,0x164814e4,0x10" => "HP/Broadcom BCM5704C Gigabit",			# DL360 G4, DL380 G4, DL 385, DL 585
		"0x310f103c,0x164814e4,0x10" => "HP/Broadcom BCM5704C Gigabit",			# DL185 G5
		"0x01701028,0x164814e4,0x10" => "Dell/Broadcom BCM5704C Gigabit",		# PE6850
		"0x164414e4,0x164814e4,0x10" => "Broadcom BCM5704C Gigabit",			# Tyan S2882, Tyan S2880
		"0x164814e4,0x164814e4,0x10" => "Broadcom BCM5704C Gigabit",			# Arima HDAMA-I

		"0x1709103c,0x164a14e4,0x02" => "HP/Broadcom BCM5706 Gigabit",			# DL585 G2, DL580 G4

		"0x????????,0x164c14e4,0x??" => "Broadcom BCM5708 Gigabit",			#
		"0x164c14e4,0x164c14e4,0x10" => "Broadcom BCM5708 Gigabit",			# Tyan S2865
		"0x7038103c,0x164c14e4,0x11" => "HP/Broadcom BCM5708 Gigabit",			# DL360 G5, DL380 G5, DL365 G1
		"0x01b31028,0x164c14e4,0x11" => "Dell/Broadcom BCM5708 Gigabit",		# PE1950
		"0x01b21028,0x164c14e4,0x11" => "Dell/Broadcom BCM5708 Gigabit",		# PE2950
		"0x03421014,0x164c14e4,0x11" => "IBM/Broadcom BCM5708 Gigabit",			# IBM x3550
		"0x164c14e4,0x164c14e4,0x12" => "Broadcom BCM5708 Gigabit",			# Adapter?
		"0x01b31028,0x164c14e4,0x12" => "Dell/Broadcom BCM5708 Gigabit",		# PE1950
		"0x01b21028,0x164c14e4,0x12" => "Dell/Broadcom BCM5708 Gigabit",		# PE2950
		"0x7037103c,0x164c14e4,0x12" => "HP NC373T/Broadcom BCM5708 Gigabit",		# HP NC373T Adapter
		"0x7038103c,0x164c14e4,0x12" => "HP NC373i/Broadcom BCM5708 Gigabit",		# DL360 G5, DL380 G5, DL365 G1, DL385 G2, DL580 G5
		"0x01ea1028,0x164c14e4,0x12" => "Dell/Broadcom BCM5708 Gigabit",		# PE6950
		"0x01f01028,0x164c14e4,0x12" => "Dell/Broadcom BCM5708 Gigabit",		# Dell R900
		"0x02051028,0x164c14e4,0x12" => "Dell/Broadcom BCM5708 Gigabit",		# PE2970

		"0x86531028,0x165314e4,0x01" => "Dell/Broadcom BCM5705 Gigabit",

		"0x3100103c,0x165414e4,0x03" => "HP NC1020/Broadcom BCM5705 Gigabit",		# HP NC1020 Adapter

		"0x????????,0x165914e4,0x??" => "Broadcom BCM5721 Gigabit",
		"0x165914e4,0x165914e4,0x00" => "Broadcom BCM5721 Gigabit",
		"0x165914e4,0x165914e4,0x01" => "Broadcom BCM5721 Gigabit",			# Gigabyte 9ILDR	!!! FreeBSD 6 says 5750
		"0x01b61028,0x165914e4,0x11" => "Dell/Broadcom BCM5721 Gigabit",		# PE850
		"0x01e61028,0x165914e4,0x11" => "Dell/Broadcom BCM5721 Gigabit",		# PE860
		"0x165914e4,0x165914e4,0x11" => "Broadcom BCM5721 Gigabit",			# Inventec Tuna, Tyan S2865, Inventec IR2100
		"0x00221170,0x165914e4,0x11" => "Inventec/Broadcom BCM5721 Gigabit",		# IR1250
		"0x1659103c,0x165914e4,0x11" => "HP/Broadcom BCM5721 Gigabit",			# DL140 G2
		"0x3209103c,0x165914e4,0x11" => "HP/Broadcom BCM5721 Gigabit",			# DL145 G2
		"0x3260103c,0x165914e4,0x11" => "HP/Broadcom BCM5721 Gigabit",			# DL140 G3
		"0x81491043,0x165914e4,0x11" => "Asus/Broadcom BCM5721 Gigabit",		# Asus K8N-DRE
		"0x5348108e,0x165914e4,0x11" => "Sun/Broadcom BCM5721 Gigabit",			# Sun X2100
		"0x02c61014,0x165914e4,0x11" => "IBM/Broadcom BCM5721 Gigabit",			# IBM 306m
		"0x81491043,0x165914e4,0x21" => "Asus/Broadcom BCM5721 Gigabit",		# Asus K8N-DRE
		"0x01eb1028,0x165914e4,0x21" => "Dell/Broadcom BCM5721 Gigabit",		# SC1435		!!! FreeBSD 6 says 5750
		"0x023c1028,0x165914e4,0x21" => "Dell/Broadcom BCM5721 Gigabit",		# Dell R200

		"0x020f1028,0x165a14e4,0x00" => "Dell/Broadcom BCM5722 Gigabit",		# Dell R300
		"0x7051103c,0x165a14e4,0x00" => "HP/Broadcom BCM5722 Gigabit",			# DL180 G5, DL120 G5, DL160 G5

		"0x7039103c,0x166814e4,0xa3" => "HP/Broadcom BCM5714 Gigabit",			# DL320 G5

		"0x????????,0x167714e4,0x??" => "Broadcom BCM5751 Gigabit",
		"0x3005103c,0x167714e4,0x01" => "HP/Broadcom BCM5751 Gigabit",			# HP DC5100, HP DC7100
		"0x3006103c,0x167714e4,0x01" => "HP/Broadcom BCM5751 Gigabit",			# HP DC5100 (PT005AW)
		"0x00221170,0x167714e4,0x11" => "Inventec/Broadcom BCM5751 Gigabit",		# IR1250
		"0x3009103c,0x167714e4,0x20" => "HP/Broadcom BCM5751 Gigabit",			# HP DX5150
		"0x01791028,0x167714e4,0x01" => "Dell/Broadcom BCM5751 Gigabit",		# Dell GX280
		"0x01ad1028,0x167714e4,0x01" => "Dell/Broadcom BCM5751 Gigabit",		# Dell GX620
		"0x01821028,0x167714e4,0x01" => "Dell/Broadcom BCM5751 Gigabit",		# Dell Latitude D610

		"0x????????,0x167814e4,0xa3" => "Broadcom BCM5715 Gigabit",
		"0x167814e4,0x167814e4,0xa3" => "Broadcom BCM5715 Gigabit",			# Huawei Tecal
		"0x703e103c,0x167814e4,0xa3" => "HP/Broadcom BCM5715 Gigabit",			# DL145 G3
		"0x8959152d,0x167814e4,0xa3" => "Quanta/Broadcom BCM5715 Gigabit",		# Quanta S47

		"0x703c103c,0x167914e4,0xa3" => "HP/Broadcom BCM5715S Gigabit",			# HP BL480c G1, HP BL260c G5, HP NC326i Adapter

		"0x2808103c,0x167b14e4,0x02" => "HP/Broadcom BCM5755 Gigabit",			# HP DC5700

		"0x000c103c,0x169614e4,0x03" => "HP/Broadcom BCM5782 Gigabit",			#
		"0x12bc103c,0x169614e4,0x03" => "HP/Broadcom BCM5782 Gigabit",			# HP DC5000, HP D530

		"0x0944103c,0x167d14e4,0x11" => "HP/Broadcom BCM5751M Gigabit",			# HP NC6220

		"0x000c14e4,0x16a614e4,0x02" => "Broadcom BCM5702 Gigabit",			# Arima HDAMA
		"0x00bb0e11,0x16a614e4,0x02" => "HP/Broadcom BCM5702 Gigabit",			# DL320 G2
		"0x800914e4,0x16a614e4,0x02" => "Broadcom BCM5702 Gigabit",			# AMD Melody

		"0x01211028,0x16a714e4,0x02" => "Dell/Broadcom BCM5703 Gigabit",		# PE2650
		"0x00cb0e11,0x16a714e4,0x02" => "HP/Broadcom BCM5703 Gigabit",			# DL360 G3, DL380 G3, DL180, DL560 G1
		"0x001017c2,0x16a714e4,0x02" => "Newisys/Broadcom BCM5703 Gigabit",		# V20z
		"0x002017c2,0x16a714e4,0x02" => "Newisys/Broadcom BCM5703 Gigabit",		# V40z
		"0x00ca0e11,0x16c714e4,0x10" => "HP NC7771/Broadcom BCM5703 Gigabit",		# HP NC7771 adapter

		"0x03011014,0x16a814e4,0x10" => "IBM/Broadcom BCM5704S Gigabit",		# IBM eServer BladeCenter LS20

		"0x01bb1028,0x16ac14e4,0x12" => "Dell/Broadcom BCM5708S Gigabit",		# PE1955
		"0x703b103c,0x16ac14e4,0x12" => "HP/Broadcom BCM5708S Gigabit",			# HP BL460c G1, HP BL480c G1, HP SB600c

		"0x30ad103c,0x16fd14e4,0x21" => "HP/Broadcom BCM5753M Gigabit",			# HP NC6400

		# Intel
		# http://support.intel.com/support/network/sb/cs-008441.htm

		"0x004a0e11,0x10018086,0x02" => "HP NC6136/Intel 82543GC Gigabit",		# HP NC6136 adapter

		"0x10048086,0x10048086,0x02" => "Intel 82543GC Gigabit",			# Intel PRO/1000 T Server Adapter

		"0x011b1028,0x10088086,0x02" => "Dell/Intel 82544EI Gigabit",			# PE1650
		"0x21201749,0x10088086,0x02" => "RLX/Intel 82544EI Gigabit",			# RLX ServerBlade
		"0x11078086,0x10088086,0x02" => "Intel 82544EI Gigabit",			# Intel PRO/1000 XT Server Adapter

		"0x34128086,0x100d8086,0x02" => "Intel 82544GC Gigabit",			# Intel SRSH4

		"0x????????,0x100e8086,0x02" => "Intel 82540EM Gigabit",
		"0x004e8086,0x100e8086,0x02" => "Intel 82540EM Gigabit",			# SM P4SGR
		"0x34188086,0x100e8086,0x02" => "Intel 82540EM Gigabit",			# Intel SE7501BR2
		"0x34258086,0x100e8086,0x02" => "Intel 82540EM Gigabit",			# Intel SE7501CW2
		"0x002e8086,0x100e8086,0x02" => "Intel 82540EM Gigabit",			# Intel adapter
		"0x892d152d,0x100e8086,0x02" => "Quanta/Intel 82540EM Gigabit",			# SU2-4200
		"0x01341028,0x100e8086,0x02" => "Dell/Intel 82540EM Gigabit",			# SU2-4200
		"0x01351028,0x100e8086,0x02" => "Dell/Intel 82540EM Gigabit",			# Dell 1600SC
		"0x01511028,0x100e8086,0x02" => "Dell/Intel 82540EM Gigabit",			# Dell GX270
		"0x01381028,0x100e8086,0x02" => "Dell/Intel 82540EM Gigabit",			# Dell SX260

		"0x10018086,0x100f8086,0x01" => "Intel 82545EM Gigabit",			# Tyan S2722
		"0x075015ad,0x100f8086,0x01" => "VMware/Intel 82545EM Gigabit",			#

		"0x????????,0x10108086,0x01" => "Intel 82546EB Gigabit",
		"0x00db0e11,0x10108086,0x01" => "HP NC7170/Intel 82546EB Gigabit",		# HP NC7170 adapter
		"0x10128086,0x10108086,0x01" => "Intel 82546EB Gigabit",			# Intel PRO/1000 MT Server Adapter
		"0x34158086,0x10108086,0x01" => "Intel 82546EB Gigabit",			# Intel SE7500WV2
		"0x34168086,0x10108086,0x01" => "Intel 82546EB Gigabit",			# Intel SE7500WV2A
		"0x341a8086,0x10108086,0x01" => "Intel 82546EB Gigabit",			# Intel SE7501WV2S
		"0x341b8086,0x10108086,0x01" => "Intel 82546EB Gigabit",			# Intel SE7501WV2A
		"0x10118086,0x10108086,0x01" => "Intel 82546EB Gigabit",			# SM X5DP8

		"0x10138086,0x10138086,0x00" => "Intel 82541EI Gigabit",			# Tyan S2875

		"0x34288086,0x10198086,0x00" => "Intel 82547EI Gigabit",			# Intel S875WP1

		"0x10018086,0x10268086,0x04" => "Intel 82545GM Gigabit",			# Intel PRO/1000 MT Server Adapter
		"0x10028086,0x10268086,0x04" => "Intel 82545GM Gigabit",			# Intel PRO/1000 MT Server Adapter
		"0x30001458,0x10268086,0x04" => "Gigabyte/Intel 82545GM Gigabit",		# Gigabyte GA-7A8DRH

		"0x00120e11,0x103b8086,0x81" => "Compaq/Intel 82801DB ICH4 10/100 VM",		# HP D510

		"0x30be103c,0x10498086,0x03" => "Intel 82566MM Gigabit",			# HP 6910p
		"0x30c1103c,0x10498086,0x03" => "Intel 82566MM Gigabit",			# HP 6910p

		"0x00008086,0x104b8086,0x03" => "Intel 82566DC Gigabit",			# MSI GM965

		"0x34288086,0x10508086,0x01" => "Intel 82562ET 10/100",				# Intel S875WP1

		"0x????????,0x105e8086,0x06" => "Intel 82571EB Gigabit",
		"0x115e8086,0x105e8086,0x06" => "Intel 82571EB Gigabit",			# Intel PRO/1000 PT Server Adapter
		"0x125e8086,0x105e8086,0x06" => "Intel 82571EB Gigabit",			# Intel PRO/1000 PT Server Adapter
		"0x135e8086,0x105e8086,0x06" => "Intel 82571EB Gigabit",			# Intel PRO/1000 PT Server Adapter
		"0x7044103c,0x105e8086,0x06" => "HP NC360T/Intel 82571EB Gigabit",		# HP NC360T adapter

		"0x30548086,0x10648086,0x01" => "Intel 82562EZ 10/100",				# Intel D915GAG
		"0x305d8086,0x10648086,0x01" => "Intel 82562EZ 10/100",				# Intel D915GUX

		"0x01651028,0x10758086,0x00" => "Dell/Intel 82547GI Gigabit",			# PE750

		"0x10761458,0x10768086,0x00" => "Gigabyte/Intel 82541GI Gigabit",		# Gigabyte GA-7A8DRH

		"0x????????,0x10768086,0x05" => "Intel 82541PI Gigabit",			#
		"0x347e8086,0x10768086,0x05" => "Intel 82541PI Gigabit",			# Intel SE7520BB2
		"0x016d1028,0x10768086,0x05" => "Dell/Intel 82541PI Gigabit",			# PE1850, PE2850
		"0x019a1028,0x10768086,0x05" => "Dell/Intel 82541PI Gigabit",			# SC1425
		"0x30a18086,0x10768086,0x05" => "Intel 82541PI Gigabit",			# Intel SE7230NH1LX
		"0x34398086,0x10768086,0x05" => "Intel 82541PI Gigabit",			# Intel SE7520BD2
		"0x34448086,0x10768086,0x05" => "Intel 82541PI Gigabit",			# Intel SE7320VP2
		"0x34508086,0x10768086,0x05" => "Intel 82541PI Gigabit",			# Intel SE7320VP2
		"0x348d8086,0x10768086,0x05" => "Intel 82541GI Gigabit",			# Intel AspenHill
		"0x01651028,0x10768086,0x00" => "Dell/Intel 82541GI Gigabit",			# PE750

		"0x????????,0x10798086,0x03" => "Intel 82546GB Gigabit",
		"0x10798086,0x10798086,0x03" => "Intel 82546GB Gigabit",			# Intel SE7520JR2
		"0x11798086,0x10798086,0x03" => "Intel 82546GB Gigabit",			# Intel PRO/1000 MT Server Adapter
		"0x118a8086,0x10798086,0x03" => "Intel 82546GB Gigabit",			# Intel Server Adapter

		"0x018a1028,0x107b8086,0x03" => "Dell/Intel 82546GB Gigabit",			# PE1855

		"0x10828086,0x107d8086,0x06" => "Intel 82572EI Gigabit",			# Intel PRO/1000 PT Server Adapter

		"0x????????,0x108b8086,0x03" => "Intel 82573V Gigabit",
		"0x349b8086,0x108b8086,0x03" => "Intel 82573V Gigabit",				# Intel Coretta, S3000PT
		"0x00008086,0x108b8086,0x03" => "Intel 82573V Gigabit",				# Quanta QSSC-295MB, DCS S45
		"0x00001458,0x108b8086,0x03" => "Gigabyte/Intel 82573V Gigabit",		# Gigabyte R116
		"0x521110f1,0x108b8086,0x03" => "Tyan/Intel 82573V Gigabit",			# Tyan S5376

		"0x????????,0x108c8086,0x03" => "Intel 82573E Gigabit",
		"0x30a28086,0x108c8086,0x03" => "Intel 82573E Gigabit",				# Intel SE7230NH1LX
		"0x348d8086,0x108c8086,0x03" => "Intel 82573E Gigabit",				# Intel AspenHill
		"0x349b8086,0x108c8086,0x03" => "Intel 82573E Gigabit",				# Intel Coretta, S3000PT
		"0x108c15d9,0x108c8086,0x03" => "Supermicro/Intel 82573E Gigabit",		# SM X6DLP, SM X7DCL-3, SM X7DCT

		"0x????????,0x10968086,0x01" => "Intel 82563EB Gigabit",
		"0x00008086,0x10968086,0x01" => "Intel 82563EB Gigabit",			# Tyan S5397
		"0x346c8086,0x10968086,0x01" => "Intel 82563EB Gigabit",			# Intel S5000PAL
		"0x34768086,0x10968086,0x01" => "Intel 82563EB Gigabit",			# Intel S5000PSL
		"0x34848086,0x10968086,0x01" => "Intel 82563EB Gigabit",			# Intel S5000VSA
		"0x34d48086,0x10968086,0x01" => "Intel 82563EB Gigabit",			# Intel S5400SF
		"0x000015d9,0x10968086,0x01" => "Supermicro/Intel 82563EB Gigabit",		# SM X7DB8, X7DVL-E, X7DVL-L
		"0x109615d9,0x10968086,0x01" => "Supermicro/Intel 82563EB Gigabit",		# SM X7DBR, X7DBi+
		"0x8949152d,0x10968086,0x01" => "Quanta/Intel 82563EB Gigabit",			# DCS S29, DCS S58
		"0x34cc8086,0x10968086,0x01" => "Intel 82563EB Gigabit",			# Intel Tigerton
		"0x00231170,0x10968086,0x01" => "Inventec/Intel 82563EB Gigabit",		# eslim, Inventec Seabream
		"0x003a1170,0x10968086,0x01" => "Inventec/Intel 82563EB Gigabit",		# Inventec 5220
		"0x83361033,0x10968086,0x01" => "NEC/Intel 82563EB Gigabit",			# NEC Express5800/120Ri-2
		"0x82171043,0x10968086,0x01" => "Asus/Intel 82563EB Gigabit",			# IBM dx340

		"0x00008086,0x10988086,0x01" => "Intel 82563EB Gigabit",			# SM B7DBE

		"0x????????,0x109a8086,0x00" => "Intel 82573L Gigabit",
		"0x00008086,0x109a8086,0x00" => "Intel 82573L Gigabit",				# Rackable S45
		"0x109a15d9,0x109a8086,0x00" => "Supermicro/Intel 82573L Gigabit",		# SM X6DLP, SM PDSML, SM X7DCA-L, SM X7DCT
		"0x80301462,0x109a8086,0x00" => "MSI/Intel 82573L Gigabit",			# MSI GM965
		"0x83661033,0x109a8086,0x00" => "NEC/Intel 82573L Gigabit",			# NEC Express5800/i120Ra-e1

		"0x10a48086,0x10a48086,0x06" => "Intel 82571EB Quad Gigabit",			# PRO/1000 PT Quad Port Server Adapter

		"0x????????,0x10a78086,0x02" => "Intel 82575EB Gigabit",
		"0x34ce8086,0x10a78086,0x02" => "Intel 82575EB Gigabit",			# Intel X38ML
		"0x10a88086,0x10a78086,0x02" => "Intel 82575EB Gigabit",			# Intel S7000FC4UR
		"0x10a715d9,0x10a78086,0x02" => "Supermicro/Intel 82575EB Gigabit",		# SM X8DTN

		"0x11998086,0x10b58086,0x03" => "Intel 82546GB Quad Gigabit",			# PRO/1000 GT Quad Port Server Adapter

		"0x704a103c,0x10b98086,0x06" => "HP NC110T/Intel 82572EI Gigabit",		# HP NC110T adapter

		"0x704b103c,0x10bc8086,0x06" => "HP NC364T/Intel 82571EB Quad Gigabit",		# HP NC364T adapter

		"0x????????,0x10bd8086,0x02" => "Intel 82566DM-2 Gigabit",
		"0x00008086,0x10bd8086,0x02" => "Intel 82566DM-2 Gigabit",			# DCS S45, Quanta QSSC-295MB
		"0x281e103c,0x10bd8086,0x02" => "HP/Intel 82566DM-2 Gigabit",			# HP DC5800
		"0x83661033,0x10bd8086,0x02" => "NEC/Intel 82566DM-2 Gigabit",			# NEC Express5800/i120Ra-e1

		"0x145a8086,0x10d68086,0x02" => "Intel 82575GB Quad Gigabit",			# VT Quad Port Server Adapter

		# http://download.intel.com/design/network/manuals/8255X_OpenSDM.pdf
		"0x????????,0x12298086,0x01" => "Intel 82557 A 10/100",
		"0x00000000,0x12298086,0x01" => "Intel 82557 A 10/100",

		"0x????????,0x12298086,0x02" => "Intel 82557 B 10/100",
		"0x00018086,0x12298086,0x02" => "Intel 82557 B 10/100",

		"0x????????,0x12298086,0x03" => "Intel 82557 C 10/100",

		"0x????????,0x12298086,0x04" => "Intel 82558 A 10/100",

		"0x????????,0x12298086,0x05" => "Intel 82558 B 10/100",
		"0x00088086,0x12298086,0x05" => "Intel 82558 B 10/100",
		"0x00098086,0x12298086,0x05" => "Intel 82558 B 10/100",				# adapter
		"0x10f08086,0x12298086,0x05" => "Intel 82558 B 10/100",				# PRO/100+ Dual Port Adapter
		"0xb0dd0e11,0x12298086,0x05" => "Compaq NC3131/Intel 82558 B 10/100",		# Compaq 6400R

		"0x????????,0x12298086,0x06" => "Intel 82559 A 10/100",

		"0x????????,0x12298086,0x07" => "Intel 82559 B 10/100",

		"0x????????,0x12298086,0x08" => "Intel 82559 C 10/100",
		"0x00000000,0x12298086,0x08" => "Intel 82559 C 10/100",				# Compaq ML370
		"0x000b8086,0x12298086,0x08" => "Intel 82559 C 10/100",
		"0x000c8086,0x12298086,0x08" => "Intel 82559 C 10/100",				# Tyan S2510
		"0x100c8086,0x12298086,0x08" => "Intel 82559 C 10/100",				# SM P3TSSR, SM 370SSR
		"0x12298086,0x12298086,0x08" => "Intel 82559 C 10/100",				# Intel STL2
		"0x30008086,0x12298086,0x08" => "Intel 82559 C 10/100",				# Quanta SU2, Intel TR440BX
		"0x30108086,0x12298086,0x08" => "Intel 82559 C 10/100",				# SM P3TSSR
		"0x34148086,0x12298086,0x08" => "Intel 82559 C 10/100",				# Intel SAI2
		"0x009b1028,0x12298086,0x08" => "Dell/Intel 82559 C 10/100",			# PE2450, PE2550
		"0x00da1028,0x12298086,0x08" => "Dell/Intel 82559 C 10/100",			# PE1550
		"0x10ca103c,0x12298086,0x08" => "HP/Intel 82559 C 10/100",			# HP NetServer
		"0x8916152d,0x12298086,0x08" => "Quanta/Intel 82559 C 10/100",			# Quanta SU6
		"0x8917152d,0x12298086,0x08" => "Quanta/Intel 82559 C 10/100",			# Gateway 7450R, Quanta SU2-3000
		"0xb1340e11,0x12298086,0x08" => "Compaq NC3163/Intel 82559 C 10/100",		# DL320, DL360, DL380 G2, ML530 G2
		"0xb1440e11,0x12298086,0x08" => "Compaq NC3123/Intel 82559 C 10/100",		# Compaq NC3123 adapter

		"0x????????,0x12298086,0x09" => "Intel 82559ER A 10/100",
		"0x10128086,0x12298086,0x09" => "Intel 82559ER A 10/100",			# PRO/100 S Server Adapter

		"0x????????,0x12298086,0x0c" => "Intel 82550 10/100",
		"0x00408086,0x12298086,0x0c" => "Intel 82550 10/100",				# PRO/100 S Desktop Adapter

		"0x????????,0x12298086,0x0d" => "Intel 82550 10/100",
		"0x10508086,0x12298086,0x0d" => "Intel 82550 10/100",				# SM P4DP6
		"0x301a8086,0x12298086,0x0d" => "Intel 82550 10/100",				# Intel S845WD1
		"0x340f8086,0x12298086,0x0d" => "Intel 82550 10/100",				# Intel SCB20-S
		"0x34108086,0x12298086,0x0d" => "Intel 82550 10/100",				# Intel SCB20-A
		"0x34128086,0x12298086,0x0d" => "Intel 82550 10/100",				# Intel SRSH4
		"0x34188086,0x12298086,0x0d" => "Intel 82550 10/100",				# Intel SE7501BR2
		"0x34198086,0x12298086,0x0d" => "Intel 82550 10/100",				# Intel SE7500CW2
		"0x34258086,0x12298086,0x0d" => "Intel 82550 10/100",				# Intel SE7501CW2

		"0x????????,0x12298086,0x0e" => "Intel 82550 10/100",

		"0x????????,0x12298086,0x0f" => "Intel 82551 10/100",				#

		"0x????????,0x12298086,0x10" => "Intel 82551 10/100",				#
		"0x10408086,0x12298086,0x10" => "Intel 82551 10/100",				# Tyan S2722, Tyan S2882
		"0x00708086,0x12298086,0x10" => "Intel 82551 10/100",				#

		"0x00120e11,0x24498086,0x01" => "Compaq/Intel 82801BA ICH2 10/100 VM",
		"0x00120e11,0x24498086,0x03" => "Compaq/Intel 82801BA ICH2 10/100 VM",		# HP D500
		"0x30108086,0x24498086,0x03" => "Intel 82801BA ICH2 10/100 VE",			# SM 370SSR
		"0x20101749,0x24498086,0x42" => "RLX/Intel 82801BA ICH2 10/100",		# RLX ServerBlade
		"0x308d8086,0x27dc8086,0x01" => "Intel 82562GX (82801G ICH7) 10/100",		# Intel D945GTP
		"0x30001458,0x294c8086,0x02" => "Gigabyte/Intel 82566DC-2 Gigabit",		# Gigabyte R116

		# wireless
		"0x27228086,0x42208086,0x05" => "Intel 2200BG Wireless",			# Dell Latitude D610
		"0x12f6103c,0x42208086,0x05" => "HP/Intel 2200BG Wireless",			# HP NC6220
		"0x135b103c,0x42228086,0x02" => "HP/Intel 3945ABG Wireless",			# HP NC6400
		"0x135c103c,0x42228086,0x02" => "HP/Intel 3945ABG Wireless",			# HP NC6400
		"0x10008086,0x42298086,0x61" => "Intel 4965AG Wireless",			# HP 6910p
		"0x10018086,0x42298086,0x61" => "Intel 4965AG Wireless",			# HP 6910p

		# infiniband
		"0x627415b3,0x627415b3,0x20" => "Mellanox MT25204 InfiniHost III",
		"0x627815b3,0x627815b3,0x20" => "Mellanox MT25208 InfiniHost III",
	},

	'storage' => {
		"0x0944103c,0x8033104c,0x00" => "HP/TI PCIxx21 SD Flash Reader",		# NC6220
		"0x30ad103c,0x803b104c" => "HP/TI 5-in-1 Multimedia Card Reader",		# NC6400

		"0x40500e11,0x00461011,0x01" => "Compaq Smart Array 4200",			# ML530 G2
		"0x40580e11,0x00461011,0x01" => "Compaq Smart Array 431",			# ML370

		"0x40800e11,0xb1780e11" => "Compaq Smart Array 5i",			# DL560 G1, DL585 G1
		"0x40830e11,0xb1780e11" => "Compaq Smart Array 5i",
		"0x40910e11,0x00460e11" => "HP Smart Array 6i",
		"0x40700e11,0xb0600e11" => "Compaq Smart Array 5300",
		"0x409b0e11,0x00460e11" => "HP Smart Array 642",
		"0x409c0e11,0x00460e11" => "HP Smart Array 6400",
		"0x409d0e11,0x00460e11" => "HP Smart Array 6400 EM",
		"0x001b0e11,0x00121000" => "Compaq/Symbios 895A Ultra2",
		"0x3225103c,0x3220103c" => "HP Smart Array P600",
		"0x3223103c,0x3230103c" => "HP Smart Array P800",			# DL360 G5
		"0x3234103c,0x3230103c" => "HP Smart Array P400",			# DL380 G5, DL385 G2
		"0x3235103c,0x3230103c" => "HP Smart Array P400i",			# DL360 G5, DL365 G1
		"0x3211103c,0x3238103c" => "HP Smart Array E200i",			# DL360 G5, HP SB600c
		"0x3212103c,0x3238103c" => "HP Smart Array E200i",			# DL360 G5
		"0x0475101e,0x1960101e" => "MegaRAID Express 500 Ultra160",
		"0x04711028,0x1960101e" => "Dell/MegaRAID PERC 3/QC",			# PE6650
		"0x04931028,0x1960101e" => "Dell/MegaRAID PERC 3/DC",
		"0x05111028,0x1960101e,0x02" => "Dell/MegaRAID CERC QC SATA/150",	# Dell 1600SC
		"0x05181028,0x19601000" => "Dell/MegaRAID PERC 4/DC",
		"0x05201028,0x19601000" => "Dell/MegaRAID PERC 4/SC",
		"0x05181000,0x19601000" => "LSILogic MegaRAID Ultra320-2",
		"0x05201000,0x19601000" => "LSILogic MegaRAID Ultra320-1",
		"0x00d11028,0x00021028,0x01" => "Dell PERC 3/Di",			# PE2550
		"0x00031028,0x00031028" => "Dell PERC 3/Si",
		"0x01211028,0x000a1028" => "Dell PERC 3/Di",				# PE2650
		"0x014a1028,0x000f1028" => "Dell PE1750/LSILogic PERC 4/Di",
	
		"0x????????,0x00131028" => "LSILogic PERC 4",
		"0x016c1028,0x00131028" => "Dell PE1850/LSILogic PERC 4e/Si",
		"0x016d1028,0x00131028" => "Dell PE2850/LSILogic PERC 4e/Di",
		"0x01701028,0x00131028" => "Dell PE6850/LSILogic PERC 4e/Di",		# PE6850
	
		"0x????????,0x00151028" => "LSILogic PERC 5",
		"0x1f011028,0x00151028" => "Dell/LSILogic PERC 5/E",
		"0x1f021028,0x00151028" => "Dell PE6950/LSILogic PERC 5/i",		# PE6950
		"0x1f031028,0x00151028" => "Dell/LSILogic PERC 5/i",			# PE2950, PE1950
	
		"0xb1430e11,0x000a1000" => "Compaq/LSILogic 1510 Ultra2",		# DL360, ML570
	
		"0x????????,0x000f1000" => "LSILogic 53C875 Ultra",
		"0x00000000,0x000f1000" => "LSILogic 53C875 Ultra",
		"0x10001000,0x000f1000" => "LSILogic 53C876 Ultra",
		"0x70040e11,0x000f1000,0x14" => "LSILogic 53C875 Ultra",		# Compaq 6400R
	
		"0x40400e11,0x00101000" => "Compaq/LSILogic 1510 Ultra2",		# DL360
		"0x60b0103c,0x00201000" => "HP/LSILogic 1010-33 Ultra160",		# HP NetServer
		"0x10001000,0x00201000" => "LSILogic 1010-33 Ultra160",			# Tyan S2510
		"0x10001000,0x00211000" => "LSILogic 1010-66 Ultra160",
	
		"0x????????,0x00301000" => "LSILogic 1030 Ultra320",
		"0x00000000,0x00301000" => "LSILogic 1030 Ultra320",			# VMware
		"0x10001000,0x00301000" => "LSILogic 1030 Ultra320",			# Tyan S4882
		"0x10101000,0x00301000" => "LSILogic 1030 Ultra320",			# Tyan S4881
		"0x10601000,0x00301000" => "LSILogic 1030 Ultra320",			# Inventec IR2300 (onboard?)
		"0x10005100,0x00301000" => "LSILogic 1030 Ultra320",			# Arima HDAMA
		"0x30e01000,0x00301000" => "LSILogic 1030 Ultra320",			# Tyan S2880
		"0x51001000,0x00301000" => "LSILogic 1030 Ultra320",			# Arima HDAMA
		"0x71301000,0x00301000" => "LSILogic 1030 Ultra320",
		"0x24501462,0x00301000" => "MSI/LSILogic 1030 Ultra320",
		"0x014a1028,0x00301000" => "Dell PE1750/LSILogic 1030 Ultra320",
		"0x016c1028,0x00301000" => "Dell PE1850/LSILogic 1030 Ultra320",
		"0x016d1028,0x00301000" => "Dell PE2850/LSILogic 1030 Ultra320",
		"0x01701028,0x00301000" => "Dell PE6850/LSILogic 1030 Ultra320",
		"0x018a1028,0x00301000" => "Dell PE1855/LSILogic 1030 Ultra320",
		"0x10401028,0x00301000" => "Dell/LSILogic 1030 Ultra320",
		"0x00da0e11,0x00301000" => "HP/LSILogic 1030 Ultra320",			# DL145 G1
		"0x00f40e11,0x00301000" => "HP/LSILogic 1030 Ultra320",			# DL140 G2
		"0x3108103c,0x00301000" => "HP/LSILogic 1030 Ultra320",
		"0x34628086,0x00301000" => "Intel/LSILogic 1030 Ultra320",		# Intel SE7520BD2
		"0x34358086,0x00301000" => "Intel/LSILogic 1030 Ultra320",		# Intel SE7520JR2
		"0x03011014,0x00301000" => "IBM/LSILogic 1030 Ultra320",		# IBM eServer BladeCenter LS20
		"0x001017c2,0x00301000" => "Newisys/LSILogic 1030 Ultra320",		# V20z
		"0x002017c2,0x00301000" => "Newisys/LSILogic 1030 Ultra320",		# V40z
	
		"0x10051000,0x04131000" => "MegaRAID SAS 8300XLP",
	
		"0x????????,0x00501000" => "LSILogic SAS 1064",
		"0x10001000,0x00501000,0x02" => "LSILogic SAS 1064",
	
		"0x????????,0x00541000" => "LSILogic SAS 1068",
		"0x1f061028,0x00541000" => "Dell/LSILogic SAS 5/i",			# PE2950
		"0x1f081028,0x00541000" => "Dell/LSILogic SAS 5/i",			# PE1955
		"0x1f091028,0x00541000" => "Dell/LSILogic SAS 5/i",			# PE860, SC1435
		"0x3228103c,0x00541000" => "HP/LSILogic SAS 1068",			# DL140 G3
	
		"0x????????,0x00561000" => "LSILogic SAS 1064E",
		"0x10001000,0x00561000" => "LSILogic SAS 1064E",
		"0x34798086,0x00561000" => "Intel/LSILogic SAS 1064E",			# Intel S5000PSL
		"0x894b152d,0x00561000" => "Quanta/LSILogic SAS 1064E",			# DCS-SAS
		"0x322b103c,0x00561000" => "HP/LSILogic SAS 1064E",			# DL160 G5
	
		"0x????????,0x00581000" => "LSILogic SAS 1068E",
		"0x10001000,0x00581000,0x04" => "LSILogic SAS 1068E",			# Tyan S5397 (onboard)
		"0x31401000,0x00581000,0x08" => "LSILogic SAS 1068E",			# Dell S45
		"0x1f0e1028,0x00581000" => "Dell/LSILogic SAS 6/iR",			# Dell R200 (card)
		"0x1f101028,0x00581000" => "Dell/LSILogic SAS 6/iR",			# Dell PE1950 (integrated)
		"0xa38015d9,0x00581000,0x08" => "Supermicro/LSILogic SAS 1068E",	# SM X7DCT (add-on card?)
		"0xa48015d9,0x00581000,0x08" => "Supermicro/LSILogic SAS 1068E",	# SM X7DCL-3
	
		"0xa18015d9,0x00591000" => "Supermicro/LSILogic 1068E AOC-SAS-L4iR",	# SM UIO
		"0xa48015d9,0x00591000" => "Supermicro/LSILogic 1068E AOC-SAS-L4iR",	# SM X7DCL-3
	
		"0x002f1170,0x00601000,0x03" => "Inventec/LSILogic MegaRAID SAS 1078",	# Inventec 5220
		"0x10131000,0x00601000,0x04" => "LSILogic MegaRAID SAS 1078",
		"0x34cc8086,0x00601000" => "Intel/LSILogic MegaRAID SAS 1078",		# Intel Tigerton
		"0x1f0a1028,0x00601000,0x04" => "Dell/LSILogic PERC 6/E",		# DCS
		"0x1f0c1028,0x00601000" => "Dell/LSILogic PERC 6/i",			# PE2950 III, Dell R900
		"0x03791014,0x00601000,0x04" => "IBM/LSILogic ServeRAID-MR10M",

		"0x03661014,0x00621000" => "IBM/LSILogic SAS 1078",			# IBM x3859 M2
		"0x05321000,0x04071000" => "MegaRAID 320-2X Ultra320 RAID",
		"0x00021028,0x04081000" => "Dell/LSILogic PERC 4e/DC",
		"0x10018086,0x04111000" => "Intel/MegaRAID SRCSAS18E SAS",		# Intel S5000PAL
	
		"0x00000000,0x80789004" => "Adaptec AIC-7880 Ultra",
		"0x????????,0x81789004" => "Adaptec 2940 Ultra",
		"0x00000000,0x81789004" => "Adaptec 2940 Ultra",
		"0x78819004,0x81789004" => "Adaptec 2940 Ultra",
		"0x78879004,0x87789004" => "Adaptec 2940 Ultra",			# 2940UW Pro Ultra-Wide SCSI Controller
		"0x????????,0x00109005" => "Adaptec 2940 Ultra2",
		"0x21809005,0x00109005" => "Adaptec 2940 Ultra2",
		"0xa1809005,0x00109005" => "Adaptec 2940 Ultra2",
		"0x????????,0x005f9005" => "Adaptec AIC-7896/97 Ultra2",
		"0x00539004,0x005f9005" => "Adaptec AIC-7896/97 Ultra2",
		"0x080f9005,0x005f9005" => "Adaptec AIC-7896/97 Ultra2",
		"0xe2a09005,0x00809005" => "Adaptec 29160 Ultra160",
		"0xe2a00e11,0x00809005" => "Compaq/Adaptec AIC-7892A Ultra160",
		"0x????????,0x008f9005" => "Adaptec AIC-7892P Ultra160",		#
		"0x010a1028,0x008f9005" => "Dell/Adaptec AIC-7892P Ultra160",		# PE6650
		"0x8916152d,0x008f9005" => "Quanta/Adaptec AIC-7892P Ultra160",		# Quanta SU6
		"0x8917152d,0x008f9005" => "Quanta/Adaptec AIC-7892P Ultra160",		# Quanta SU2-3000
		"0xf6209005,0x00c09005" => "Adaptec 3960D Ultra160",
		"0xf6200e11,0x00c09005,0x01" => "Compaq/Adaptec AIC-7899A Ultra160",	# ML530 G2
		"0x00c51028,0x00c59005" => "Dell/Adaptec RAID subsystem HBA",		# PE2450, PE2550, PE2650 (noop SCSI channels of a Dell PERC card, see /sys/dev/aac/aac_pci.c)

		"0x????????,0x00cf9005" => "Adaptec AIC-7899 Ultra160",
		"0xffffffff,0x00cf9005" => "Adaptec AIC-7899 Ultra160",
		"0xffff9005,0x00cf9005" => "Adaptec AIC-7899 Ultra160",
		"0x00cf8086,0x00cf9005" => "Intel/Adaptec AIC-7899 Ultra160",
		"0x340f8086,0x00cf9005" => "Intel/Adaptec AIC-7899 Ultra160",		# Intel SCB20-S
		"0x34158086,0x00cf9005" => "Intel/Adaptec AIC-7899 Ultra160",		# Intel SE7500WV2
		"0x00a61028,0x00cf9005" => "Dell PE2450/Adaptec AIC-7899 Ultra160",
		"0x00d11028,0x00cf9005" => "Dell PE2550/Adaptec AIC-7899 Ultra160",
		"0x00df1028,0x00cf9005,0x01" => "Dell/Adaptec AIC-7899 Ultra160",	# PE1550
		"0x011b1028,0x00cf9005" => "Dell PE1650/Adaptec AIC-7899 Ultra160",
		"0x01211028,0x00cf9005" => "Dell PE2650/Adaptec AIC-7899 Ultra160",
		"0x900515d9,0x00cf9005" => "Supermicro/Adaptec AIC-7899 Ultra160",	# SM P4DP6
		"0x5539d915,0x00cf9005" => "Supermicro/Adaptec AIC-7899 Ultra160",	# SM P4DLR

		"0x00609005,0x80009005" => "Adaptec ASC-29320A Ultra320",
		"0x91661462,0x800f9005" => "MSI/Adaptec AIC-7901 Ultra320",		# MSI ???
		"0x005e9005,0x801d9005" => "Adaptec AIC-7902B Ultra320",
		"0x90059005,0x801d9005" => "Adaptec AIC-7902B Ultra320",
		"0xb0101458,0x801d9005" => "Gigabyte/Adaptec AIC-7902B Ultra320",	# Gigabyte GA-7A8DRH
		"0x????????,0x801f9005" => "Adaptec AIC-7902 Ultra320",
		"0x34128086,0x801f9005,0x03" => "Intel/Adaptec AIC-7902 Ultra320",	# Intel SRSH4
		"0x341a8086,0x801f9005" => "Intel/Adaptec AIC-7902 Ultra320",		# Intel SE7501WV2S
		"0x90059005,0x801f9005" => "Adaptec AIC-7902 Ultra320",
		"0xffff9005,0x801f9005" => "Adaptec AIC-7902 Ultra320",			# Tyan S2881
		"0x01681028,0x80959005" => "Dell/Adaptec ASC-39320 Ultra320 RAID",	# SC1425
		"0x005e9005,0x809d9005" => "Adaptec AIC-7902B Ultra320 RAID",
		"0xb0101458,0x809d9005" => "Gigabyte/Adaptec AIC-7902B Ultra320 RAID",	# Gigabyte GA-7A8DRH
		"0x90059005,0x809f9005" => "Adaptec AIC-7902 Ultra320 RAID",
		"0x02859005,0x02859005" => "Adaptec 2200S Ultra320 RAID",
		"0x02869005,0x02859005" => "Adaptec 2120S Ultra320 RAID",
		"0x02911028,0x02859005" => "Dell/Adaptec CERC SATA 2",			# PE850
		"0x02ca15d9,0x02859005" => "Supermicro/Adaptec AOC-USAS-S8iR",		# SM UIO
		"0x608015d9,0x041e9005" => "Supermicro/Adaptec 9410W SAS",		# SM X7DB8?
		"0x928015d9,0x041e9005" => "Supermicro/Adaptec 9410W SAS",		# SM X7DBR-3
	
		"0x100013c1,0x100013c1" => "3ware ATA-RAID",
		"0x100113c1,0x100113c1" => "3ware 7xxx/8xxx ATA/SATA RAID",
		"0x100213c1,0x100213c1" => "3ware 9xxx SATA RAID",
		"0x100313c1,0x100313c1" => "3ware 9550X SATA RAID",
	
		"0xc0331044,0xa5111044" => "DPT/Adaptec 2000S Ultra160 RAID",
		"0xc05a1044,0xa5011044" => "DPT/Adaptec 2400A ATA RAID",
	
		"0x00541069,0x00501069" => "Mylex AcceleRAID 160",
	
		"0x00091077,0x23001077" => "QLogic QLA2300 2G FC",
		"0x01061077,0x23001077,0x01" => "QLogic QLA2300 2G FC",

		"0x01001077,0x23121077" => "QLogic ISP2312 2G FC",
		"0x01011077,0x23121077" => "QLogic ISP2312 2G FC",
		"0x01021077,0x23121077" => "QLogic ISP2312 2G FC",
		"0x010d1077,0x23121077,0x02" => "QLogic ISP2312 2G FC",

		"0x01331077,0x24221077" => "QLogic ISP2422 4G FC",
		"0x01341077,0x24221077" => "QLogic ISP2422 4G FC",

		"0x01371077,0x24321077,0x03" => "QLogic ISP2432 4G FC",
		"0x01381077,0x24321077" => "QLogic ISP2432 4G FC",
		"0x7040103c,0x24321077,0x02" => "HP/QLogic ISP2432 4G FC",
		"0x7041103c,0x24321077" => "HP/QLogic ISP2432 4G FC",
		"0x7041103c,0x24321077" => "HP/QLogic ISP2432 4G FC",

		"0x8471101e,0x12161077" => "QLogic ISP12160 Ultra160 RAID",		# PE6650
		"0x8493101e,0x12161077" => "QLogic ISP12160 Ultra160 RAID",		# PE2650
	
		"0xf0a510df,0xf0a510df" => "Emulex LP1050 2G FC",			# Emulex Thor LightPulse
		"0xf90010df,0xf90010df" => "Emulex LP9000 2G FC",
		"0xfa0010df,0xfa0010df" => "Emulex LP10000 2G FC",			# Emulex Thor-X LightPulse
	
		"0x72119005,0x564a9005" => "Adaptec iSCSI ASA-7211",
	
		"0xa48015d9,0x82131283" => "Supermicro/ITE IT8213F ATA/133",

		"0x82121043,0x612111ab,0xb1" => "Asus/Marvell 88SE6121 ATA/133 + SATA/300",	# Asus P5Q-Deluxe

		"0x2368197b,0x2368197b,0x00" => "JMicron JMB368 ATA/133",		# SM X8DTN
		"0xb0001458,0x2368197b,0x00" => "Gigabyte/JMicron JMB368 ATA/133",
	
		"0x3009103c,0x43761002" => "HP/ATI ATA",				# HP DX5150
		"0x3009103c,0x43791002" => "HP/ATI 4379 SATA/150",			# HP DX5150
	
		"0x807a1043,0x55131039" => "Asus/SiS 5513 ATA/100",			# Asus P4SGX-MX
	
		"0x3024103c,0x53371106,0x07" => "HP/VIA VT8237S SATA/300",		# HP DX2255

		"0x00000000,0x05711106" => "VIA VT82C686 ATA/66",			# Asus CUV4X
		"0x3024103c,0x05711106" => "HP/VIA VT82C686 ATA/66",			# HP DX2255
		"0x50021458,0x05711106" => "Gigabyte/VIA VT82C686 ATA/66",
	
		"0x3024103c,0x05911106" => "HP/VIA VT8237A SATA/150",			# HP DX2255
	
		"0xb0031458,0x31491106" => "Gigabyte/VIA VT6420 SATA/150 RAID",		# Gigabyte EP45-DS3R
	
		"0x????????,0x74691022" => "AMD-8111 ATA/133",				#
		"0x001017c2,0x74691022" => "Newisys/AMD-8111 ATA/133",			# V20z
		"0x002017c2,0x74691022" => "Newisys/AMD-8111 ATA/133",			# V40z
		"0x3016161f,0x74691022" => "Arima/AMD-8111 ATA/133",			# Arima HDAMA
		"0x2b801022,0x74691022" => "AMD-8111 ATA/133",				# Tyan S4882
		"0x36c01022,0x74691022" => "AMD-8111 ATA/133",				# DL145 G1
		"0x74691022,0x74691022" => "AMD-8111 ATA/133",				# Tyan S2882, Tyan S2881, Tyan S2880, MSI 8480000
		"0x74691462,0x74691022" => "MSI/AMD-8111 ATA/133",			# MSI 8480000
		"0x32040e11,0x74691022" => "Compaq/AMD-8111 ATA/133",			# DL385, DL585
	
		"0x4d33105a,0x0d30105a,0x02" => "Promise PDC20265 ATA/100",		# FastTrak100 Adapter
		"0x6619105a,0x3373105a" => "Promise PDC20378 SATA/150",			# Tyan S2880
		"0x34108086,0x4d30105a" => "Intel/Promise PDC20267 ATA/100",		# Intel SCB20-A
		"0x06801095,0x06801095" => "SiI 0680A ATA/133",				# Silicon Image
		"0x61141095,0x31141095" => "SiI 3114 SATA/150 RAID",			# Arima HDAMA
		"0x31141095,0x31141095" => "SiI 3114 SATA/150 RAID",			# Tyan S2882, Tyan S2881
		"0x31248086,0x31241095" => "SiI 3124 SATA/150 RAID",			# Intel SE7520BB2
		"0x71248086,0x31241095" => "SiI 3124 SATA/150 RAID",			# Intel SE7520BB2
		"0x00450e11,0x06491095" => "Compaq/CMD 649 ATA/100",			# DL320
		"0x007e0e11,0x06491095" => "Compaq/CMD 649 ATA/100",			# DL320 G2
		"0x00000000,0x02111166" => "ServerWorks OSB4 ATA/33",			# DL320, DL360, DL380 G2, ML570, Quanta SU2-3000, PE2550, Tyan S2510
	
		"0x????????,0x02121166" => "ServerWorks CSB5 ATA/100",			#
		"0x02121166,0x02121166" => "ServerWorks CSB5 ATA/100",			# DL360 G2, DL360 G3, DL380 G3, PE6650, DL580 G2, DL560 G1
		"0x014a1028,0x02121166" => "Dell/ServerWorks CSB5 ATA/100",		# PE1750
		"0x810b1028,0x02121166" => "Dell/ServerWorks CSB5 ATA/100",		# PE1650
		"0x41351028,0x02121166,0x93" => "Dell/ServerWorks CSB5 ATA/100",	# Dell 1600SC
		"0x340f8086,0x02121166" => "Intel/ServerWorks CSB5 ATA/100",		# Intel SCB20-S
		"0x34108086,0x02121166" => "Intel/ServerWorks CSB5 ATA/100",		# Intel SCB20-A
		"0x34148086,0x02121166" => "Intel/ServerWorks CSB5 ATA/100",		# Intel SAI2
	
		"0x????????,0x02131166" => "ServerWorks CSB6 ATA/100",			#
		"0x02201166,0x02131166" => "ServerWorks CSB6 ATA/100",			# SU2-4200
		"0x02121166,0x02131166" => "ServerWorks CSB6 ATA/100",			# DL320 G2, DL140
		"0x8920107b,0x02131166,0xa0" => "Gateway/ServerWorks CSB6 ATA/100",	# SU2-4200
	
		"0x????????,0x02141166" => "ServerWorks HT-1000 ATA/100",		#
		"0x01ea1028,0x02141166" => "Dell/ServerWorks HT-1000 ATA/100",		# PE6950
		"0x01eb1028,0x02141166" => "Dell/ServerWorks HT-1000 ATA/100",		# SC1435
		"0x02051028,0x02141166" => "Dell/ServerWorks HT-1000 ATA/100",		# PE2970
		"0x320b103c,0x02141166" => "HP/ServerWorks HT-1000 ATA/100",		# DL145 G3, DL365 G1, DL385 G2
	
		"0x320b103c,0x024a1166" => "HP/ServerWorks HT-1000 SATA/150",		# DL145 G3 (native SATA mode), DL185 G5
	
		"0x????????,0x024b1166" => "ServerWorks HT-1000 SATA/150 (PATA mode)",		#
		"0x01eb1028,0x024b1166" => "Dell/ServerWorks HT-1000 SATA/150 (PATA mode)",	# SC1435
		"0x02051028,0x024b1166" => "Dell/ServerWorks HT-1000 SATA/150 (PATA mode)",	# PE2970
		"0x320b103c,0x024b1166" => "HP/ServerWorks HT-1000 SATA/150 (PATA mode)",	# DL145 G3 (PATA mode)
	
		"0x00000000,0x70108086" => "Intel 82371SB PIIX3 ATA/33",		#
		"0x00000000,0x71118086" => "Intel 82371AB PIIX4 ATA/33",		# L440GX, TR440BX
		"0x197615ad,0x71118086" => "VMware/Intel 82371AB PIIX4 ATA/33",		#
		"0x24118086,0x24118086" => "Intel ICH 82801AA ATA/66",			# 810
	
		"0x????????,0x244b8086" => "Intel 82801BA ICH2 ATA/100",		#
		"0x57448086,0x244b8086" => "Intel 82801BA ICH2 ATA/100",		# Intel S845WD1
		"0x24118086,0x244b8086" => "Intel 82801BA ICH2 ATA/100",		#
		"0x24110e11,0x244b8086" => "Compaq/Intel 82801BA ICH2 ATA/100",		# HP D500
		"0x113015d9,0x244b8086" => "Supermicro/Intel 82801BA ICH2 ATA/100",	# SM P3TSSR
		"0x010e1028,0x244b8086" => "Dell/Intel 82801BA ICH2 ATA/100",		# Dell OptiPlex GX240
		"0x00d81028,0x244b8086" => "Dell/Intel 82801BA ICH2 ATA/100",		# Dell 530
	
		"0x????????,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		#
		"0x00000000,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Tyan S2722
		"0x34158086,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Intel SE7500WV2
		"0x34168086,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Intel SE7500WV2A
		"0x34188086,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Intel SE7501BR2
		"0x34198086,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Intel SE7500CW2
		"0x341a8086,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Intel SE7501WV2S
		"0x341b8086,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Intel SE7501WV2A
		"0x34258086,0x248b8086" => "Intel 82801CA ICH3 ATA/100",		# Intel SE7501CW2
		"0x00001749,0x248b8086" => "RLX/Intel 82801CA ICH3 ATA/100",		# RLX ServerBlade
		"0x348015d9,0x248b8086" => "Supermicro/Intel 82801CA ICH3 ATA/100",	# SM P4DP6, SM X5DP8
	
		"0x????????,0x24cb8086" => "Intel ICH4 82801DB ATA/100",		#
		"0x00b80e11,0x24cb8086" => "Compaq/Intel ICH4 82801DB ATA/100",		# HP D510
		"0x00b90e11,0x24cb8086" => "Compaq/Intel ICH4 82801DB ATA/100",		# HP D510
		"0x01381028,0x24cb8086,0x01" => "Dell/Intel ICH4 82801DB ATA/100",	# Dell SX260
		"0x368015d9,0x24cb8086" => "Supermicro/Intel ICH4 82801DB ATA/100",	# SM P4SGR
		"0x80891043,0x24cb8086,0x02" => "Asus/Intel ICH4 82801DB ATA/100",	# Asus P4GE-MX
	
		"0x????????,0x24d18086" => "Intel ICH5 82801EB SATA/150",		#
		"0x00000000,0x24d18086" => "Intel ICH5 82801EB SATA/150",		# NEC Express5800/120Re-1
		"0x34288086,0x24d18086" => "Intel ICH5 82801EB SATA/150",		# Intel S875WP1
		"0x34398086,0x24d18086" => "Intel ICH5 82801EB SATA/150",		# Intel SE7520BD2
		"0x343b8086,0x24d18086" => "Intel ICH5 82801EB SATA/150",		# Intel SE7520BD2S
		"0x347e8086,0x24d18086" => "Intel ICH5 82801EB SATA/150",		# Intel SE7520BB2
		"0x3208103c,0x24d18086" => "HP/Intel ICH5 82801EB SATA/150",		# DL140 G2
		"0x12bc103c,0x24d18086" => "HP/Intel ICH5 82801EB SATA/150",		# HP D530
		"0x12bd103c,0x24d18086,0x02" => "HP/Intel ICH5 82801EB SATA/150",	# HP ??
		"0x01511028,0x24d18086" => "Dell/Intel ICH5 82801EB SATA/150",		# Dell GX270
		"0x01561028,0x24d18086,0x02" => "Dell/Intel ICH5 82801EB SATA/150",	# Dell Precision 360
		"0x019a1028,0x24d18086" => "Dell/Intel ICH5 82801EB SATA/150",		# SC1425
		"0xb0021458,0x24d18086" => "Gigabyte/Intel ICH5 82801EB SATA/150",	# Gigabyte 9ILDR
	
		"0x????????,0x24db8086" => "Intel ICH5 82801EB ATA/100",		#
		"0x000c1170,0x24db8086" => "Inventec/Intel ICH5 82801EB ATA/100",	# Inventec IR2100
		"0x34288086,0x24db8086" => "Intel ICH5 82801EB ATA/100",		# Intel S875WP1
		"0x347e8086,0x24db8086" => "Intel ICH5 82801EB ATA/100",		# Intel SE7520BB2
		"0x34398086,0x24db8086" => "Intel ICH5 82801EB ATA/100",		# Intel SE7520BD2
		"0x10798086,0x24db8086" => "Intel ICH5 82801EB ATA/100",		# Intel SE7520JR2
		"0x12bc103c,0x24db8086" => "HP/Intel ICH5 82801EB ATA/100",		# HP D530, DC5000
		"0x12bd103c,0x24db8086,0x02" => "HP/Intel ICH5 82801EB ATA/100",	# HP ??
		"0x3201103c,0x24db8086" => "HP/Intel ICH5 82801EB ATA/100",		# DL580 G4
		"0x3208103c,0x24db8086" => "HP/Intel ICH5 82801EB ATA/100",		# DL140 G2
		"0x01511028,0x24db8086" => "Dell/Intel ICH5 82801EB ATA/100",		# Dell GX270
		"0x01561028,0x24db8086,0x02" => "Dell/Intel ICH5 82801EB ATA/100",	# Dell Precision 360
		"0x016c1028,0x24db8086" => "Dell/Intel ICH5 82801EB ATA/100",		# PE1850
		"0x016d1028,0x24db8086" => "Dell/Intel ICH5 82801EB ATA/100",		# PE2850
		"0x01701028,0x24db8086" => "Dell/Intel ICH5 82801EB ATA/100",		# PE6850
		"0x019a1028,0x24db8086" => "Dell/Intel ICH5 82801EB ATA/100",		# SC1425
		"0x32010e11,0x24db8086" => "Compaq/Intel ICH5 82801EB ATA/100",		# DL380 G4
		"0x50061458,0x24db8086" => "Gigabyte/Intel ICH5 82801EB ATA/100",	# Gigabyte 9ILDR
	
		"0x019a1028,0x24df8086" => "Dell/Intel ICH5R 82801ER SATA/150",		# SC1425
	
		"0x????????,0x26518086" => "Intel ICH6 82801FB SATA/150",
		"0x3005103c,0x26518086" => "HP/Intel ICH6 82801FB SATA/150",		# HP DC7100
		"0x300a103c,0x26518086" => "HP/Intel ICH6 82801FB SATA/150",		# HP DX6120
		"0x300c103c,0x26518086" => "HP/Intel ICH6 82801FB SATA/150",		# HP DC5100
		"0x300d103c,0x26518086" => "HP/Intel ICH6 82801FB SATA/150",		# HP DC5100 (PT005AW)
		"0x301e103c,0x26518086" => "HP/Intel ICH6 82801FB SATA/150",		# HP DX2180
		"0x301f103c,0x26518086" => "HP/Intel ICH6 82801FB SATA/150",		# HP D290
		"0x41478086,0x26518086" => "Intel ICH6 82801FB SATA/150",		# Intel D915GAG
		"0x4c588086,0x26518086" => "Intel ICH6 82801FB SATA/150",		# Intel D915GUX
		"0x01791028,0x26518086" => "Dell/Intel ICH6 82801FB SATA/150",		# Dell GX280
	
		"0x32010e11,0x26528086" => "Compaq/Intel ICH6R 82801FR SATA/150",	# DL320 G3

		"0x01821028,0x26538086,0x03" => "Dell/Intel ICH6M 82801FBM SATA/150",	# Dell Latitude D610
	
		"0x????????,0x266f8086" => "Intel ICH6 82801FB ATA/100",
		"0x32010e11,0x266f8086" => "Compaq/Intel ICH6 82801FB ATA/100",		# DL320 G3
		"0x3005103c,0x266f8086" => "HP/Intel ICH6 82801FB ATA/100",		# HP DC7100
		"0x300a103c,0x266f8086" => "HP/Intel ICH6 82801FB ATA/100",		# HP DX6120
		"0x300c103c,0x266f8086" => "HP/Intel ICH6 82801FB ATA/100",		# HP DC5100
		"0x300d103c,0x266f8086" => "HP/Intel ICH6 82801FB ATA/100",		# HP DC5100 (PT005AW)
		"0x301e103c,0x266f8086" => "HP/Intel ICH6 82801FB ATA/100",		# HP DX2180
		"0x301f103c,0x266f8086" => "HP/Intel ICH6 82801FB ATA/100",		# HP D290
		"0x0944103c,0x266f8086,0x03" => "HP/Intel ICH6 82801FB ATA/100",	# HP NC6220
		"0x41478086,0x266f8086" => "Intel ICH6 82801FB ATA/100",		# Intel D915GAG
		"0x4c588086,0x266f8086" => "Intel ICH6 82801FB ATA/100",		# Intel D915GUX
		"0x01791028,0x266f8086" => "Dell/Intel ICH6 82801FB ATA/100",		# Dell GX280
	
		"0x????????,0x27c08086" => "Intel ICH7 82801GB SATA/300",		#
		"0x348f8086,0x27c08086" => "Intel ICH7 82801GB SATA/300",		# Intel Coretta
		"0x349a8086,0x27c08086" => "Intel ICH7 82801GB SATA/300",		# Intel S3000PT
		"0x349b8086,0x27c08086" => "Intel ICH7 82801GB SATA/300",		# Intel S3000PT
		"0x348d8086,0x27c08086" => "Intel ICH7 82801GB SATA/300",		# Intel AspenHill
		"0x464c8086,0x27c08086,0x01" => "Intel ICH7 82801GB SATA/300",		# Intel D945GCLF
		"0x544e8086,0x27c08086" => "Intel ICH7 82801GB SATA/300",		# Intel D945GTP
		"0x3011103c,0x27c08086" => "HP/Intel ICH7 82801GB SATA/300",		# HP DC7600
		"0x31fe103c,0x27c08086" => "HP/Intel ICH7 82801GB SATA/300",		# DL320 G5
		"0x3022103c,0x27c08086" => "HP/Intel ICH7 82801GB SATA/300",		# HP DX2280
		"0x00000000,0x27c08086" => "HP/Intel ICH7 82801GB SATA/300",		# DL320s
		"0x01ad1028,0x27c08086,0x01" => "Dell/Intel ICH7 82801GB SATA/300",	# Dell GX620
		"0x01b61028,0x27c08086" => "Dell/Intel ICH7 82801GB SATA/300",		# PE850
		"0x01e61028,0x27c08086" => "Dell/Intel ICH7 82801GB SATA/300",		# PE860
		"0x02fd1014,0x27c08086" => "IBM/Intel ICH7 82801GB SATA/300",		# IBM 306m
		"0x858015d9,0x27c08086" => "Supermicro/Intel ICH7 82801GB SATA/300",	# SM PDSML
		"0x798015d9,0x27c08086" => "Supermicro/Intel ICH7 82801GB SATA/300",	# SM PDSMi
		"0xb58015d9,0x27c08086" => "Supermicro/Intel ICH7 82801GB SATA/300",	# SM PDSBM
		"0x00221170,0x27c08086" => "Inventec/Intel ICH7 82801GB SATA/300",	# IR1250
		"0x81901043,0x27c08086,0x01" => "Asus/Intel ICH7 82801GB SATA/300",	# Asus P5BV-C
		"0xb0021458,0x27c08086,0x01" => "Gigabyte/Intel ICH7 82801GB SATA/300",	# Gigabyte 945GCM-S2C
	
		"0x????????,0x27c18086,0x01" => "Intel ICH7 82801GR SATA/300 AHCI",
		"0x346a8086,0x27c18086,0x01" => "Intel ICH7 82801GR SATA/300 AHCI",		# Intel Allagash
		"0x858015d9,0x27c18086,0x01" => "Supermicro/Intel ICH7 82801GR SATA/300 AHCI",	# SM PDSML
		"0x81901043,0x27c18086,0x01" => "Asus/Intel ICH7 82801GR SATA/300 AHCI",	# Asus P5BV-C
	
		"0x????????,0x27c38086,0x01" => "Intel ICH7 82801GB SATA/300 RAID",
		"0x3206103c,0x27c38086,0x01" => "HP/Intel ICH7 82801GB SATA/300 RAID",	# DL320 G5

		"0x????????,0x27c58086,0x01" => "Intel ICH7 82801GBM SATA/300 AHCI",
		"0x30ad103c,0x27c58086,0x01" => "HP/Intel ICH7 82801GBM SATA/300 AHCI",	# HP NC6400

		"0x????????,0x27df8086" => "Intel ICH7 82801G ATA/100",			#
		"0x346a8086,0x27df8086" => "Intel ICH7 82801G ATA/100",			# Intel Allagash
		"0x348d8086,0x27df8086" => "Intel ICH7 82801G ATA/100",			# Intel AspenHill
		"0x348f8086,0x27df8086" => "Intel ICH7 82801G ATA/100",			# Intel Coretta
		"0x349a8086,0x27df8086" => "Intel ICH7 82801G ATA/100",			# Intel S3000PT
		"0x349b8086,0x27df8086" => "Intel ICH7 82801G ATA/100",			# Intel S3000PT
		"0x464c8086,0x27df8086,0x01" => "Intel ICH7 82801G ATA/100",		# Intel D945GCLF
		"0x544e8086,0x27df8086" => "Intel ICH7 82801G ATA/100",			# Intel D945GTP
		"0x3011103c,0x27df8086" => "HP/Intel ICH7 82801G ATA/100",		# HP DC7600
		"0x30ad103c,0x27df8086" => "HP/Intel ICH7 82801G ATA/100",		# HP NC6400
		"0x31fe103c,0x27df8086" => "HP/Intel ICH7 82801G ATA/100",		# DL320 G5
		"0x3022103c,0x27df8086" => "HP/Intel ICH7 82801G ATA/100",		# HP DX2280
		"0x01ad1028,0x27df8086,0x01" => "Dell/Intel ICH7 82801G ATA/100",	# Dell GX620
		"0x01b61028,0x27df8086" => "Dell/Intel ICH7 82801G ATA/100",		# PE850
		"0x01e61028,0x27df8086" => "Dell/Intel ICH7 82801G ATA/100",		# PE860
		"0x00221170,0x27df8086" => "Inventec/Intel ICH7 82801G ATA/100",	# IR1250
		"0x858015d9,0x27df8086" => "Supermicro/Intel ICH7 82801G ATA/100",	# SM PDSML
		"0x03811014,0x27df8086" => "IBM/Intel ICH7 82801G ATA/100",		# IBM x3850 M2
		"0x819e1043,0x27df8086,0x01" => "Asus/Intel ICH7 82801G ATA/100",	# Asus P5BV-C
		"0xb0011458,0x27df8086,0x01" => "Gigabyte/Intel ICH7 82801G ATA/100",	# Gigabyte 945GCM-S2C
	
		"0x2808103c,0x28208086" => "HP/Intel ICH8 82801H 4 Port SATA/300",	# HP DC5700

		"0x31f4103c,0x28228086,0x02" => "HP/Intel ICH8 82801H SATA/300 RAID",	# DL180 G5
		"0x28228086,0x28228086,0x02" => "Intel ICH8 82801H SATA/300 RAID",	# Dell S45, SM X8DTN

		"0x2808103c,0x28258086" => "HP/Intel ICH8 82801H 2 Port SATA/300",	# HP DC5700
	
		"0x????????,0x28288086,0x03" => "Intel ICH8M 82801HBM SATA/300",
		"0x80301462,0x28288086,0x03" => "MSI/Intel ICH8M 82801HBM SATA/300",	# MSI GM965
		"0x063ea0a0,0x28288086,0x03" => "AOpen/Intel ICH8M 82801HBM SATA/300",	# AOpen i965GMt-LA

		"0x????????,0x28298086,0x03" => "Intel ICH8M 82801HBM SATA/300 AHCI",
		"0x30c1103c,0x28298086,0x03" => "HP/Intel ICH8M 82801HBM SATA/300 AHCI",	# HP 6910p
		"0x80301462,0x28298086,0x03" => "MSI/Intel ICH8M 82801HBM SATA/300 AHCI",	# MSI GM965

		"0x????????,0x28508086,0x03" => "Intel ICH8M 82801HBM ATA/100",
		"0x30be103c,0x28508086" => "HP/Intel ICH8M 82801HBM ATA/100",		# HP 6910p
		"0x30c1103c,0x28508086,0x03" => "HP/Intel ICH8M 82801HBM ATA/100",	# HP 6910p
		"0x80301462,0x28508086,0x03" => "MSI/Intel ICH8M 82801HBM ATA/100",	# MSI GM965
		"0x063ea0a0,0x28508086,0x03" => "AOpen/Intel ICH8M 82801HBM ATA/100",	# AOpen i965GMt-LA
	
		"0x????????,0x29208086" => "Intel ICH9 82801I 4 Port SATA/300",
		"0x29208086,0x29208086" => "Intel ICH9 82801I 4 Port SATA/300",			# DCS S45
		"0x31f4103c,0x29208086" => "HP/Intel ICH9 82801I 4 Port SATA/300",		# DL180 G5, DL120 G5
		"0x31fe103c,0x29208086" => "HP/Intel ICH9 82801I 4 Port SATA/300",		# BL260c G5
		"0x023c1028,0x29208086" => "Dell/Intel ICH9 82801I 4 Port SATA/300",		# Dell R200
		"0x020f1028,0x29208086" => "Dell/Intel ICH9 82801I 4 Port SATA/300",		# Dell R300
		"0xdc8015d9,0x29208086,0x02" => "Supermicro/Intel ICH9 82801I 4 Port SATA/300",	# SM X7DCA-L
		"0xa48015d9,0x29208086" => "Supermicro/Intel ICH9 82801I 4 Port SATA/300",	# SM X7DCL
		"0x8954152d,0x29208086" => "Quanta/Intel ICH9 82801I 4 Port SATA/300",		# Quanta QSSC-295MB
		"0x8959152d,0x29208086,0x02" => "Quanta/Intel ICH9 82801I 4 Port SATA/300",	# Quanta S47
	
		"0x????????,0x29218086,0x02" => "Intel ICH9 82801IB 2 Port SATA/300",
		"0x281e103c,0x29218086,0x02" => "HP/Intel ICH9 82801IB 2 Port SATA/300",	# HP DC5800

		"0x????????,0x29228086,0x02" => "Intel ICH9 82801I 6 Port SATA/300 AHCI",
		"0x10001458,0x29228086" => "Gigabyte/Intel ICH9 82801I 6 Port SATA/300 AHCI",		# Gigabyte R116
		"0xdc8015d9,0x29228086" => "Supermicro/Intel ICH9 82801I 6 Port SATA/300 AHCI",		# SM X7DCA-L
		"0xd88015d9,0x29228086,0x02" => "Supermicro/Intel ICH9 82801I 6 Port SATA/300 AHCI",	# SM X7SBL
		"0x29228086,0x29228086,0x02" => "Intel ICH9 82801I 6 Port SATA/300 AHCI",		# DCS S45, SM X8DTN
		"0x34d08086,0x29228086,0x02" => "Intel ICH9 82801I 6 Port SATA/300 AHCI",		# Intel X38ML
		"0x537610f1,0x29228086,0x02" => "Tyan/Intel ICH9 82801I 6 Port SATA/300 AHCI",		# Tyan S5376
		"0x8954152d,0x29228086,0x02" => "Quanta/Intel ICH9 82801I 6 Port SATA/300 AHCI",	# Quanta S45
		"0x8959152d,0x29228086,0x02" => "Quanta/Intel ICH9 82801I 6 Port SATA/300 AHCI",	# Quanta S47
		"0x83661033,0x29228086,0x02" => "NEC/Intel ICH9 82801I 6 Port SATA/300 AHCI",		# NEC Express5800/i120Ra-e1
	
		"0x????????,0x29238086,0x02" => "Intel ICH9 82801IB 4 Port SATA/300 AHCI",
		"0xb0051458,0x29238086,0x02" => "Gigabyte/Intel ICH9 82801IB 4 Port SATA/300 AHCI",	# Gigabyte P35-S3G, Gigabyte G33M-S2L

		"0x????????,0x29268086" => "Intel ICH9 82801I 2 Port SATA/300",
		"0x00000000,0x29268086" => "Intel ICH9 82801I 2 Port SATA/300",			# Dell R300
		"0x020f1028,0x29268086,0x02" => "Dell/Intel ICH9 82801I 2 Port SATA/300",	# Dell R300
		"0x29268086,0x29268086" => "Intel ICH9 82801I 2 Port SATA/300",			# DCS S45
		"0x281e103c,0x29268086,0x02" => "HP/Intel ICH9 82801I 2 Port SATA/300",		# HP DC5800
		"0x31f4103c,0x29268086" => "HP/Intel ICH9 82801I 2 Port SATA/300",		# DL180 G5, DL120 G5
		"0xa48015d9,0x29268086" => "Supermicro/Intel ICH9 82801I 2 Port SATA/300",	# SM X7DCL
		"0xdc8015d9,0x29268086,0x02" => "Supermicro/Intel ICH9 82801I 2 Port SATA/300",	# SM X7DCA-L
		"0x8954152d,0x29268086" => "Quanta/Intel ICH9 82801I 2 Port SATA/300",		# Quanta QSSC-295MB
		"0x8959152d,0x29268086,0x02" => "Quanta/Intel ICH9 82801I 2 Port SATA/300",	# Quanta S47
	
		"0x????????,0x25a28086" => "Intel 6300ESB ATA/100",			#
		"0x32010e11,0x25a28086" => "Compaq/Intel 6300ESB ATA/100",		# DL360 G4
		"0x25a18086,0x25a28086" => "Intel 6300ESB ATA/100",			# Intel
		"0x34508086,0x25a28086" => "Intel 6300ESB ATA/100",			# Intel SE7320VP2
	
		"0x????????,0x25a38086" => "Intel 6300ESB SATA/150",			#
		"0x01651028,0x25a38086" => "Dell/Intel 6300ESB SATA/150",		# PE750
		"0x698015d9,0x25a38086" => "Supermicro/Intel 6300ESB SATA/150",		# SM X6DLP
	
		# http://download.intel.com/design/chipsets/datashts/31308201.pdf
		"0x????????,0x26808086" => "Intel 631xESB SATA/300",			#
		"0x346c8086,0x26808086" => "Intel 631xESB SATA/300",			# Intel S5000PAL
		"0x34768086,0x26808086" => "Intel 631xESB SATA/300",			# Intel S5000PSL
		"0x34788086,0x26808086" => "Intel 631xESB SATA/300",			# Intel S5000PSL-SAS
		"0x347a8086,0x26808086,0x09" => "Intel 631xESB SATA/300",		# Intel S5000XSL
		"0x34848086,0x26808086" => "Intel 631xESB SATA/300",			# Intel S5000VSA
		"0x34cc8086,0x26808086,0x09" => "Intel 631xESB SATA/300",		# Intel S7000FC4UR
		"0x34d48086,0x26808086" => "Intel 631xESB SATA/300",			# Intel S5400SF
		"0x808015d9,0x26808086" => "Supermicro/Intel 631xESB SATA/300",		# SM X7DB8
		"0x848015d9,0x26808086" => "Supermicro/Intel 631xESB SATA/300",		# SM X7DBR
		"0x868015d9,0x26808086" => "Supermicro/Intel 631xESB SATA/300",		# SM X7DVL-E
		"0x928015d9,0x26808086" => "Supermicro/Intel 631xESB SATA/300",		# SM X7DBR-3 (SATA)
		"0x978015d9,0x26808086" => "Supermicro/Intel 631xESB SATA/300",		# SM X7DBU
		"0xb08015d9,0x26808086" => "Supermicro/Intel 631xESB SATA/300",		# SM B7DBE
		"0xb28015d9,0x26808086" => "Supermicro/Intel 631xESB SATA/300",		# SM X7DVL-L
		"0x01b21028,0x26808086" => "Dell/Intel 631xESB SATA/300",		# PE2950
		"0x01b31028,0x26808086" => "Dell/Intel 631xESB SATA/300",		# PE1950
		"0x31f6103c,0x26808086" => "HP/Intel 631xESB SATA/300",			# DL180, DL160 G5
		"0x31fe103c,0x26808086" => "HP/Intel 631xESB SATA/300",			# DL140 G3
		"0x8949152d,0x26808086" => "Quanta/Intel 631xESB SATA/300",		# DCS S29
		"0x894b152d,0x26808086" => "Quanta/Intel 631xESB SATA/300",		# DCS S58
		"0x00231170,0x26808086" => "Inventec/Intel 631xESB SATA/300",		# Inventec Seabream
		"0x003a1170,0x26808086,0x09" => "Inventec/Intel 631xESB SATA/300",	# Inventec 5220
		"0x539710f1,0x26808086,0x09" => "Tyan/Intel 631xESB SATA/300",		# Tyan S5397
	
		"0x????????,0x26818086" => "Intel 631xESB SATA/300 AHCI",		#
		"0x347a8086,0x26818086" => "Intel 631xESB SATA/300 AHCI",		# Intel S5000XSL
		"0x34768086,0x26818086" => "Intel 631xESB SATA/300 AHCI",		# Intel S5000PSL
		"0x34788086,0x26818086" => "Intel 631xESB SATA/300 AHCI",		# Intel S5000PSL-SAS
		"0x34848086,0x26818086" => "Intel 631xESB SATA/300 AHCI",		# Intel S5000VSA
		"0x34d48086,0x26818086,0x09" => "Intel 631xESB SATA/300 AHCI",		# Intel S5400SF
		"0x808015d9,0x26818086" => "Supermicro/Intel 631xESB SATA/300 AHCI",	# SM X7DB8
		"0x848015d9,0x26818086" => "Supermicro/Intel 631xESB SATA/300 AHCI",	# SM X7DBR
		"0x868015d9,0x26818086" => "Supermicro/Intel 631xESB SATA/300 AHCI",	# SM X7DVL-E
		"0x928015d9,0x26818086" => "Supermicro/Intel 631xESB SATA/300 AHCI",	# SM X7DBR-3 (SATA)
		"0x948015d9,0x26818086" => "Supermicro/Intel 631xESB SATA/300 AHCI",	# SM X7DAL
		"0x978015d9,0x26818086" => "Supermicro/Intel 631xESB SATA/300 AHCI",	# SM X7DBU
		"0xb28015d9,0x26818086" => "Supermicro/Intel 631xESB SATA/300 AHCI",	# SM X7DVL-L
		"0x00231170,0x26818086" => "Inventec/Intel 631xESB SATA/300 AHCI",	# eslim
		"0x31f6103c,0x26818086,0x09" => "HP/Intel 631xESB SATA/300 AHCI",	# DL160 G5
		"0x31fe103c,0x26818086" => "HP/Intel 631xESB SATA/300 AHCI",		# DL140 G3
		"0x8949152d,0x26818086" => "Quanta/Intel 631xESB SATA/300 AHCI",	# DCS S29
		"0x894b152d,0x26818086" => "Quanta/Intel 631xESB SATA/300 AHCI",	# DCS S58
		"0x03251014,0x26818086,0x09" => "IBM/Intel 631xESB SATA/300 AHCI",	# IBM dx340
	
		"0x????????,0x26828086" => "Intel 631xESB SATA/300 RAID",			#
		"0x346d8086,0x26828086" => "Intel 631xESB SATA/300 RAID",			# Intel S5000PAL
		"0x34d48086,0x26828086,0x09" => "Intel 631xESB SATA/300 RAID",			# Intel S5400SF
		"0x02dd1014,0x26828086" => "IBM/Intel 631xESB SATA/300 RAID",			# IBM x3550
		"0x31f6103c,0x26828086,0x09" => "HP/Intel 631xESB SATA/300 RAID",		# DL160 G5
		"0x31fe103c,0x26828086" => "HP/Intel 631xESB SATA/300 RAID",			# DL140 G3
		"0x8949152d,0x26828086" => "Quanta/Intel 631xESB SATA/300 RAID",		# DCS S29
		"0x8950152d,0x26828086" => "Quanta/Intel 631xESB SATA/300 RAID",		# DCS S29, DCS S58
		"0x868015d9,0x26828086" => "Supermicro/Intel 631xESB SATA/300 RAID",		# SM X7DVL-E
		"0x928015d9,0x26828086,0x09" => "Supermicro/Intel 631xESB SATA/300 RAID",	# SM X7DBR-3 (SATA)
		"0x978015d9,0x26828086,0x09" => "Supermicro/Intel 631xESB SATA/300 RAID",	# SM X7DBU
		"0x978115d9,0x26828086,0x09" => "Supermicro/Intel 631xESB SATA/300 RAID",	# SM X7DBU
		"0xb28015d9,0x26828086" => "Supermicro/Intel 631xESB SATA/300 RAID",		# SM X7DVL-L
	
		"0x????????,0x269e8086" => "Intel 631xESB ATA/133",			#
		"0x00000000,0x269e8086" => "Intel 631xESB ATA/133",			# Dell R900
		"0x346c8086,0x269e8086" => "Intel 631xESB ATA/133",			# Intel S5000PAL
		"0x346d8086,0x269e8086" => "Intel 631xESB ATA/133",			# Intel S5000PAL
		"0x34768086,0x269e8086" => "Intel 631xESB ATA/133",			# Intel S5000PSL
		"0x34788086,0x269e8086" => "Intel 631xESB ATA/133",			# Intel S5000PSL-SAS
		"0x347a8086,0x269e8086,0x09" => "Intel 631xESB ATA/133",		# Intel S5000XSL
		"0x34848086,0x269e8086" => "Intel 631xESB ATA/133",			# Intel S5000VSA
		"0x34d48086,0x269e8086" => "Intel 631xESB ATA/133",			# Intel S5400SF
		"0x01b21028,0x269e8086" => "Dell/Intel 631xESB ATA/133",		# PE2950
		"0x01b31028,0x269e8086" => "Dell/Intel 631xESB ATA/133",		# PE1950
		"0x01f01028,0x269e8086,0x09" => "Dell/Intel 631xESB ATA/133",		# Dell R900
		"0x31fe103c,0x269e8086" => "HP/Intel 631xESB ATA/133",			# DL360 G5, DL380 G5, DL140 G3, DL180, DL580 G5
		"0x02dd1014,0x269e8086" => "IBM/Intel 631xESB ATA/133",			# IBM x3550
		"0x808015d9,0x269e8086" => "Supermicro/Intel 631xESB ATA/133",		# SM X7DB8
		"0x838015d9,0x269e8086,0x09" => "Supermicro/Intel 631xESB ATA/133",	# SM X7DBP
		"0x848015d9,0x269e8086" => "Supermicro/Intel 631xESB ATA/133",		# SM X7DBR
		"0x868015d9,0x269e8086" => "Supermicro/Intel 631xESB ATA/133",		# SM X7DVL-E
		"0x928015d9,0x269e8086" => "Supermicro/Intel 631xESB ATA/133",		# SM X7DBR-3 (SAS)
		"0x948015d9,0x269e8086" => "Supermicro/Intel 631xESB ATA/133",		# SM X7DAL
		"0xb18015d9,0x269e8086" => "Supermicro/Intel 631xESB ATA/133",		# SM X7DBi+
		"0xb28015d9,0x269e8086" => "Supermicro/Intel 631xESB ATA/133",		# SM X7DVL-L
		"0x8949152d,0x269e8086" => "Quanta/Intel 631xESB ATA/133",		# DCS S29
		"0x894b152d,0x269e8086" => "Quanta/Intel 631xESB ATA/133",		# DCS-SAS
		"0x00231170,0x269e8086" => "Inventec/Intel 631xESB ATA/133",		# eslim, Inventec Seabream
		"0x003a1170,0x269e8086,0x09" => "Inventec/Intel 631xESB ATA/133",	# Inventec 5220
		"0x83361033,0x269e8086" => "NEC/Intel 631xESB ATA/133",			# NEC Express5800/120Ri-2
		"0x539710f1,0x269e8086,0x09" => "Tyan/Intel 631xESB ATA/133",		# Tyan S5397
	
		"0x????????,0x3a208086,0x00" => "Intel ICH10 82801J 4 Port SATA/300",
		"0xb0021458,0x3a208086,0x00" => "Gigabyte/Intel ICH10 82801J 4 Port SATA/300",	# Gigabyte EP45-DS3R

		"0x????????,0x3a228086,0x00" => "Intel ICH10 82801J 6 Port SATA/300 AHCI",
		"0x82d41043,0x3a228086,0x00" => "Asus/Intel ICH10 82801J 6 Port SATA/300 AHCI",		# Asus P5Q-Deluxe
		"0xb0051458,0x3a228086,0x00" => "Gigabyte/Intel ICH10 82801J 6 Port SATA/300 AHCI",	# Gigabyte EP45-DS3R

		"0x????????,0x3a268086,0x00" => "Intel ICH10 82801J 2 Port SATA/300",
		"0xb0021458,0x3a268086,0x00" => "Gigabyte/Intel ICH10 82801J 2 Port SATA/300",	# Gigabyte EP45-DS3R

		"0x00261170,0x036e10de" => "Inventec/nVIDIA MCP55 ATA/133",		# IR2400
		"0x00261170,0x037f10de" => "Inventec/nVIDIA MCP55 SATA/300",		# IR2400
	
		"0x????????,0x005310de" => "nVIDIA nForce4 ATA/133",			#
		"0x3207103c,0x005310de" => "HP/nVIDIA nForce4 ATA/133",			# DL145 G2
		"0x31f8103c,0x005310de" => "HP/nVIDIA nForce4 ATA/133",			# DL585 G2
		"0x286510f1,0x005310de" => "Tyan/nVIDIA nForce4 ATA/133",		# Tyan S2865
		"0x289110f1,0x005310de" => "Tyan/nVIDIA nForce4 ATA/133",		# Tyan S4881
		"0x00161170,0x005310de" => "Inventec/nVIDIA nForce4 ATA/133",		# Inventec IR2300
		"0x00171170,0x005310de" => "Inventec/nVIDIA nForce4 ATA/133",		# Inventec IR2350
		"0x81621043,0x005310de" => "Asus/nVIDIA nForce4 ATA/133",		# Asus K8N-DRE
		"0x5348108e,0x005310de" => "Sun/nVIDIA nForce4 ATA/133",		# Sun X2100
	
		"0x????????,0x005410de" => "nVIDIA nForce4 SATA/150",			#
		"0x286510f1,0x005410de" => "Tyan/nVIDIA nForce4 SATA/150",		# Tyan S2865
		"0x289110f1,0x005410de" => "Tyan/nVIDIA nForce4 SATA/150",		# Tyan S4881
		"0x00161170,0x005410de" => "Inventec/nVIDIA nForce4 SATA/150",		# Inventec IR2300
		"0x00171170,0x005410de" => "Inventec/nVIDIA nForce4 SATA/150",		# Inventec IR2350
		"0x81621043,0x005410de" => "Asus/nVIDIA nForce4 SATA/150",		# Asus K8N-DRE
		"0x5348108e,0x005410de" => "Sun/nVIDIA nForce4 SATA/150",		# Sun X2100
	
		"0x????????,0x005510de" => "nVIDIA nForce4 SATA/150",			#
		"0x3207103c,0x005510de" => "HP/nVIDIA nForce4 SATA/150",		# DL145 G2
		"0x286510f1,0x005510de" => "Tyan/nVIDIA nForce4 SATA/150",		# Tyan S2865
		"0x289110f1,0x005510de" => "Tyan/nVIDIA nForce4 SATA/150",		# Tyan S4881
		"0x00161170,0x005510de" => "Inventec/nVIDIA nForce4 SATA/150",		# Inventec IR2300
		"0x00171170,0x005510de" => "Inventec/nVIDIA nForce4 SATA/150",		# Inventec IR2350
		"0x81621043,0x005510de" => "Asus/nVIDIA nForce4 SATA/150",		# Asus K8N-DRE
		"0x5348108e,0x005510de" => "Sun/nVIDIA nForce4 SATA/150",		# Sun X2100
	},
	'other' => {
		"0x0001177d,0x0001177d,0x01" => { "type" => "Crypto", "model" => "Cavium Nitrox XL" },
# Nitrox PX Processor - CN1620(proc) NHB(half-height)
#		"0x0001177d,0x0010177d,0x01" => { "type" => "Crypto", "model" => "Cavium Nitrox XL" },
	},
);

my(%cpu_models) = (
	# http://www.cpu-world.com/index.html

	# for AMD, many procs will share same family/model/stepping - e.g. normal, HE, EE, etc.
	# for HE info you have to read cpu string
	# http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25481.pdf

	"AMD-15,4,8" => ["Newcastle CG, 64-bit, 130nm, L2: 512KB",1,1,0],		# e.g. Athlon 64 3000+
	"AMD-15,5,0" => ["SledgeHammer, 64-bit, 130nm, L2: 1MB",1,1,0],			# e.g. 242
	"AMD-15,5,1" => ["SledgeHammer B3, 64-bit, 130nm, L2: 1MB",1,1,0],		# stepping SH7-B3, socket940 - e.g. 242, 240
	"AMD-15,5,8" => ["SledgeHammer C0, 64-bit, 130nm, L2: 1MB",1,1,0],		# stepping SH7-C0, socket940 - e.g. 848, 246, 244, 240
	"AMD-15,5,10" => ["SledgeHammer CG, 64-bit, 130nm, L2: 1MB",1,1,0],		# stepping CG, socket940 - e.g. 250, 244, 850, 240EE, 246HE
	"AMD-15,31,0" => ["64-bit, 90nm, L2: 512KB",1,1,0],				# e.g. Athlon 64 3200+
	"AMD-15,33,0" => ["64-bit, dual-core, 90nm, L2: 2MB",2,1,0],			# e.g. 875 (dual-core)
	"AMD-15,33,2" => ["64-bit, dual-core, 90nm, L2: 2MB",2,1,0],			# e.g. 275 (dual-core)
	"AMD-15,1,2" => ["64-bit, dual-core, 90nm, L2: 2MB",2,1,0],			# buggy linux kernel 2.4.22-y24 on bangles.ink?
	"AMD-15,35,2" => ["64-bit, dual-core, 90nm, L2: 2MB",2,1,0],			# e.g. 180 (dual-core)
	"AMD-15,37,1" => ["64-bit, 90nm, L2: 1MB",1,1,0],				# stepping SH-E4, socket940 (not dual-core)
	"AMD-15,65,2" => ["64-bit, dual-core, 90nm, L2: 2MB",2,1,0],			# e.g. 8220 SE
	"AMD-15,65,3" => ["64-bit, dual-core, 65nm, L2: 2MB",2,1,0],			# e.g. 2220, not sure about 65nm - YYYY
	"AMD-15,75,2" => ["Windsor F2, 64-bit, dual-core, 90nm, L2: 1MB",2,1,0],	# e.g. Athlon 64 X2 4200+

	"AMD-15,107,1" => ["64-bit, dual-core, 90nm, L2: 2MB",2,1,0],			# e.g. Athlon 64 X2 4000+

	"AMD-16,2,3" => [
		["Opteron", ["Barcelona B3, 64-bit, quad-core, 65nm, L2: 2MB, L3: 2MB",4,1,0]],	# e.g. Opteron 2352
		["Phenom", ["Agena B3, 64-bit, quad-core, 65nm, L2: 2MB, L3: 2MB",4,1,0]],	# e.g. Phenom 9850
	],

	# http://processorfinder.intel.com/
	# http://www.intel.com/design/xeon/applnots/24161830.pdf#search='intel%20cpuid'
	# http://en.wikipedia.org/wiki/Intel_Next_Generation_Microarchitecture
	# http://balusc.xs4all.nl/srv/har-cpu-int-p1.php
	# click "cpu search", select intel brand, and type f-6-5 in family, search
	# http://www.cpu-world.com/index.html

	"Intel-6,1,7" => ["PPro, 350nm, L2: 256KB/512KB",1,1,0],		# PPro
	"Intel-6,1,9" => ["PPro, 350nm, L2: 256KB/512KB/1MB",1,1,0],		# PPro

	"Intel-6,3,3" => ["Klamath C0, 350nm, L2: 512KB half-speed",1,1,0],	# P2
	"Intel-6,3,4" => ["Klamath C1, 350nm, L2: 512KB half-speed",1,1,0],	# P2
	"Intel-6,5,0" => ["Deschutes A0, 250nm, L2: 512KB half-speed",1,1,0],	# P2
	"Intel-6,5,1" => ["Deschutes A1, 250nm, L2: 512KB half-speed",1,1,0],	# P2
	"Intel-6,5,2" => ["Deschutes B0, 250nm, L2: 512KB half-speed",1,1,0],	# P2
	"Intel-6,5,3" => ["Deschutes B1, 250nm, L2: 512KB half-speed",1,1,0],	# P2

	"Intel-6,7,2" => ["Katmai, 250nm, L2: 512KB half-speed",1,1,0],		# P3
	"Intel-6,7,3" => ["Katmai, 250nm, L2: 512KB half-speed",1,1,0],		# P3
	"Intel-6,8,1" => ["Coppermine, 180nm, L2: 256KB",1,1,0],		# P3
	"Intel-6,8,3" => ["Coppermine, 180nm, L2: 256KB",1,1,0],		# P3
	"Intel-6,8,6" => ["Coppermine, 180nm, L2: 256KB",1,1,0],		# P3
	"Intel-6,9,5" => ["Banias B1, 130nm, L2: 1MB",1,1,0],			# Pentium M
	"Intel-6,8,10" => ["Coppermine, 180nm, L2: 256KB",1,1,0],		# P3
	"Intel-6,10,1" => ["Cascades A1, 180nm, L2: 2MB",1,1,0],		# Pentium III Xeon 700
	"Intel-6,11,1" => ["Tualatin, 130nm, L2: 256KB/512KB",1,1,0],		# P3
	"Intel-6,11,4" => ["Tualatin, 130nm, L2: 512KB",1,1,0],			# P3

	"Intel-6,13,8" => ["Dothan C0, 90nm, L2: 2MB",1,1,0],			# e.g. Pentium M 740

	"Intel-6,14,4" => ["Sossaman B0, dual-core, 65nm, L2: 2MB",2,1,2],		# mobile P3 (same as Yonah)
	"Intel-6,14,8" => ["Sossaman C0, dual-core, 65nm, L2: 2MB",2,1,2],		# mobile P3 (same as Yonah)
	"Intel-6,14,12" => ["Sossaman D0, dual-core, 65nm, L2: 2MB",2,1,2],		# mobile P3 (same as Yonah)
	"Intel-6,15,2" => [
		["Core", ["Allendale L2, 64-bit, dual-core, 65nm, L2: 2MB",2,1,2]],
		["Xeon", ["Conroe L2, 64-bit, dual-core, 65nm, L2: 2MB",2,1,2]],	# only 3040, 3050
	],
	"Intel-6,15,4" => ["Woodcrest, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2],
	"Intel-6,15,5" => [
		["Core", ["Conroe B1, 64-bit, dual-core, 65nm, L2: 2MB",2,1,2]],	# Core 2 Duo
		["Xeon 3", ["Conroe B1, 64-bit, dual-core, 65nm, L2: 2MB",2,1,2]],	# Core 2 Duo
		["Xeon", ["Woodcrest B1, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],	# Core 2 Duo DP
	],
	"Intel-6,15,6" => [
		["Core 2 [LTX]7", ["Merom B2, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Core 2 T5", ["Merom B2, 64-bit, dual-core, 65nm, L2: 2MB",2,1,2]],
		["Core 2 6(300|400)", ["Conroe B2, 64-bit, dual-core, 65nm, L2: 2MB",2,1,2]],
		["Core", ["Conroe B2, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Xeon 30(40|50)", ["Conroe B2, 64-bit, dual-core, 65nm, L2: 2MB",2,1,2]],
		["Xeon 3", ["Conroe B2, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Xeon", ["Woodcrest B2, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
	],
	"Intel-6,15,7" => [								# B3 stepping is only quad-core
		["Quad", ["Kentsfield B3, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],	# Core 2 Quad
		["Xeon X3", ["Kentsfield B3, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],	# Core 2 Quad
		["Xeon", ["Clovertown B3, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],	# Core 2 Xeon DP
		["Core", ["Kentsfield B3, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],	# Core 2 Quad
	],
	"Intel-6,15,9" => ["Tigerton, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2],
	"Intel-6,15,10" => [
		["Core 2 [LTX]7", ["Merom E1, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
	],
	"Intel-6,15,11" => [
		["Xeon 30", ["Conroe G0, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Xeon 51", ["Woodcrest G0, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Xeon 72", ["Tigerton-DC G0, 64-bit, dual-core, 65nm, L2: 8MB",2,1,2]],
		["Xeon X32", ["Kentsfield G0, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],
		["Xeon [LEX]53", ["Clovertown G0, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],
		["Xeon [LEX]73", ["Tigerton G0, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],
		["Xeon", ["Clovertown G0, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],
		["Solo", ["Merom G0, 64-bit, single-core, 65nm, L2: 1MB",1,1,2]],
		["Duo Mobile", ["Merom G0, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Core 2 [LTX]7", ["Merom G0, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Duo", ["Allendale G0, 64-bit, dual-core, 65nm, L2: 4MB",2,1,2]],
		["Quad", ["Kentsfield G0, 64-bit, quad-core, 65nm, L2: 8MB",4,1,2]],
	],
	"Intel-6,22,1" => [
		["Celeron", ["Merom A1, 64-bit, 65nm, L2: 1M",1,1,2]],
	],
	"Intel-6,23,1" => [
		["Xeon", ["Harpertown, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
	],
	"Intel-6,23,4" => [
		["Xeon", ["Harpertown, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
	],
	"Intel-6,23,6" => [
		["Core 2 Duo [PT]8", ["Penryn M0, 64-bit, dual-core, 45nm, L2: 3MB",2,1,3]],
		["Core 2 Duo E7", ["Wolfdale M0, 64-bit, dual-core, 45nm, L2: 3MB",2,1,3]],
		["Core 2 Duo E8", ["Wolfdale C0, 64-bit, dual-core, 45nm, L2: 6MB",2,1,3]],
		["Xeon [LEX]52", ["Wolfdale-DP C0, 64-bit, dual-core, 45nm, L2: 6MB",2,1,3]],
		["Xeon [LEX]54", ["Harpertown C0, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
		["Xeon", ["Harpertown C0, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
	],
	"Intel-6,23,7" => [
		["Quad Q93", ["Yorkfield M1, 64-bit, quad-core, 45nm, L2: 6MB",4,1,3]],
		["Quad Q9", ["Yorkfield C1, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
		["Xeon X33", ["Yorkfield C1, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
		["Xeon [LEX]52", ["Wolfdale-DP C1, 64-bit, dual-core, 45nm, L2: 6MB",2,1,3]],
		["Xeon [LEX]54", ["Harpertown C1, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
		["Xeon", ["Harpertown C1, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
	],
	"Intel-6,23,10" => [
		["Quad Q9", ["Yorkfield E0, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
		["Xeon X3320", ["Yorkfield R0, 64-bit, quad-core, 45nm, L2: 6MB",4,1,3]],
		["Xeon X33", ["Yorkfield E0, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
		["Xeon [LEX]52", ["Wolfdale-DP E0, 64-bit, dual-core, 45nm, L2: 6MB",2,1,3]],
		["Xeon [LEX]54", ["Harpertown E0, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
		["Xeon", ["Harpertown E0, 64-bit, quad-core, 45nm, L2: 12MB",4,1,3]],
	],
	"Intel-6,26,2" => [
		["Xeon", ["Nehalem B0, 64-bit, 45nm, L3: 8MB",4,2,4]],
	],
	"Intel-6,28,2" => [
		["Atom", ["Silverthorne C0, 64-bit, 45nm, L2: 512KB",1,2,3]],
	],
	"Intel-15,1,2" => ["Willamette D0, 180nm, L2: 256KB",1,1,0],		# P4
	"Intel-15,1,3" => [
		["Celeron", ["Willamette E0, 180nm, L2: 128KB",1,1,0]],		# P4
		["Pentium", ["Willamette E0, 180nm, L2: 256KB",1,1,0]],		# P4
	],
	"Intel-15,2,2" => ["Gallatin A0, 130nm, L3: 1MB",1,2,0],		# P4 Xeon MP
	"Intel-15,2,4" => [
		["Pentium", ["Northwood B0, 130nm, L2: 512KB",1,1,0]],		# P4, no support for HT
		["Xeon", ["Prestonia B0, 130nm, L2: 512KB",1,2,0]],		# P4 Xeon DP
	],
	"Intel-15,2,5" => [
		["Pentium", ["Northwood M0, 130nm, L2: 512KB",1,2,0]],		# P4, some low end GHz parts do not support HT
		["Xeon MP", ["Gallatin B1, 130nm, L3: 1MB",1,2,0]],		# P4 Xeon MP
		["Xeon", ["Prestonia M0, 130nm, L2: 512KB",1,2,0]],		# P4 Xeon DP
	],
	"Intel-15,2,6" => ["Gallatin C0, 130nm, L3: 2MB",1,2,0],		# P4 Xeon MP
	"Intel-15,2,7" => [
		["Pentium 4 3060", ["Northwood C1, 130nm, L2: 512KB",1,2,0]],	# P4, accoring to Intel only 3.06GHz parts do support HT (e.g. fe2.rd.aue)
		["Pentium", ["Northwood C1, 130nm, L2: 512KB",1,1,0]],		# P4, accoring to Intel only 3.06GHz parts do support HT (e.g. fe2.rd.aue)
		["Xeon", ["Prestonia C1, 130nm, L2: 512KB",1,2,0]],		# P4 Xeon DP
	],
	"Intel-15,2,9" => [
		["Celeron", ["Northwood D1, 130nm, L2: 128KB",1,1,0]],		# P4
		["Pentium", ["Northwood D1, 130nm, L2: 512KB",1,2,0]],		# P4, some low end GHz parts do not support HT
		["Xeon", ["Prestonia D1, 130nm, L2: 512KB",1,2,0]],		# P4 Xeon DP
	],
	"Intel-15,3,3" => [
		["Celeron", ["Prescott C0, 64-bit, 90nm, L2: 1MB",1,1,0]],	# P4
		["Pentium", ["Prescott C0, 64-bit, 90nm, L2: 1MB",1,2,0]],	# P4, some low end GHz parts do not support HT
	],
	"Intel-15,3,4" => [
		["Celeron", ["Prescott D0, 64-bit, 90nm, L2: 1MB",1,1,0]],	# P4
		["Pentium", ["Prescott D0, 64-bit, 90nm, L2: 1MB",1,2,0]],	# P4, some low end GHz parts do not support HT
		["Xeon", ["Nocona D0, 64-bit, 90nm, L2: 1MB",1,2,0]],		# P4 Xeon DP
	],
	"Intel-15,4,1" => [
		["Celeron", ["Prescott E0, 64-bit, 90nm, L2: 1MB",1,1,0]],	# P4
		["Pentium", ["Prescott E0, 64-bit, 90nm, L2: 1MB",1,2,0]],	# P4, some low end GHz parts do not support HT
		["Xeon MP", ["Cranford A0, 64-bit, 90nm, L2: 1MB",1,2,0]],	# P4 Xeon MP
		["Xeon", ["Nocona E0, 64-bit, 90nm, L2: 1MB",1,2,0]],		# P4 Xeon DP
	],
	"Intel-15,4,3" => [
		["Pentium", ["Prescott N0, 64-bit, 90nm, L2: 2MB",1,2,0]],	# P4, full support for HT
		["Xeon", ["Irwindale N0, 64-bit, 90nm, L2: 2MB",1,2,0]],	# P4 Xeon DP
	],
	"Intel-15,4,4" => ["Smithfield A0, 64-bit, dual-core, 90nm, L2: 2MB",2,1,0],	# Pentium D
	"Intel-15,4,7" => ["Smithfield B0, 64-bit, dual-core, 90nm, L2: 2MB",2,1,0],	# Pentium D
	"Intel-15,4,8" => ["Paxville A0, 64-bit, dual-core, 90nm, L2: 2MB",2,2,0],	# PD Xeon MP
	"Intel-15,4,9" => [
		["Pentium", ["Prescott G1, 64-bit, 90nm, L2: 1MB",1,2,0]],		# P4
		["Xeon", ["Nocona G1, 64-bit, 90nm, L2: 1MB",1,2,0]],			# P4 Xeon DP
	],
	"Intel-15,4,10" => [
		["Pentium", ["Prescott R0, 64-bit, 90nm, L2: 2MB",1,2,0]],		# P4
		["Xeon", ["Irwindale R0, 64-bit, 90nm, L2: 2MB",1,2,0]],		# P4 Xeon DP
	],
	"Intel-15,6,2" => [
		["Pentium", ["Presler B1, 64-bit, dual-core, 65nm, L2: 4MB",2,1,0]],	# Pentium D
		["Xeon", ["Dempsey B1, 64-bit, dual-core, 65nm, L2: 4MB",2,1,0]],	# PD Xeon DP
	],
	"Intel-15,6,4" => [
		["Pentium", ["Presler C1, 64-bit, dual-core, 65nm, L2: 4MB",2,1,0]],	# Pentium D
		["Xeon", ["Dempsey C1, 64-bit, dual-core, 65nm, L2: 4MB",2,2,0]],	# PD Xeon DP
	],
	"Intel-15,6,5" => [
		["Pentium", ["Presler D0, 64-bit, dual-core, 65nm, L2: 4MB",2,1,0]],	# Pentium D
	],
	"Intel-15,6,8" => [
		["Xeon", ["Tulsa B0, 64-bit, dual-core, 65nm, L2: 4MB",2,2,0]],		# PD Xeon MP
	],
);

# http://www.stchas.edu/faculty/mcse-cisco/a-plus/Fuszner/SANDRA/program/Sandra.bio
my(%jedec) = (
	"2C" => "Micron",
	"AD" => "Hynix",
	"C1" => "Infineon",
	"CE" => "Samsung",
	"7F61" => "Wintec",
	"7F94" => "Smart Modular",
	"7F98" => "Kingston",
	"7FA8" => "Simple Tech",
	"7F7F9E" => "Corsair",
	"7F7FFE" => "Elpida",
	"7F7F7F0B" => "Nanya",
	"7F7F7F16" => "Netlist",
	"7F7F7F83" => "Buffalo",
	"7F7F7F7F46" => "Legacy",
	"7F7F7F7F43" => "Ramaxel",
);

my(%memory_models) = (

	# 2Rx4 with ECC ==> 36 chips (2R ==> 144 bits / x4 == 36)
	# 1Rx4 with ECC ==> 18 chips (1R ==> 72 bits / x4 == 18)
	# 4Rx8 with ECC ==> 36 chips (4R ==> 288 bits / x8 == 36)
	# 2Rx8 with ECC ==> 18 chips
	# 1Rx8 with ECC ==> 9 chips
	# 2Rx16 ==> 8 chips
	# 1Rx16 ==> 4 chips
	# 2Rx4 DDP ==> 36 chips / 2 (DDP) == 18 chips
	# DDP = dual die package (2 dies embedded on one chip)

	# http://www.digchips.com/datasheets/search.php
	# http://www.datasheetcatalog.com/

	# amb info for fbdimms
	# http://developer.intel.com/technology/memory/FBDIMM/667_FBDIMM_web_posting_results_ww30_5.pdf

	# Unknown
	"GM431-QAA-INTD1F" => "2GB PC2-5300 DDR2-667 ECC Fully Buffered CL5",
	"GR2DF56724QB667C" => "2GB PC2-5300 DDR2-667 ECC Fully Buffered CL5",
	"GR2DF56728MT667N" => "2GB PC2-5300 DDR2-667 ECC Fully Buffered CL5",
	"GR2DF56728EL667Q" => "2GB PC2-5300 DDR2-667 ECC Fully Buffered CL5",
	"GC6CD404" => "512MB PC-2700 Legend DDR-333 Unbuffered CL2.5 2R",
	"FC7CD905" => "256MB PC-3200 Legend DDR-400 Unbuffered CL3",
	"GC7CD905" => "512MB PC-3200 Legend DDR-400 Unbuffered CL3 2R",
	"7D-22KB5K1FPP" => "1GB PC2-4200 DDR2-533 Unbuffered CL4 2R",
	"HPQ00-21183-723SCU" => "2GB PC-2700 HP/Simple DDR-333 ECC Registered CL2.5 2Rx4",
	"140134-031" => "256MB PC133 HP SDRAM Unbuffered CL3",
	"127006-041" => "512MB PC133 HP/Micron SDRAM ECC Registered CL3 1Rx4",
	"512E40K3R24" => "512MB PC2-3200 Itaucom DDR2-400 ECC Registered CL3",
	"M0840038" => "2GB PC2-4200 PQI DDR2-533 Unbuffered CL3",

	# Ramaxel
	"RML1040HB38D6F-533" => "512MB PC2-4200 Ramaxel DDR2-533 Unbuffered CL4 1Rx4",
	"RML1040MD38D6F-533" => "512MB PC2-4200 Ramaxel DDR2-533 Unbuffered CL4 1Rx8",
	"RML1040EG38D6W-533" => "512MB PC2-4200 Ramaxel DDR2-533 Unbuffered CL4",
	"RML1040EG38D6F-533" => "512MB PC2-4200 Ramaxel DDR2-533 Unbuffered CL4 1Rx8",
	"RML1040E38D6F-533" => "512MB PC2-4200 Ramaxel DDR2-533 Unbuffered CL4",
	"RML1040S38D6F-533" => "512MB PC2-4200 Ramaxel DDR2-533 Unbuffered CL4 1Rx8",
	"RML1320EG38D7W-667" => "1GB PC2-5300 Ramaxel DDR2-667 Unbuffered CL5",
	"RML1520EC48D7W-800" => "1GB PC2-6400 Ramaxel DDR2-800 Unbuffered CL6",

	# Transend
	"TS64MLD64V4F" => "512MB PC-3200 Transend DDR-400 Unbuffered CL2.5 2Rx8",
	"TS128MLD64V3J" => "1GB PC-2700 Transend DDR-333 Unbuffered CL2.5 2Rx8",
	"TS128MLD64V4J" => "1GB PC-3200 Transend DDR-400 Unbuffered CL3 2Rx8",

	"TS64MLQ64V4J" => "512MB PC2-3200 Transend DDR2-400 Unbuffered CL3 1Rx8",
	"TS64MLQ64V5J" => "512MB PC2-4200 Transend DDR2-533 Unbuffered CL4 1Rx8",
	"JM388Q643A-5" => "1GB PC2-4200 Transend DDR2-533 Unbuffered CL4 2Rx8",
	"JM667QLJ-1G" => "1GB PC2-5300 Transend DDR2-667 Unbuffered CL5 2Rx8",
	"JM667QLU-2G" => "2GB PC2-5300 Transend DDR2-667 Unbuffered CL5 2Rx8",

	# Aeneon
	"AET660UD00-30DB97X" => "512MB PC2-5300 Aeneon DDR2-667 Unbuffered CL5 1Rx8",

	# http://www.corsairmicro.com/main/prddr.html
	"CM766S512-133" => "512MB PC133 Corsair SDRAM ECC Registered CL3 1Rx4",
	"CM73SD512R-2100" => "512MB PC-2100 Corsair DDR-266 ECC Registered CL2.5 1Rx4",
	"CM74SD1024R-2100" => "1GB PC-2100 Corsair DDR-266 ECC Registered CL2.5 2Rx4",
	"CM66SD1024-2700" => "1GB PC-2700 Corsair DDR-333 Unbuffered CL2.5 2Rx4",
	"CM73DD1024R-400" => "1GB PC2-3200 Corsair DDR2-400 ECC Registered CL3 1Rx4",
	"CM2X512-5400C4" => "512MB PC2-5400 Corsair DDR2-675 Unbuffered CL4 2Rx8",	# specs are correct

	# Kingston
	"KT326667-041-INCE5" => "256MB PC-3200 Kingston DDR-400 Unbuffered CL3",
	"KT305957-041-INCE5" => "256MB PC-2700 Kingston DDR-333 Unbuffered CL2.5",
	"UW728-IFA-INTC0S" => "1GB PC2-4200 Dell/Kingston DDR2-533 ECC Fully Buffered CL4",
	"UW729-IFA-INTC0S" => "2GB PC2-4200 Dell/Kingston DDR2-533 ECC Fully Buffered CL4 2Rx4",
	"KG7132-IFA-INTC0S" => "2GB PC2-4200 Dell/Kingston DDR2-533 ECC Fully Buffered CL4",
	"9W657-NAA-INTC0F" => "2GB PC2-5300 Dell/Kingston DDR2-667 ECC Fully Buffered CL5 2R",
	"GM431-NAA-INTD1F" => "2GB PC2-5300 Dell/Kingston DDR2-667 ECC Fully Buffered CL5",
	"KD6502-ELG" => "1GB PC2-5300 Kingston DDR2-667 Unbuffered CL5 2Rx8",
	"KD6502-ELJ" => "1GB PC2-5300 Kingston DDR2-667 Unbuffered CL5 2Rx8",
	"KX1563-NAB" => "2GB PC2-3200 Kingston DDR2-400 ECC Registered CL3",
	"9905316-005.A04LF" => "1GB PC2-5300 Kingston DDR2-667 Unbuffered CL5 2Rx8",	# KVR667D2N5/1G
	"9905316-131.A01LF" => "1GB PC2-6400 Kingston DDR2-800 Unbuffered CL6",
	"9905316-132.A01LF" => "2GB PC2-6400 Kingston DDR2-800 Unbuffered CL6",
	"DGM431NABINTD1F" => "2GB PC2-5300 Kingston DDR2-667 ECC Fully Buffered CL5 2R",

	# Viking
	# http://www.vikingcomponents.com/brochure/details.asp
	"VR5ER567214EBPD1" => "2GB PC2-3200 Viking DDR2-400 ECC Registered CL3 2Rx4",

	# Simple Tech
	"ST72P4T128M-A05A" => "1GB PC2-3200 Simple DDR2-400 ECC Registered CL3 1R",
	"S1024R3NN2QK-I" => "1GB PC2-5300 Simple DDR2-667 Unbuffered CL5 2R",

	# http://www.legacyelectronics.com/products.php
	"L512872M20A-50." => "1GB PC2-3200 Legacy DDR2-400 ECC Registered CL3 1Rx4",
	"L527R5A2AHA-50." => "2GB PC2-3200 Legacy DDR2-400 ECC Registered CL3 1Rx4",
	"L527R5A2AHA-37R" => "2GB PC2-4200 Legacy DDR2-533 ECC Registered CL4 1Rx4",
	"L547R5A2A4B-50Y" => "4GB PC2-3200 Legacy DDR2-400 ECC Registered CL3 2Rx4",

	# http://www.qimonda.com/index.html
	"HYS64V32220GU-7.5." => "256MB PC133 Infineon SDRAM Unbuffered CL3",

	# http://www.qimonda.com/system/galleries/download/products/nomenclature_ddr_ddr2.pdf

	"64D16301[GHE]U6B" => "128MB PC-2700 Infineon DDR-333 Unbuffered CL2.5 1Rx8",
	"64D32000[GHE]U7B" => "256MB PC-2100 Infineon DDR-266 Unbuffered CL2 1Rx8",
	"64D32300[GHE]U6C" => "256MB PC-2700 Infineon DDR-333 Unbuffered CL2.5 1Rx4",
	"64D32300[GHE]U5C" => "256MB PC-3200 Infineon DDR-400 Unbuffered CL3 1Rx4",
	"64D64020[GHE]U7B" => "512MB PC-2100 Infineon DDR-266 Unbuffered CL2 2Rx8",
	"64D64320[GHE]U5C" => "512MB PC-3200 Infineon DDR-400 Unbuffered CL3 2Rx8",
	"72D128521[GHE]R7B" => "1GB PC-2100 Infineon DDR-266 ECC Registered CL2 2Rx4",
	"HYS72D128520[GHE]R-7" => "1GB PC-2100 Infineon DDR-266 ECC Registered CL2 2Rx4",

	# http://www.qimonda.com/computing-dram/ddr2/registered-dimms.html
	# http://www.qimonda.com/download.jsp?ref=/qis_docs/Downloads%20Computing/DDR2%20%26%20DDR_SDRAM_Mod.pdf
	# HYS prefix
	"72T32000[EH]R5." => "256MB PC2-3200 Infineon DDR2-400 ECC Registered CL3 1Rx8",
	"72T64001[EH]R5." => "512MB PC2-3200 Infineon DDR2-400 ECC Registered CL3 1Rx8",

	"72T128000[EH]R3.7." => "1GB PC2-4200 Infineon DDR2-533 ECC Registered CL4 1Rx4",
	"72T128000[EH]R5." => "1GB PC2-3200 Infineon DDR2-400 ECC Registered CL3 1Rx4",
	"72T128001[EH]R5." => "1GB PC2-3200 Infineon DDR2-400 ECC Registered CL3 1Rx8",
	"72T128020[EH]R5." => "1GB PC2-3200 Infineon DDR2-400 ECC Registered CL3 2Rx8",
	"72T128000[EH]R3S..?" => "1GB PC2-5300 Infineon DDR2-667 ECC Registered CL5 1Rx4",
	"72T128000[EH]P3S..?" => "1GB PC2-5300 Infineon DDR2-667 ECC Registered CL5 1Rx4",

	"72T256220[EH]R5." => "2GB PC2-3200 Infineon DDR2-400 ECC Registered CL3 2Rx4",
	"72T256000[EH]R5." => "2GB PC2-3200 Infineon DDR2-400 ECC Registered CL3 1Rx4",		# R = reg
	"72T256220[EH]P3S..?" => "2GB PC2-5300 Infineon DDR2-667 ECC Registered CL5 2Rx4",	# P = reg with addr parity
	"72T256920[EH]P3S..?" => "2GB PC2-5300 Infineon DDR2-667 ECC Registered CL5 2Rx4",

	"72T512022[EH]R5." => "4GB PC2-3200 Qimonda DDR2-400 ECC Registered CL3 2Rx4 DDP",
	"72T512022[EH]R3.7." => "4GB PC2-4200 Qimonda DDR2-533 ECC Registered CL4 2Rx4 DDP",
	"72T512022[EH]P3S..?" => "4GB PC2-5300 Qimonda DDR2-667 ECC Registered CL5 2Rx4 DDP",
	"72T512220[EH]P3S..?" => "4GB PC2-5300 Qimonda DDR2-667 ECC Registered CL5 2Rx4",
	"72T512922[EH]P3S..?" => "4GB PC2-5300 Qimonda DDR2-667 ECC Registered CL5 2Rx4 DDP",
	"72T512040[EH]P3S..?" => "4GB PC2-5300 Qimonda DDR2-667 ECC Registered CL5 4Rx8",
	#       ^     ^
	#       |     |-- R=registered, P=registerd/addr parity
	#       |-------- rank: 0=1R, 2=2R, 4=4R

	# http://www.qimonda.com/computing-dram/ddr2/unbuffered.html
	"64T32000[EH]U3.7." => "256MB PC2-4200 Infineon DDR2-533 Unbuffered CL4 1Rx16",
	"64T64000[EH]U5." => "512MB PC2-3200 Infineon DDR2-400 Unbuffered CL3 1Rx8",
	"64T64000[EH]U3.7." => "512MB PC2-4200 Infineon DDR2-533 Unbuffered CL4 1Rx8",
	"64T64000[EH]U3S..?" => "512MB PC2-5300 Infineon DDR2-667 Unbuffered CL5 1Rx8",
	"64T128020[EH]U3S..?" => "1GB PC2-5300 Infineon DDR2-667 Unbuffered CL5 2Rx8",
	"72T64000[EH]U3.7." => "512MB PC2-4200 Infineon DDR2-533 ECC Unbuffered CL4 1Rx8",
	"72T64000[EH]U3S..?" => "512MB PC2-5300 Infineon DDR2-667 ECC Unbuffered CL4 1Rx8",
	"72T128020[EH]U3.7." => "1GB PC2-4200 Infineon DDR2-533 ECC Unbuffered CL4 2Rx8",
	"72T128020[EH]U3S..?" => "1GB PC2-5300 Infineon DDR2-667 ECC Unbuffered CL5 2Rx8",
	"64T128020[EH]U2.5.." => "1GB PC2-6400 Infineon DDR2-800 ECC Unbuffered CL6 2Rx8",
	"72T256020[EH]U3.7." => "2GB PC2-4200 Infineon DDR2-533 ECC Unbuffered CL4 2Rx8",
	"72T256020[EH]U3S..?" => "2GB PC2-5300 Infineon DDR2-667 ECC Unbuffered CL5 2Rx8",

	# http://www.qimonda.com/download.jsp?ref=/qis_docs/Downloads%20Computing/Nomenclature_DDR_DDR2.pdf
	# http://www.qimonda.com/computing-dram/ddr2/fully-buffered-dimms.html
	"72T64.00[EH]F[AND]3S..?" => "512MB PC2-5300 Infineon DDR2-667 ECC Fully Buffered CL5 1Rx8",
	"72T64.00[EH]F[AND]3.7." => "512MB PC2-4200 Infineon DDR2-533 ECC Fully Buffered CL4 1Rx8",
	"72T128.20[EH]F[AND]3.7." => "1GB PC2-4200 Infineon DDR2-533 ECC Fully Buffered CL4 2Rx8",
	"72T128.20[EH]F[AND]3S..?" => "1GB PC2-5300 Infineon DDR2-667 ECC Fully Buffered CL5 2Rx8",	# A=?, N=Intel C0 AMB, D=IDT 1.5 AMB
	"72T256.20[EH]F[AND]3.7." => "2GB PC2-4200 Infineon DDR2-533 ECC Fully Buffered CL4 2Rx4",
	"72T256.20[EH]F[AND]3S..?" => "2GB PC2-5300 Infineon DDR2-667 ECC Fully Buffered CL5 2Rx4",

	"72T512.20[EH]F[AND]3S..?" => "4GB PC2-5300 Infineon DDR2-667 ECC Fully Buffered CL5 2Rx4",

	# http://www.qimonda.com/computing-dram/ddr/registered-dimms.html
	"72D128320GBR6C" => "1GB PC-2700 Infineon DDR-333 ECC Registered CL2.5 2Rx4",

	"72D128300GBR5B" => "1GB PC-3200 Infineon DDR-400 ECC Registered CL3 1Rx4",
	"72D256220HBR5B" => "2GB PC-3200 Infineon DDR-400 ECC Registered CL3 2Rx4",
	"72D256220GBR5B" => "2GB PC-3200 Infineon DDR-400 ECC Registered CL3 2Rx4",

	# Nanya
	# http://www.nanya.com/e-htm/B/2abc/2abc-01.htm
	# http://www.nanya.com/PageEdition1.aspx?Menu_ID=23&lan=en-us&def=210&isPrint=&KeyWords=
	# http://www.nanya.com/NanyaAdmin/GetFiles.ashx?ID=351
	"NT256D64S88B1G-6K" => "256MB PC-2700 Nanya DDR-333 Unbuffered CL2.5 1Rx8",
	"NT256D64S88C0G-5T" => "256MB PC-3200 Nanya DDR-400 Unbuffered CL3 1Rx8",
	"NT512D64S8HB0G-75B" => "512MB PC-2100 Nanya DDR-266 Unbuffered CL2.5 2Rx8",

	"NT1GT72U4P...U-5A" => "1GB PC2-3200 Nanya DDR2-400 ECC Registered CL3 1Rx4",
	"NT1GT72U4P...V-5A" => "1GB PC2-3200 Nanya DDR2-400 ECC Registered CL3 1Rx4",
	"NT1GT72U4P...V-3C" => "1GB PC2-5300 Nanya DDR2-667 ECC Registered CL5 1Rx4",
	"NT2GT72U4N...V-5A" => "2GB PC2-3200 Nanya DDR2-400 ECC Registered CL3 2Rx4",
	"NT2GT72U4N...V-3C" => "2GB PC2-5300 Nanya DDR2-667 ECC Registered CL5 2Rx4",

	"NT256T64UH4...Y-37B?" => "256MB PC2-4200 Nanya DDR2-533 Unbuffered CL4 1Rx16",
	"NT512T64U88..(F|B|BY)-5A" => "512MB PC2-3200 Nanya DDR2-400 Unbuffered CL3 1Rx8",
	"NT512T64U88..(F|B|BY)-37B?" => "512MB PC2-4200 Nanya DDR2-533 Unbuffered CL4 1Rx8",
	"NT512T72U89...Y-37B?" => "512MB PC2-4200 Nanya DDR2-533 ECC Unbuffered CL4 1Rx8",
	"NT512T72U89...Y-3C" => "512MB PC2-5300 Nanya DDR2-667 ECC Unbuffered CL5 1Rx8",
	"NT1GT64U8H..(F|B|BY)-37B?" => "1GB PC2-4200 Nanya DDR2-533 Unbuffered CL4 2Rx8",
	"NT1GT64U8H..(F|B|BY)-3C" => "1GB PC2-5300 Nanya DDR2-667 Unbuffered CL5 2Rx8",
	"NT1GT64U8H..(F|B|BY)-25C" => "1GB PC2-6400 Nanya DDR2-800 Unbuffered CL5 2Rx8",
	"NT1GT64U8H..(F|B|BY)-25D" => "1GB PC2-6400 Nanya DDR2-800 Unbuffered CL6 2Rx8",
	"NT1GT72U8P...Y-37B" => "1GB PC2-4200 Nanya DDR2-533 ECC Unbuffered CL4 2Rx8",
	"NT1GT72U8P...Y-3C" => "1GB PC2-5300 Nanya DDR2-667 ECC Unbuffered CL5 2Rx8",
	"NT1GT64U88...Y-AC" => "1GB PC2-6400 Nanya DDR2-800 Unbuffered CL5 1Rx8",
	"NT1GT64U88...Y-AD" => "1GB PC2-6400 Nanya DDR2-800 Unbuffered CL6 1Rx8",
	"NT2GT72U8P...Y-3C" => "2GB PC2-5300 Nanya DDR2-667 ECC Unbuffered CL5 2Rx8",
	"NT2GT72U8P...Y-AC" => "2GB PC2-6400 Nanya DDR2-800 ECC Unbuffered CL5 2Rx8",
	"NT2GT72U8P...Y-AD" => "2GB PC2-6400 Nanya DDR2-800 ECC Unbuffered CL6 2Rx8",

	"NT1GT72U8P..BD-3C" => "1GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT AMB
	"NT1GT72U8P..BN-3C" => "1GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel AMB
	"NT2GT72U4N..BD-3C" => "2GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx4",	# IDT AMB
	"NT2GT72U4N..BN-3C" => "2GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel AMB
	"NT2GT72U8P..BN-3C" => "2GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel AMB
	"NT2GT72U8P..BD-3C" => "2GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT AMB
	"NT4GT72U4N..BD-3C" => "4GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx4",	# IDT AMB
	#        ^^   ^
	#        ||   |- AMB: D=IDT, N=Intel, E=NEC
	#        ||----- #chips: 4=4, 8=8, 9=9, H=16, P=18, N=36
	#        |------ 4=x4, 8=x8, H=x16
	#
	"NT4GTT72U4P..UN-3C" => "4GB PC2-5300 Nanya DDR2-667 ECC Fully Buffered CL5 2Rx4 DDP",	# Intel AMB
	# 2Rx4 DDP ==> 36 chips / 2 (DDP) == 18 chips

	# Elixir (brand of Nanya)
	# http://www.elixir-memory.com/product_elixir_1.asp?pro_Name=elixir&pro_Class=DDR+SDRAM&pro_Type=All
	"M2U51264DS8HC3G-5T" => "512MB PC-3200 Elixir DDR-400 Unbuffered CL3 2Rx8",
	"M2Y51264DS8HC3G-5T" => "512MB PC-3200 Elixir DDR-400 Unbuffered CL3 2Rx8",
	"M2Y51264DS88C1G-5T" => "512MB PC-3200 Elixir DDR-400 Unbuffered CL3 1Rx8",
	"M2Y1G64DS8HC1G-5T" => "1GB PC-3200 Elixir DDR-400 Unbuffered CL3 2Rx8",

	"M2U51264TU88A2B-37" => "512MB PC2-4200 Elixir DDR2-533 Unbuffered CL4 1Rx8",
	"M2Y51264TU88A4B-3C" => "512MB PC2-5300 Elixir DDR2-667 Unbuffered CL5 1Rx8",
	"M2Y1G64TU8HB4B-3C" => "1GB PC2-5300 Elixir DDR2-667 Unbuffered CL5 2Rx8",
	"M2Y1G64TU8HA2B-3C" => "1GB PC2-5300 Elixir DDR2-667 Unbuffered CL5 2Rx8",
	"M2Y1G64TU8HA4B-3C" => "1GB PC2-5300 Elixir DDR2-667 Unbuffered CL5 2Rx8",

	# http://www.samsung.com/Products/Semiconductor/Support/ProductLists/index.htm
	# http://www.samsung.com/Products/Semiconductor/Support/Label_CodeInfo/PartNumberDecoder.htm
	# http://www.samsung.com/Products/Semiconductor/Support/Label_CodeInfo/DDR_SDRAM_Module.pdf
	# http://www.samsung.com/Products/Semiconductor/Support/Label_CodeInfo/ddr2_product_guide_aug_04.pdf
	# http://www.samsung.com/Products/Semiconductor/common/product_list.aspx?family_cd=DDR020201
	# http://www.samsung.com/global/system/business/semiconductor/family/2007/8/30/746403DDR_SDRAM_Module.pdf
	"M3 12L6420...-CB0" => "512MB PC-2100 Samsung DDR-266 ECC Registered CL2.5 1Rx4",
	"M3 12L6523...-CB3" => "512MB PC-2700 Samsung DDR-333 ECC Registered CL2.5 1Rx8",
	"M3 12L2920...-CB3" => "1GB PC-2700 Samsung DDR-333 ECC Registered CL2.5 1Rx4",
	"M3 68L1624...-CB3" => "128MB PC-2700 Samsung DDR-333 Unbuffered CL2.5 1Rx16",
	"M3 68L3223...-CB0" => "256MB PC-2100 Samsung DDR-266 Unbuffered CL2.5 1Rx8",
	"M3 68L3223...-CB3" => "256MB PC-2700 Samsung DDR-333 Unbuffered CL2.5 1Rx8",
	"M3 68L3223...-CCC" => "256MB PC-3200 Samsung DDR-400 Unbuffered CL3 1Rx8",
	"M3 68L6523...-CB3" => "512MB PC-2700 Samsung DDR-333 Unbuffered CL2.5 1Rx8",
	"M3 68L6523...-CCC" => "512MB PC-3200 Samsung DDR-400 Unbuffered CL3 1Rx8",
	"M3 68L6423...-CCC" => "512MB PC-3200 Samsung DDR-400 Unbuffered CL3 2Rx8",
	"M3 68L2923...-CCC" => "1GB PC-3200 Samsung DDR-400 Unbuffered CL3 2Rx8",
	#         ^
	#         |-- 0=x4, 3=x8, 4=x16

	# http://www.samsung.com/global/business/semiconductor/products/dram/downloads/ddr2_product_guide_apr_07.pdf
	"M3 93T6450...-CCC" => "512MB PC2-3200 Samsung DDR2-400 ECC Registered CL3 1Rx4",
	"M3 93T6453...-CCC" => "512MB PC2-3200 Samsung DDR2-400 ECC Registered CL3 2Rx8",
	"M3 93T6553...-CCC" => "512MB PC2-3200 Samsung DDR2-400 ECC Registered CL3 1Rx8",
	"M3 93T6553...-CE6" => "512MB PC2-5300 Samsung DDR2-667 ECC Registered CL5 1Rx8",
	"M3 93T2950...-CCC" => "1GB PC2-3200 Samsung DDR2-400 ECC Registered CL3 1Rx4",
	"M3 93T2950...-CE6" => "1GB PC2-5300 Samsung DDR2-667 ECC Registered CL5 1Rx4",
	"M3 93T2953...-CCC" => "1GB PC2-3200 Samsung DDR2-400 ECC Registered CL3 2Rx8",
	"M3 93T5750...-CCC" => "2GB PC2-3200 Samsung DDR2-400 ECC Registered CL3 2Rx4",
	"M3 93T5750...-CD5" => "2GB PC2-4200 Samsung DDR2-533 ECC Registered CL4 2Rx4",
	"M3 93T5660...-CCC" => "2GB PC2-3200 Samsung DDR2-400 ECC Registered CL3 1Rx4",
	"M3 93T5660...-CD5" => "2GB PC2-4200 Samsung DDR2-533 ECC Registered CL4 1Rx4",
	"M3 93T5750...-CE6" => "2GB PC2-5300 Samsung DDR2-667 ECC Registered CL5 2Rx4",
	"M3 93T5160...-CE6" => "4GB PC2-5300 Samsung DDR2-667 ECC Registered CL5 2Rx4",

	"M3 78T3253...-CD5" => "256MB PC2-4200 Samsung DDR2-533 Unbuffered CL4 1Rx8",
	"M3 78T6453...-CD5" => "512MB PC2-4200 Samsung DDR2-533 Unbuffered CL4 2Rx8",
	"M3 78T6553...-CD5" => "512MB PC2-4200 Samsung DDR2-533 Unbuffered CL4 1Rx8",
	"M3 78T6553...-CE6" => "512MB PC2-5300 Samsung DDR2-667 Unbuffered CL5 1Rx8",
	"M3 78T2953...-CD5" => "1GB PC2-4200 Samsung DDR2-533 Unbuffered CL4 2Rx8",
	"M3 78T2953...-CE6" => "1GB PC2-5300 Samsung DDR2-667 Unbuffered CL5 2Rx8",
	"M3 78T2953...-CF7" => "1GB PC2-6400 Samsung DDR2-800 Unbuffered CL6 2Rx8",
	"M3 78T2863...-CE6" => "1GB PC2-5300 Samsung DDR2-667 Unbuffered CL5 1Rx8",
	"M3 78T2863...-CF7" => "1GB PC2-6400 Samsung DDR2-800 Unbuffered CL6 1Rx8",
	"M3 91T2953...-CD5" => "1GB PC2-4200 Samsung DDR2-533 ECC Unbuffered CL4 2Rx8",
	"M3 91T2953...-CE6" => "1GB PC2-5300 Samsung DDR2-667 ECC Unbuffered CL5 2Rx8",
	"M3 91T5663...-CD5" => "2GB PC2-4200 Samsung DDR2-533 ECC Unbuffered CL4 2Rx8",
	"M3 91T5663...-CE6" => "2GB PC2-5300 Samsung DDR2-667 ECC Unbuffered CL5 2Rx8",

	# http://www.samsung.com/global/system/business/semiconductor/family/2008/5/26/041421DDR2_SDRAM_Module.pdf
	# http://www.samsung.com/Products/Semiconductor/common/product_list.aspx?family_cd=DDR020204
	"M395T6553...-CE60" => "512MB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 1Rx8",	# Intel C0 AMB
	"M395T6553...-CE61" => "512MB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 1Rx8",	# IDT A1.5 AMB
	"M395T6553...-CE66" => "512MB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 1Rx8",	# IDT C1 AMB
	"M395T6553...-CE6." => "512MB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 1Rx8",	# wildcard AMB

	"M395T2953...-CD50" => "1GB PC2-4200 Samsung DDR2-533 ECC Fully Buffered CL4 2Rx8",	# Intel C0 AMB
	"M395T2953...-CD51" => "1GB PC2-4200 Samsung DDR2-533 ECC Fully Buffered CL4 2Rx8",	# IDT A1.5 AMB
	"M395T2953...-CE60" => "1GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel C0 AMB
	"M395T2953...-CE61" => "1GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT A1.5 AMB
	"M395T2953...-CE62" => "1GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# NEC B5 AMB
	"M395T2953...-CE65" => "1GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel D1 AMB
	"M395T2953...-CE66" => "1GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT C1 AMB
	"M395T2953...-CE6." => "1GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# wildcard AMB

	"M395T5750...-CD50.?" => "2GB PC2-4200 Samsung DDR2-533 ECC Fully Buffered CL4 2Rx4",	# Intel C0 AMB
	"M395T5750...-CD51" => "2GB PC2-4200 Samsung DDR2-533 ECC Fully Buffered CL4 2Rx4",	# IDT A1.5 AMB
	"M395T5750...-CE60.?" => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel C0 AMB
	"M395T5750...-CE61" => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# IDT A1.5 AMB
	"M395T5750...-CE62" => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# NEC B5 AMB
	"M395T5750...-CE65" => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel D1 AMB
	"M395T5750...-CE66" => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# IDT C1 AMB
	"M395T5750...-CE6." => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# wildcard AMB

	"M395T5663...-CE65" => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel D1 AMB
	"M395T5663...-CE66" => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT C1 AMB
	"M395T5663...-CE6." => "2GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx8",	# wildcard AMB

	"M395T5160...-CE65" => "4GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel D1 AMB
	"M395T5160...-CE6." => "4GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# wildcard AMB
	"M395T5166...-CE65" => "4GB PC2-5300 Samsung DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel D1 AMB
	#        ^
	#        |-- 0=x4, 3=x8, 4=x16, 6=x4:stack, 7=x8:stack

	# http://www.amd.com/us-en/assets/content_type/DownloadableAssets/Opteron_Registered_DIMM_AVL.pdf
	# http://www.samsung.com/Products/Semiconductor/Support/Label_CodeInfo/DDR_SDRAM_Module.pdf
	"M3 12L5720CZ0-CB3" => "2GB PC-2700 Samsung DDR-333 ECC Registered CL2.5 2Rx4",
	"M3 12L5720CZ3-CCC" => "2GB PC-3200 Samsung DDR-400 ECC Registered CL3 2Rx4",

	# http://download.micron.com/pdf/guide/modguide.pdf
	# http://www.micron.com/products/modules/ddr2sdram/partlist.aspx?pincount=240-pin&version=Registered&package=DIMM
	# http://www.micron.com/products/search.aspx
	# http://www.micron.com/products/modules/
	"9VDDF6472G-335.." => "512MB PC-2700 Micron DDR-333 ECC Registered CL2.5 1Rx8",
	"8LSDT3264AG-133.." => "256MB PC133 Micron SDRAM Unbuffered CL3 1Rx8",
	"18LSDT6472G-133.." => "512MB PC133 Micron SDRAM ECC Registered CL3 1Rx4",
	"18VDDT6472G-265.." => "512MB PC-2100 Micron DDR-266 ECC Registered CL2.5 1Rx4",
	"18VDDF12872G-335.." => "1GB PC-2700 Micron DDR-333 ECC Registered CL2.5 1Rx4",

	# http://www.micron.com/products/modules/udimm/partlist?tech=DDR%20SDRAM
	"4VDDT1664AG-335.." => "128MB PC-2700 Micron DDR-333 Unbuffered CL2.5 1Rx16",
	"8VDDT3264AG-265.." => "256MB PC-2100 Micron DDR-266 Unbuffered CL2.5 1Rx8",
	"8VDDT3264AG-335.." => "256MB PC-2700 Micron DDR-333 Unbuffered CL2.5 1Rx8",
	"8VDDT3264AG-40B.." => "256MB PC-3200 Micron DDR-400 Unbuffered CL3 1Rx8",
	"8VDDT6464AG-335.." => "512MB PC-2700 Micron DDR-333 Unbuffered CL2.5 1Rx8",
	"16VDDT6464AY-40B.." => "512MB PC-3200 Micron DDR-400 Unbuffered CL3 2Rx8",

	"9HTF3272Y-40E.." => "256MB PC2-3200 Micron DDR2-400 ECC Registered CL3 1Rx8",
	"9HTF6472Y-40E.." => "512MB PC2-3200 Micron DDR2-400 ECC Registered CL3 1Rx8",
	"18HTF6472DY-40E.." => "512MB PC2-3200 Micron DDR2-400 ECC Registered CL3 2Rx8",
	"18HTF6472Y-40E.." => "512MB PC2-3200 Micron DDR2-400 ECC Registered CL3 1Rx4",
	"18HTF12872Y-40E.." => "1GB PC2-3200 Micron DDR2-400 ECC Registered CL3 1Rx4",
	"18HTF12872DY-40E.." => "1GB PC2-3200 Micron DDR2-400 ECC Registered CL3 2Rx8",
	"18HTF12872PY-667.." => "1GB PC2-5300 Micron DDR2-667 ECC Registered CL5 1Rx4",
	"18HTF25672Y-40E.." => "2GB PC2-3200 Micron DDR2-400 ECC Registered CL3 1Rx4",
	"36HTF25672Y-40E.." => "2GB PC2-3200 Micron DDR2-400 ECC Registered CL3 2Rx4",		# Non-Parity
	"36HTF25672PY-667.." => "2GB PC2-5300 Micron DDR2-667 ECC Registered CL5 2Rx4",		# Parity
	"36HTF51272PY-667.." => "4GB PC2-5300 Micron DDR2-667 ECC Registered CL5 2Rx4",		# Parity

	"4HTF3264AY-53E.." => "256MB PC2-4200 Micron DDR2-533 Unbuffered CL4 1Rx16",
	"8HTF6464AY-53E.." => "512MB PC2-4200 Micron DDR2-533 Unbuffered CL4 1Rx8",
	"9HTF6472AY-53E.." => "512MB PC2-4200 Micron DDR2-533 ECC Unbuffered CL4 1Rx8",
	"8HTF12864AY-667.." => "1GB PC2-5300 Micron DDR2-667 Unbuffered CL5 1Rx8",
	"8HTF12864AY-800.." => "1GB PC2-6400 Micron DDR2-800 Unbuffered CL6 1Rx8",
	"16HTF12864AY-667.." => "1GB PC2-5300 Micron DDR2-667 Unbuffered CL5 2Rx8",
	"18HTF12872AY-53E.." => "1GB PC2-4200 Micron DDR2-533 ECC Unbuffered CL4 2Rx8",
	"18HTF25672AY-53E.." => "2GB PC2-4200 Micron DDR2-533 ECC Unbuffered CL4 2Rx8",

	"MT9HTF6472FY-53E.." => "512MB PC2-4200 Micron DDR2-533 ECC Fully Buffered CL4 1Rx8",
	"18HTF12872FDY-53EB" => "1GB PC2-4200 Micron DDR2-533 ECC Fully Buffered CL4 2Rx8",
	"18HF12872FD667...." => "1GB PC2-5300 Micron DDR2-667 ECC Fully Buffered CL5 1Rx4",
	"36HTF25672F667...." => "2GB PC2-5300 Micron DDR2-667 ECC Fully Buffered CL5 2Rx4",
	"36HTF51272F667...." => "4GB PC2-5300 Micron DDR2-667 ECC Fully Buffered CL5 2Rx4",

	# http://www.elpida.com/en/products/index.html
	# http://www.elpida.com/pdfs/ECT-TS-1971.pdf
	"EBD25UC8AMFA-5B" => "256MB PC-3200 Elpida DDR-400 Unbuffered CL3 1Rx8",

	"EBE51UD8A.WA-6E-E" => "512MB PC2-5300 Elpida DDR2-667 Unbuffered CL5 1Rx8",
	"EBE11UD8A.WA-6E-E" => "1GB PC2-5300 Elpida DDR2-667 Unbuffered CL5 2Rx8",
	"EBE11UD8A.WA-8E-E" => "1GB PC2-6400 Elpida DDR2-800 Unbuffered CL5 2Rx8",
	"EBE11UD8A.WA-8G-E" => "1GB PC2-6400 Elpida DDR2-800 Unbuffered CL6 2Rx8",

	"EBE51RD8A.FA-4A-E" => "512MB PC2-3200 Elpida DDR2-400 ECC Registered CL3 1Rx8",
	"EBE10RD4A.FA-4A-E" => "1GB PC2-3200 Elpida DDR2-400 ECC Registered CL3 1Rx4",

	"EBE20RE4A.FA-4A-E" => "2GB PC2-3200 Elpida DDR2-400 ECC Registered CL3 1Rx4",
	"EBE20AE4A.FA-6E-E" => "2GB PC2-5300 Elpida DDR2-667 ECC Registered CL5 1Rx4",
	"EBE21AD4A.FA-6E-E" => "2GB PC2-5300 Elpida DDR2-667 ECC Registered CL5 2Rx4",
	"EBE41AE4A.FA-6E-E" => "4GB PC2-5300 Elpida DDR2-667 ECC Registered CL5 2Rx4",
	#   ^^^ ^
	#    || |- 4=x4, 8=x8, 6=x16
	#    ||--- A=registered/addr parity, R=registered
	#    |---- density/rank: 10=1G/1R, 11=1G/2R, 20=2G/1R, 21=2G/2R, 41=4G/2R, 51=512M/1R

	# e.g. http://www.elpida.com/pdfs/E0868E30.pdf
	"EBE11FD8A.F.-6E-E" => "1GB PC2-5300 Elpida DDR2-667 ECC Fully Buffered CL5 2Rx8",
	"EBE21FD4A.F.-5C-E" => "2GB PC2-4200 Elpida DDR2-533 ECC Fully Buffered CL4 2Rx4",
	"EBE21FD4A.F.-6E-E" => "2GB PC2-5300 Elpida DDR2-667 ECC Fully Buffered CL5 2Rx4",
	"EBE21FE8A.F.-6E-E" => "2GB PC2-5300 Elpida DDR2-667 ECC Fully Buffered CL5 2Rx8",

	# http://www.hynix.com/inc/pdfDownload.jsp?path=/upload/products/gl/products/dram/down/DDR2.pdf
	# http://hynix.com/datasheet/eng/module/module_sub.jsp?RK=08&SK=RD&RAM_NAME=DDR2+SDRAM&SUB_RAM=512MB&SUB_RAM1=1GB&SUB_RAM2=2GB&SUB_RAM3=4GB
	# P == lead-free, M == 36 chips?
	"71V32635HCT8-H" => "256MB PC133 Hynix SDRAM Unbuffered CL3 2Rx8",

	"HYMP564R72P?8-E3" => "512MB PC2-3200 Hynix DDR2-400 ECC Registered CL3 1Rx8",
	"HYMP512R72B?P?4-E3" => "1GB PC2-3200 Hynix DDR2-400 ECC Registered CL3 1Rx4",
	"HYMP512R72P?8-E3" => "1GB PC2-3200 Hynix DDR2-400 ECC Registered CL3 2Rx8",
	"HYMP125R72P?4-E3" => "2GB PC2-3200 Hynix DDR2-400 ECC Registered CL3 1Rx4",
	"HYMP125R72MP?4-E3" => "2GB PC2-3200 Hynix DDR2-400 ECC Registered CL3 2Rx4",
	"HYMP525R72B?P?4-E3" => "2GB PC2-3200 Hynix DDR2-400 ECC Registered CL3 2Rx4",

	"HYMP512P72.P4-Y5" => "1GB PC2-5300 Hynix DDR2-667 Parity Registered CL5 1Rx4",
	"HYMP525P72.P4-Y5" => "2GB PC2-5300 Hynix DDR2-667 Parity Registered CL5 2Rx4",
	"HYMP125P72.P4-Y5" => "2GB PC2-5300 Hynix DDR2-667 Parity Registered CL5 1Rx4",
	"HYMP151P72.P4-Y5" => "4GB PC2-5300 Hynix DDR2-667 Parity Registered CL5 2Rx4",
	"HYMP351P72.MP4-Y5" => "4GB PC2-5300 Hynix DDR2-667 Parity Registered CL5 2Rx4 DDP",
	#       ^   ^ ^
	#       |   | |-- 4=x4, 8=x8, 6=x16
	#       |   |---- M=DDP
	#       |-------- R=registered, P=registered/addr parity

	# http://hynix.com/datasheet/eng/module/module_sub.jsp?menu3=03&RK=07&SK=UD&RAM_NAME=DDR%20SDRAM&SUB_RAM1=256MB&SUB_RAM2=512MB&SUB_RAM3=
	# http://www.hynix.com/inc/pdfDownload.jsp?path=/gl/products/down/DDR_MODULE_Old.pdf
	"HYMD216 646[A-D]6J-J" => "128MB PC-2700 Hynix DDR-333 Unbuffered CL2.5 1Rx16",
	"HYMD232 646[A-D]8-H" => "256MB PC-2100 Hynix DDR-266 Unbuffered CL2.5 1Rx8",
	"HYMD232 646[A-D]8J-J" => "256MB PC-2700 Hynix DDR-333 Unbuffered CL2.5 1Rx8",
	"HYMD232 646[A-D]8J-D43" => "256MB PC-3200 Hynix DDR-400 Unbuffered CL3 1Rx8",
	"HYMD264 646[A-D]8J-J" => "512MB PC-2700 Hynix DDR-333 Unbuffered CL2.5 2Rx8",
	"HYMD264 646[A-D]8J-D43" => "512MB PC-3200 Hynix DDR-400 Unbuffered CL3 2Rx8",

	# http://hynix.com/datasheet/eng/module/module_sub.jsp?menu3=02&RK=08&SK=UD&RAM_NAME=DDR2%20SDRAM&SUB_RAM=256MB&SUB_RAM1=512MB&SUB_RAM2=
	# http://hynix.com/eng/02_products/01_dram/down/DDR2MODULE.pdf
	"HYMP532U64[A-G]?P6-E3" => "256MB PC2-3200 Hynix DDR2-400 Unbuffered CL3 1Rx16",
	"HYMP564U64[A-G]?P8-C4" => "512MB PC2-4200 Hynix DDR2-533 Unbuffered CL4 1Rx8",
	"HYMP512U64[A-G]?P8-Y5" => "1GB PC2-5300 Hynix DDR2-667 Unbuffered CL5 2Rx8",
	"HYMP112U64[A-G]?P8-Y5" => "1GB PC2-5300 Hynix DDR2-667 Unbuffered CL5 1Rx8",

	"HYMP564U72[A-G]?P8-C4" => "512MB PC2-4200 Hynix DDR2-533 ECC Unbuffered CL4 1Rx8",
	"HYMP564U72[A-G]?P8-Y5" => "512MB PC2-5300 Hynix DDR2-667 ECC Unbuffered CL5 1Rx8",
	"HYMP512U72[A-G]?P8-C4" => "1GB PC2-4200 Hynix DDR2-533 ECC Unbuffered CL4 2Rx8",
	"HYMP512U72[A-G]?P8-Y5" => "1GB PC2-5300 Hynix DDR2-667 ECC Unbuffered CL5 2Rx8",
	"HYMP125U72[A-G]?P8-C4" => "2GB PC2-4200 Hynix DDR2-533 ECC Unbuffered CL4 2Rx8",
	"HYMP125U72[A-G]?P8-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Unbuffered CL5 2Rx8",
	"HYMP125U72[A-G]?P8-S5" => "2GB PC2-6400 Hynix DDR2-800 ECC Unbuffered CL5 2Rx8",
	"HYMP125U72[A-G]?P8-S6" => "2GB PC2-6400 Hynix DDR2-800 ECC Unbuffered CL6 2Rx8",

	# http://hynix.com/datasheet/eng/module/module_sub.jsp?menu3=02&RK=08&SK=FB&RAM_NAME=DDR2%20SDRAM
	"HYMP564[BF]72[A-G]?P8D2-C4" => "512MB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 1Rx8",	# IDT A1.5 AMB
	"HYMP564[BF]72[A-G]?P8N2-C4" => "512MB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 1Rx8",	# Intel C0 AMB

	"HYMP564[BF]72[A-G]?P8D2-Y5" => "512MB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 1Rx8",	# IDT A1.5 AMB
	"HYMP564[BF]72[A-G]?P8N2-Y5" => "512MB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 1Rx8",	# Intel C0 AMB
	"HYMP564[BF]72[A-G]?P8N3-Y5" => "512MB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 1Rx8",	# Intel D1 AMB

	"HYMP512[BF]72[A-G]?P8D2-C4" => "1GB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 2Rx8",	# IDT A1.5 AMB
	"HYMP512[BF]72[A-G]?P8N2-C4" => "1GB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 2Rx8",	# Intel C0 AMB
	"HYMP512[BF]72[A-G]?P8N3-C4" => "1GB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 2Rx8",	# Intel D1 AMB

	"HYMP512[BF]72[A-G]?P8D2-Y5" => "1GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT A1.5 AMB
	"HYMP512[BF]72[A-G]?P8D3-Y5" => "1GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT C1 AMB
	"HYMP512[BF]72[A-G]?P8N2-Y5" => "1GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel C0 AMB
	"HYMP512[BF]72[A-G]?P8N3-Y5" => "1GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel D1 AMB
	"HYMP512[BF]72[A-G]?P8E4-Y5" => "1GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx8",	# NEC B5+ AMB

	"HYMP525[BF]72[A-G]?P4D2-C4" => "2GB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 2Rx4",	# IDT A1.5 AMB
	"HYMP525[BF]72[A-G]?P4N2-C4" => "2GB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 2Rx4",	# Intel C0 AMB
	"HYMP525[BF]72[A-G]?P4N3-C4" => "2GB PC2-4200 Hynix DDR2-533 ECC Fully Buffered CL4 2Rx4",	# Intel D1 AMB

	"HYMP525[BF]72[A-G]?P4D2-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4",	# IDT A1.5 AMB
	"HYMP525[BF]72[A-G]?P4D3-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4",	# IDT C1 AMB
	"HYMP525[BF]72[A-G]?P4N2-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel C0 AMB
	"HYMP525[BF]72[A-G]?P4N3-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel D1 AMB
	"HYMP525[BF]72[A-G]?P4E4-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4",	# NEC B5+ AMB

	"HYMP125[BF]72[A-G]?P8D3-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx8",	# IDT C1 AMB
	"HYMP125[BF]72[A-G]?P8N3-Y5" => "2GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx8",	# Intel D1 AMB

	"HYMP151[BF]72[A-G]?P4D3-Y5" => "4GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4",	# IDT C1 AMB
	"HYMP151[BF]72[A-G]?P4N3-Y5" => "4GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4",	# Intel D1 AMB
	"HYMP351[BF]72[A-G]?MP4N3Y5" => "4GB PC2-5300 Hynix DDR2-667 ECC Fully Buffered CL5 2Rx4 DDP",	# Intel D1 AMB

	# SODIMM
	"HYMP564S64[A-G]?P6-Y5" => "512MB PC2-5300 Hynix DDR2-667 Unbuffered CL5 2Rx16",
	"HYMP512S64[A-G]?P8-Y5" => "1GB PC2-5300 Hynix DDR2-667 Unbuffered CL5 2Rx8",

	# http://www.netlistinc.com/technology_products.aspx?sm=pr
	"NLD127R21203F-D32K" => "1GB PC2-3200 Netlist DDR2-400 ECC Registered CL3 1R",
	"NLD257R21203F-D32K" => "2GB PC2-3200 Netlist DDR2-400 ECC Registered CL3 2R",
	"NLD257R22503F-D32K" => "2GB PC2-3200 Netlist DDR2-400 ECC Registered CL3 2R",
	"NMD257A26407FD53N1" => "2GB PC2-5300 Netlist DDR2-667 ECC Fully Buffered CL5 4Rx8",
	"N.D517A21207FD53.." => "4GB PC2-5300 Netlist DDR2-667 ECC Fully Buffered CL5 4Rx8",

	# http://www.smartm.com/product/productCat.cfm?productCatID=1
	# http://www.smartupgradeconfigurator.com/config/
	"SX5643285D8N6CLICH" => "256MB PC-2700 Smart DDR-333 Unbuffered CL2.5",
	"SM5643285D8N6CLIBH" => "256MB PC-2700 Smart DDR-333 Unbuffered CL2.5",
	"SM5643285D8N6CHIBH" => "256MB PC-2100 Smart DDR-266 Unbuffered CL2.5",
	"SM5643285D8N6CHM1H" => "256MB PC-2100 Smart DDR-266 Unbuffered CL2.5",
	"SG2567UDR212852HC" => "2GB PC2-5300 Smart DDR2-667 ECC Unbuffered CL5 2Rx8",
	"SM5643285D8N6CHHAH" => "256MB PC-2100 Smart DDR-266 Unbuffered CL2.5",
	"SB2567RDR212835IA" => "2GB PC2-3200 Smart DDR2-400 ECC Registered CL3 2R",
	"SB1287RDR212435IA" => "1GB PC2-3200 Smart DDR2-400 ECC Registered CL3 1Rx4",
	"SB572284FG8E03BIAH" => "1GB PC2-3200 Smart DDR2-400 ECC Registered CL3 1Rx4",
	"SG572284FG8E0DBHBH" => "1GB PC2-3200 Smart DDR2-400 ECC Registered CL3 1Rx4",
	"SG5SC82N2G1CDNDSED" => "1GB PC2-5300 Smart DDR2-667 ECC Fully Buffered CL5",
	"SG5SD42N2G1CDNDSCD" => "2GB PC2-5300 Smart DDR2-667 ECC Fully Buffered CL5 1Rx4",
	"SG5SD42N2G1CDNDSED" => "2GB PC2-5300 Smart DDR2-667 ECC Fully Buffered CL5 1Rx4",
	"SG5SD42N2G1BDDEHCH" => "2GB PC2-5300 Smart DDR2-667 ECC Fully Buffered CL5 1Rx4",
	"SG2567RDR212452IB" => "2GB PC2-5300 Smart DDR2-667 ECC Fully Buffered CL5 2Rx4",
	"SG5SE84N2G1CDDGSD" => "4GB PC2-5300 Smart DDR2-667 ECC Fully Buffered CL5 2Rx4",
	"SG5127FBD12852HCDL" => "4GB PC2-5300 Smart DDR2-667 ECC Fully Buffered CL5 4Rx8",
);

my(%drive_models) = (
	# http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/knowndrives.cpp?view=log

	# Toshiba

	"MK4032GAX" => "40GB 5.4K ATA/100 2.5\" Toshiba 8MB",
	"MK4026GAX" => "40GB 5.4K ATA/100 2.5\" Toshiba 16MB",

	# Fujitsu
	# http://www.fujitsu.com/us/services/computing/storage/hdd/support/docs.html

	"MHS2020AT" => "20GB 4.2K ATA/100 2.5\" Fujitsu Mobile MHS 2MB",
	"MHS2030AT" => "30GB 4.2K ATA/100 2.5\" Fujitsu Mobile MHS 2MB",
	"MHS2040AT" => "40GB 4.2K ATA/100 2.5\" Fujitsu Mobile MHS 2MB",
	"MHS2060AT" => "60GB 4.2K ATA/100 2.5\" Fujitsu Mobile MHS 2MB",

	"MHV2040AH" => "40GB 5.4K ATA/100 2.5\" Fujitsu Mobile MHV2 AH 8MB",
	"MHV2060AH" => "60GB 5.4K ATA/100 2.5\" Fujitsu Mobile MHV2 AH 8MB",
	"MHV2080AH" => "80GB 5.4K ATA/100 2.5\" Fujitsu Mobile MHV2 AH 8MB",
	"MHV2100AH" => "100GB 5.4K ATA/100 2.5\" Fujitsu Mobile MHV2 AH 8MB",
	"MHV2120AH" => "120GB 5.4K ATA/100 2.5\" Fujitsu Mobile MHV2 AH 8MB",

	"MHV2040AS" => "40GB 5.4K ATA/100 2.5\" Fujitsu Extended Duty Mobile 8MB",
	"MHV2060AS" => "60GB 5.4K ATA/100 2.5\" Fujitsu Extended Duty Mobile 8MB",
	"MHV2080AS" => "80GB 5.4K ATA/100 2.5\" Fujitsu Extended Duty Mobile 8MB",

	"MHV2040BS" => "40GB 5.4K SATA/150 2.5\" Fujitsu Extended Duty Mobile 8MB",
	"MHV2060BS" => "60GB 5.4K SATA/150 2.5\" Fujitsu Extended Duty Mobile 8MB",
	"MHV2080BS" => "80GB 5.4K SATA/150 2.5\" Fujitsu Extended Duty Mobile 8MB",

	"MHV2040BH" => "40GB 5.4K SATA/150 2.5\" Fujitsu Mobile 8MB",
	"MHV2060BH" => "60GB 5.4K SATA/150 2.5\" Fujitsu Mobile 8MB",
	"MHV2080BH" => "80GB 5.4K SATA/150 2.5\" Fujitsu Mobile 8MB",
	"MHV2100BH" => "100GB 5.4K SATA/150 2.5\" Fujitsu Mobile 8MB",
	"MHV2120BH" => "120GB 5.4K SATA/150 2.5\" Fujitsu Mobile 8MB",

	# Samsung

	"SP0411N" => "40GB 7.2K ATA/133 Samsung SpinPoint PL40 2MB",
	"SP2514N" => "250GB 7.2K ATA/133 Samsung SpinPoint P120 8MB",
	"SP0411C" => "40GB 7.2K SATA/300 Samsung SpinPoint PL40 2MB",
	"HD040GJ" => "40GB 7.2K SATA/300 Samsung SpinPoint P80 SD 8MB",
	"HD080HJ" => "80GB 7.2K SATA/300 Samsung SpinPoint P80 SD 8MB",
	"HD160JJ" => "160GB 7.2K SATA/300 Samsung SpinPoint P80 SD 8MB",

	"HD041GJ" => "40GB 7.2K SATA/300 Samsung SpinPoint S166 2MB",
	"HD042GJ" => "40GB 7.2K SATA/300 Samsung SpinPoint S166 8MB",
	"HD081GJ" => "80GB 7.2K SATA/300 Samsung SpinPoint S166 2MB",
	"HD082GJ" => "80GB 7.2K SATA/300 Samsung SpinPoint S166 8MB",
	"HD120HJ" => "120GB 7.2K SATA/300 Samsung SpinPoint S166 8MB",
	"HD161HJ" => "160GB 7.2K SATA/300 Samsung SpinPoint S166 8MB",

	"HD080GJ" => "80GB 7.2K SATA/300 Samsung SpinPoint T166 8MB",
	"HD160HJ" => "160GB 7.2K SATA/300 Samsung SpinPoint T166 8MB",
	"HD320KJ" => "320GB 7.2K SATA/300 Samsung SpinPoint T166 8MB",
	"HD321KJ" => "320GB 7.2K SATA/300 Samsung SpinPoint T166 16MB",
	"HD500LJ" => "500GB 7.2K SATA/300 Samsung SpinPoint T166 8MB",
	"HD501LJ" => "500GB 7.2K SATA/300 Samsung SpinPoint T166 16MB",

	"HD161GJ" => "160GB 7.2K SATA/300 Samsung SpinPoint F1 8MB",
	"HD162GJ" => "160GB 7.2K SATA/300 Samsung SpinPoint F1 16MB",
	"HD251HJ" => "250GB 7.2K SATA/300 Samsung SpinPoint F1 8MB",
	"HD252HJ" => "250GB 7.2K SATA/300 Samsung SpinPoint F1 16MB",
	"HD321HJ" => "320GB 7.2K SATA/300 Samsung SpinPoint F1 8MB",
	"HD322HJ" => "320GB 7.2K SATA/300 Samsung SpinPoint F1 16MB",
	"HD501IJ" => "500GB 7.2K SATA/300 Samsung SpinPoint F1 8MB",
	"HD502IJ" => "500GB 7.2K SATA/300 Samsung SpinPoint F1 16MB",
	"HD642JJ" => "640GB 7.2K SATA/300 Samsung SpinPoint F1 16MB",
	"HD753LJ" => "750GB 7.2K SATA/300 Samsung SpinPoint F1 32MB",
	"HD103UJ" => "1TB 7.2K SATA/300 Samsung SpinPoint F1 32MB",

	# need more info on this model
	"HE160HJ" => "160GB 7.2K SATA/300 Samsung 8MB",

	"HE252HJ" => "250GB 7.2K SATA/300 Samsung SpinPoint F1 RAID 16MB",
	"HE322HJ" => "320GB 7.2K SATA/300 Samsung SpinPoint F1 RAID 16MB",
	"HE502IJ" => "500GB 7.2K SATA/300 Samsung SpinPoint F1 RAID 16MB",
	"HE753LJ" => "750GB 7.2K SATA/300 Samsung SpinPoint F1 RAID 32MB",
	"HE103UJ" => "1TB 7.2K SATA/300 Samsung SpinPoint F1 RAID 32MB",

	# http://www.wdc.com/en/products/productcatalog.asp?Language=en

	# http://websupport.wdc.com/products/pf.asp?fid=18
	"WD100EB" => "10GB 5.4K ATA/100 WD Protege 2MB",
	"WD200EB" => "20GB 5.4K ATA/100 WD Protege 2MB",
	"WD300EB" => "30GB 5.4K ATA/100 WD Protege 2MB",
	"WD400EB" => "40GB 5.4K ATA/100 WD Protege 2MB",
	"WD600EB" => "60GB 5.4K ATA/100 WD Protege 2MB",
	"WD800EB" => "80GB 5.4K ATA/100 WD Protege 2MB",

	"WD64AA" => "6GB 5.4K ATA/66 WD Caviar 2MB",
	"WD75AA" => "7GB 5.4K ATA/66 WD Caviar 2MB",
	"WD84AA" => "8GB 5.4K ATA/66 WD Caviar 2MB",
	"WD102AA" => "10GB 5.4K ATA/66 WD Caviar 2MB",
	"WD136AA" => "13GB 5.4K ATA/66 WD Caviar 2MB",
	"WD153AA" => "15GB 5.4K ATA/66 WD Caviar 2MB",
	"WD205AA" => "20GB 5.4K ATA/66 WD Caviar 2MB",
	"WD307AA" => "30GB 5.4K ATA/66 WD Caviar 2MB",
	"WD450AA" => "45GB 5.4K ATA/66 WD Caviar 2MB",

	"WD102BA" => "10GB 7.2K ATA/66 WD Caviar 2MB",
	"WD136BA" => "13GB 7.2K ATA/66 WD Caviar 2MB",
	"WD153BA" => "15GB 7.2K ATA/66 WD Caviar 2MB",
	"WD205BA" => "20GB 7.2K ATA/66 WD Caviar 2MB",

	"WD100AB" => "10GB 5.4K ATA/100 WD Caviar 2MB",
	"WD200AB" => "20GB 5.4K ATA/100 WD Caviar 2MB",
	"WD300AB" => "30GB 5.4K ATA/100 WD Caviar 2MB",
	"WD400AB" => "40GB 5.4K ATA/100 WD Caviar 2MB",
	"WD600AB" => "60GB 5.4K ATA/100 WD Caviar 2MB",
	"WD800AB" => "80GB 5.4K ATA/100 WD Caviar 2MB",
	"WD1000AB" => "100GB 5.4K ATA/100 WD Caviar 2MB",
	"WD1200AB" => "120GB 5.4K ATA/100 WD Caviar 2MB",
	"WD1600AB" => "160GB 5.4K ATA/100 WD Caviar 2MB",

	# http://websupport.wdc.com/products/pf.asp?fid=2
	"WD100BB" => "10GB 7.2K ATA/100 WD Caviar 2MB",
	"WD200BB" => "20GB 7.2K ATA/100 WD Caviar 2MB",
	"WD300BB" => "30GB 7.2K ATA/100 WD Caviar 2MB",
	"WD400BB" => "40GB 7.2K ATA/100 WD Caviar 2MB",
	"WD600BB" => "60GB 7.2K ATA/100 WD Caviar 2MB",
	"WD800BB" => "80GB 7.2K ATA/100 WD Caviar 2MB",
	"WD1000BB" => "100GB 7.2K ATA/100 WD Caviar 2MB",
	"WD1200BB" => "120GB 7.2K ATA/100 WD Caviar 2MB",
	"WD1600BB" => "160GB 7.2K ATA/100 WD Caviar 2MB",
	"WD1800BB" => "180GB 7.2K ATA/100 WD Caviar 2MB",
	"WD2000BB" => "200GB 7.2K ATA/100 WD Caviar 2MB",
	"WD2500BB" => "250GB 7.2K ATA/100 WD Caviar 2MB",
	"WD3000BB" => "300GB 7.2K ATA/100 WD Caviar 2MB",
	"WD3200BB" => "320GB 7.2K ATA/100 WD Caviar 2MB",

	# http://websupport.wdc.com/products/pf.asp?fid=6
	"WD400JB" => "40GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD800JB" => "80GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD1000JB" => "100GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD1200JB" => "120GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD1600JB" => "160GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD1800JB" => "180GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD2000JB" => "200GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD2500JB" => "250GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD3000JB" => "300GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD3200JB" => "320GB 7.2K ATA/100 WD Caviar SE 8MB",

	# same as JB drives above
	"WD800AAJB" => "80GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD1600AAJB" => "160GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD2500AAJB" => "250GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD3200AAJB" => "320GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD4000AAJB" => "400GB 7.2K ATA/100 WD Caviar SE 8MB",
	"WD5000AAJB" => "500GB 7.2K ATA/100 WD Caviar SE 8MB",

	# same as BB line with liquid bearing?
	"WD800LB" => "80GB 7.2K ATA/100 WD Caviar 2MB",
	"WD1200LB" => "120GB 7.2K ATA/100 WD Caviar 2MB",
	"WD1600LB" => "160GB 7.2K ATA/100 WD Caviar 2MB",

	# PB drives sold in korea only?
	"WD800PB" => "80GB 7.2K ATA/100 WD Caviar",

	"WD1200JD" => "120GB 7.2K SATA/150 WD Caviar SE 8MB",
	"WD1600JD" => "160GB 7.2K SATA/150 WD Caviar SE 8MB",
	"WD2000JD" => "200GB 7.2K SATA/150 WD Caviar SE 8MB",
	"WD2500JD" => "250GB 7.2K SATA/150 WD Caviar SE 8MB",
	"WD3000JD" => "300GB 7.2K SATA/150 WD Caviar SE 8MB",
	"WD3200JD" => "320GB 7.2K SATA/150 WD Caviar SE 8MB",

	"WD400BD" => "40GB 7.2K SATA/150 WD Caviar",
	"WD800BD" => "80GB 7.2K SATA/150 WD Caviar",

	"WD400PD" => "40GB 7.2K SATA/150 WD Caviar SE",
	"WD800PD" => "80GB 7.2K SATA/150 WD Caviar SE",
	"WD1200PD" => "120GB 7.2K SATA/150 WD Caviar SE",
	"WD1600PD" => "160GB 7.2K SATA/150 WD Caviar SE",
	"WD2000PD" => "200GB 7.2K SATA/150 WD Caviar SE",
	"WD2500PD" => "250GB 7.2K SATA/150 WD Caviar SE",

	"WD400JD" => "40GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD800JD" => "80GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD1200JS" => "120GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD1600JS" => "160GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD2000JS" => "200GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD2500JS" => "250GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD3000JS" => "300GB 7.2K SATA/300 WD Caviar SE 8MB",

	"WD800AAJS" => "80GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD1600AAJS" => "160GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD2500AAJS" => "250GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD3200AAJS" => "320GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD4000AAJS" => "400GB 7.2K SATA/300 WD Caviar SE 8MB",
	"WD5000AAJS" => "500GB 7.2K SATA/300 WD Caviar SE 8MB",

	"WD1600SB" => "160GB 7.2K ATA/100 WD Caviar RE 8MB",
	"WD2500SB" => "250GB 7.2K ATA/100 WD Caviar RE 8MB",
	"WD3200SB" => "320GB 7.2K ATA/100 WD Caviar RE 8MB",

	"WD1200SD" => "120GB 7.2K SATA/150 WD Caviar RE 8MB",
	"WD1600SD" => "160GB 7.2K SATA/150 WD Caviar RE 8MB",
	"WD2500SD" => "250GB 7.2K SATA/150 WD Caviar RE 8MB",
	"WD3200SD" => "320GB 7.2K SATA/150 WD Caviar RE 8MB",

	"WD1600YD" => "160GB 7.2K SATA/300 WD Caviar RE 16MB",
	"WD2500YD" => "250GB 7.2K SATA/300 WD Caviar RE 16MB",

	"WD4000YR" => "400GB 7.2K SATA/150 WD Caviar RE2 16MB",

	"WD2500KS" => "250GB 7.2K SATA/300 WD Caviar SE16 16MB",
	"WD3200KS" => "320GB 7.2K SATA/300 WD Caviar SE16 16MB",
	"WD4000KS" => "400GB 7.2K SATA/300 WD Caviar SE16 16MB",
	"WD5000KS" => "500GB 7.2K SATA/300 WD Caviar SE16 16MB",

	"WD3200AAKB" => "320GB 7.2K ATA/100 WD Caviar SE16 16MB",	# Now called "Caviar Blue"
	"WD4000AAKB" => "400GB 7.2K ATA/100 WD Caviar SE16 16MB",
	"WD5000AAKB" => "500GB 7.2K ATA/100 WD Caviar SE16 16MB",

	"WD2500AAKS" => "250GB 7.2K SATA/300 WD Caviar SE16 16MB",
	"WD3200AAKS" => "320GB 7.2K SATA/300 WD Caviar SE16 16MB",
	"WD4000AAKS" => "400GB 7.2K SATA/300 WD Caviar SE16 16MB",
	"WD5000AAKS" => "500GB 7.2K SATA/300 WD Caviar SE16 16MB",
	"WD7500AAKS" => "750GB 7.2K SATA/300 WD Caviar SE16 16MB",

	"WD5000AACS" => "500GB 7.2K SATA/300 WD Caviar GP 16MB",
	"WD7500AACS" => "750GB 7.2K SATA/300 WD Caviar GP 16MB",
	"WD10EACS" => "1TB 7.2K SATA/300 WD Caviar GP 16MB",

	# Enterprise drives

	"WD1600YS" => "160GB 7.2K SATA/300 WD RE 16MB",
	"WD2500YS" => "250GB 7.2K SATA/300 WD RE 16MB",
	"WD3200YS" => "320GB 7.2K SATA/300 WD RE 16MB",

	"WD1601ABYS" => "160GB 7.2K SATA/300 WD RE2 16MB",
	"WD3201ABYS" => "320GB 7.2K SATA/300 WD RE2 16MB",
	"WD4000YS" => "400GB 7.2K SATA/300 WD RE2 16MB",
	"WD4000ABYS" => "400GB 7.2K SATA/300 WD RE2 16MB",
	"WD4001ABYS" => "400GB 7.2K SATA/300 WD RE2 16MB",
	"WD5000YS" => "500GB 7.2K SATA/300 WD RE2 16MB",
	"WD5000ABYS" => "500GB 7.2K SATA/300 WD RE2 16MB",
	"WD5001ABYS" => "500GB 7.2K SATA/300 WD RE2 16MB",
	"WD7500AYYS" => "750GB 7.2K SATA/300 WD RE2 16MB",

	"WD2502ABYS" => "250GB 7.2K SATA/300 WD RE3 16MB",
	"WD3202ABYS" => "320GB 7.2K SATA/300 WD RE3 16MB",
	"WD5002ABYS" => "500GB 7.2K SATA/300 WD RE3 16MB",
	"WD7502ABYS" => "750GB 7.2K SATA/300 WD RE3 32MB",
	"WD1002FBYS" => "1TB 7.2K SATA/300 WD RE3 32MB",

	"WD5000ABPS" => "500GB 7.2K SATA/300 WD RE2-GP 16MB",
	"WD7500AYPS" => "750GB 7.2K SATA/300 WD RE2-GP 16MB",
	"WD1000FYPS" => "1TB 7.2K SATA/300 WD RE2-GP 16MB",

	# Hitachi

	"DK32DJ-18M" => "18GB 10K U320 Hitachi Ultrastar",
	"DK32DJ-36M" => "36GB 10K U320 Hitachi Ultrastar",
	"DK32DJ-72M" => "73GB 10K U320 Hitachi Ultrastar",

	"DK32EJ-36N" => "36GB 10K U320 Hitachi Ultrastar",
	"DK32EJ-72N" => "73GB 10K U320 Hitachi Ultrastar",
	"DK32EJ-14N" => "147GB 10K U320 Hitachi Ultrastar",

	"DNES-309170W" => "9GB 7.2K Ultra IBM Ultrastar 18ES 2MB",
	"DNES-318350W" => "18GB 7.2K Ultra IBM Ultrastar 18ES 2MB",

	"DPSS-309170" => "9GB 10K U160 IBM Ultrastar 36LP",
	"DPSS-318350" => "18GB 10K U160 IBM Ultrastar 36LP",
	"DPSS-336950" => "36GB 10K U160 IBM Ultrastar 36LP",

	"DMVS18M" => "18GB 10K U160 IBM",

	"DMVS18V" => "18GB 10K Ultra2 IBM Ultrastar 18LZX",

	"DDYS-T09170" => "9GB 10K U160 IBM Ultrastar 36LZX",
	"DDYS-T18350" => "18GB 10K U160 IBM Ultrastar 36LZX",
	"DDYS-T36950" => "36GB 10K U160 IBM Ultrastar 36LZX",

	"IC35L009UWD210" => "9GB 10K U160 IBM Ultrastar 73LZX",
	"IC35L018UWD210" => "18GB 10K U160 IBM Ultrastar 73LZX",
	"IC35L036UWD210" => "36GB 10K U160 IBM Ultrastar 73LZX",
	"IC35L073UWD210" => "73GB 10K U160 IBM Ultrastar 73LZX",

	"IC35L009XWD210" => "9GB 10K U320 IBM Ultrastar 73LZX",
	"IC35L018XWD210" => "18GB 10K U320 IBM Ultrastar 73LZX",
	"IC35L036XWD210" => "36GB 10K U320 IBM Ultrastar 73LZX",
	"IC35L073XWD210" => "73GB 10K U320 IBM Ultrastar 73LZX",

	"IC35L018U[CW]DY10" => "18GB 10K U320 IBM Ultrastar 146Z10",
	"IC35L036U[CW]DY10" => "36GB 10K U320 IBM Ultrastar 146Z10",
	"IC35L073U[CW]DY10" => "73GB 10K U320 IBM Ultrastar 146Z10",
	"IC35L146U[CW]DY10" => "146GB 10K U320 IBM Ultrastar 146Z10",

	"HUS151436VL3[68]00" => "36GB 15K U320 Hitachi Ultrastar 15K147",
	"HUS151473VL3[68]00" => "73GB 15K U320 Hitachi Ultrastar 15K147",
	"HUS151414VL3[68]00" => "147GB 15K U320 Hitachi Ultrastar 15K147",

	"HUS151436VLS300" => "36GB 15K SAS/3 Hitachi Ultrastar 15K147 16MB",
	"HUS151473VLS300" => "73GB 15K SAS/3 Hitachi Ultrastar 15K147 16MB",
	"HUS151414VLS300" => "147GB 15K SAS/3 Hitachi Ultrastar 15K147 16MB",

	"HUS153073VLS300" => "73GB 15K SAS/3 Hitachi Ultrastar 15K300 16MB",
	"HUS153014VLS300" => "147GB 15K SAS/3 Hitachi Ultrastar 15K300 16MB",
	"HUS153030VLS300" => "300GB 15K SAS/3 Hitachi Ultrastar 15K300 16MB",

	"HUC101473CSS300" => "73GB 10K SAS/3 2.5\" Hitachi Ultrastar C10K147",
	"HUC101414CSS300" => "147GB 10K SAS/3 2.5\" Hitachi Ultrastar C10K147",

	"DHEA-34330" => "4GB 5.4K ATA/33 IBM Desktar 5",
	"DHEA-36480" => "6GB 5.4K ATA/33 IBM Desktar 5",

	"DHEA-34331" => "4GB 5.4K ATA/33 IBM Desktar 8",
	"DHEA-36481" => "6GB 5.4K ATA/33 IBM Desktar 8",
	"DHEA-38451" => "8GB 5.4K ATA/33 IBM Desktar 8",

	"DPTA-371360" => "14GB 7.2K ATA/66 IBM Deskstar 34GXP",
	"DPTA-372050" => "20GB 7.2K ATA/66 IBM Deskstar 34GXP",
	"DPTA-372730" => "27GB 7.2K ATA/66 IBM Deskstar 34GXP",
	"DPTA-373420" => "34GB 7.2K ATA/66 IBM Deskstar 34GXP",

	"HDS728040PLAT20" => "40GB 7.2K ATA/133 Hitachi Deskstar 7K80",
	"HDS728080PLAT20" => "80GB 7.2K ATA/133 Hitachi Deskstar 7K80",

	"HTS548020M9AT00" => "20GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K80 8MB",
	"HTS548040M9AT00" => "40GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K80 8MB",
	"HTS548060M9AT00" => "60GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K80 8MB",
	"HTS548080M9AT00" => "80GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K80 8MB",

	"HTS541640J9AT00" => "40GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541660J9AT00" => "60GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541680J9AT00" => "80GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541612J9AT00" => "120GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541616J9AT00" => "160GB 5.4K ATA/100 2.5\" Hitachi Travelstar 5K160 8MB",

	"HTS541640J9SA00" => "40GB 5.4K SATA/150 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541660J9SA00" => "60GB 5.4K SATA/150 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541680J9SA00" => "80GB 5.4K SATA/150 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541612J9SA00" => "120GB 5.4K SATA/150 2.5\" Hitachi Travelstar 5K160 8MB",
	"HTS541616J9SA00" => "160GB 5.4K SATA/150 2.5\" Hitachi Travelstar 5K160 8MB",

	"HTE542580K9A300" => "80GB 5.4K SATA/300 2.5\" Hitachi Travelstar E5K250 8MB",
	"HTE542512K9A300" => "120GB 5.4K SATA/300 2.5\" Hitachi Travelstar E5K250 8MB",
	"HTE542516K9A300" => "160GB 5.4K SATA/300 2.5\" Hitachi Travelstar E5K250 8MB",
	"HTE542525K9A300" => "250GB 5.4K SATA/300 2.5\" Hitachi Travelstar E5K250 8MB",

	"HTS721060G9AT00" => "60GB 7.2K ATA/100 2.5\" Hitachi Travelstar 7K100 8MB",
	"HTS721080G9AT00" => "80GB 7.2K ATA/100 2.5\" Hitachi Travelstar 7K100 8MB",
	"HTS721010G9AT00" => "100GB 7.2K ATA/100 2.5\" Hitachi Travelstar 7K100 8MB",

	"HTS721060G9SA00" => "60GB 7.2K SATA/150 2.5\" Hitachi Travelstar 7K100 8MB",
	"HTS721080G9SA00" => "80GB 7.2K SATA/150 2.5\" Hitachi Travelstar 7K100 8MB",
	"HTS721010G9SA00" => "100GB 7.2K SATA/150 2.5\" Hitachi Travelstar 7K100 8MB",


	"HDS721680PLA380" => "80GB 7.2K SATA/300 Hitachi Deskstar 7K160",
	"HDS721616PLA380" => "160GB 7.2K SATA/300 Hitachi Deskstar 7K160",

	"HDS722540VLAT20" => "40GB 7.2K ATA/100 Hitachi Deskstar 7K250 2MB",
	"HDS722580VLAT20" => "80GB 7.2K ATA/100 Hitachi Deskstar 7K250 2MB",
	"HDS722512VLAT20" => "120GB 7.2K ATA/100 Hitachi Deskstar 7K250 2MB",
	"HDS722512VLAT80" => "120GB 7.2K ATA/100 Hitachi Deskstar 7K250 8MB",
	"HDS722516VLAT20" => "160GB 7.2K ATA/100 Hitachi Deskstar 7K250 2MB",
	"HDS722516VLAT80" => "160GB 7.2K ATA/100 Hitachi Deskstar 7K250 8MB",
	"HDS722525VLAT80" => "250GB 7.2K ATA/100 Hitachi Deskstar 7K250 8MB",

	"HDS722540VLSA80" => "40GB 7.2K SATA/150 Hitachi Deskstar 7K250 8MB",
	"HDS722580VLSA80" => "80GB 7.2K SATA/150 Hitachi Deskstar 7K250 8MB",
	"HDS722512VLSA80" => "120GB 7.2K SATA/150 Hitachi Deskstar 7K250 8MB",
	"HDS722516VLSA80" => "160GB 7.2K SATA/150 Hitachi Deskstar 7K250 8MB",
	"HDS722525VLSA80" => "250GB 7.2K SATA/150 Hitachi Deskstar 7K250 8MB",

	"HDS724040KLAT80" => "400GB 7.2K ATA/133 Hitachi Deskstar 7K400 8MB",
	"HDS724040KLSA80" => "400GB 7.2K SATA/150 Hitachi Deskstar 7K400 8MB",

	"HDS725050KLAT80" => "500GB 7.2K ATA/133 Hitachi Deskstar 7K500 8MB",

	"HDS725050KLA361" => "400GB 7.2K SATA/300 Hitachi Deskstar E7K500 16MB",
	"HDS725050KLA360" => "500GB 7.2K SATA/300 Hitachi Deskstar E7K500 16MB",

	"HDP725016GLA380" => "160GB 7.2K SATA/300 Hitachi Deskstar P7K500 8MB",
	"HDP725025GLA380" => "250GB 7.2K SATA/300 Hitachi Deskstar P7K500 8MB",
	"HDP725032GLA380" => "320GB 7.2K SATA/300 Hitachi Deskstar P7K500 8MB",
	"HDP725032GLA360" => "320GB 7.2K SATA/300 Hitachi Deskstar P7K500 16MB",
	"HDP725040GLA380" => "400GB 7.2K SATA/300 Hitachi Deskstar P7K500 8MB",
	"HDP725040GLA360" => "400GB 7.2K SATA/300 Hitachi Deskstar P7K500 16MB",
	"HDP725050GLA380" => "500GB 7.2K SATA/300 Hitachi Deskstar P7K500 8MB",
	"HDP725050GLA360" => "500GB 7.2K SATA/300 Hitachi Deskstar P7K500 16MB",

	"HDT725025VLA380" => "250GB 7.2K SATA/300 Hitachi Deskstar T7K500 8MB",
	"HDT725025VLA360" => "250GB 7.2K SATA/300 Hitachi Deskstar T7K500 16MB",
	"HDT725032VLA380" => "320GB 7.2K SATA/300 Hitachi Deskstar T7K500 8MB",
	"HDT725032VLA360" => "320GB 7.2K SATA/300 Hitachi Deskstar T7K500 16MB",
	"HDT725040VLA380" => "400GB 7.2K SATA/300 Hitachi Deskstar T7K500 8MB",
	"HDT725040VLA360" => "400GB 7.2K SATA/300 Hitachi Deskstar T7K500 16MB",
	"HDT725050VLA380" => "500GB 7.2K SATA/300 Hitachi Deskstar T7K500 8MB",
	"HDT725050VLA360" => "500GB 7.2K SATA/300 Hitachi Deskstar T7K500 16MB",

	"HDS721075KLA330" => "750GB 7.2K SATA/300 Hitachi Deskstar 7K1000 32MB",
	"HDS721010KLA330" => "1TB 7.2K SATA/300 Hitachi Deskstar 7K1000 32MB",

	"HDT721016SLA380" => "160GB 7.2K SATA/300 Hitachi Deskstar 7K1000.B 8MB",
	"HDT721025SLA380" => "250GB 7.2K SATA/300 Hitachi Deskstar 7K1000.B 8MB",
	"HDT721032SLA360" => "320GB 7.2K SATA/300 Hitachi Deskstar 7K1000.B 8MB",
	"HDT721050SLA360" => "500GB 7.2K SATA/300 Hitachi Deskstar 7K1000.B 8MB",
	"HDT721064SLA360" => "640GB 7.2K SATA/300 Hitachi Deskstar 7K1000.B 8MB",
	"HDT721075SLA360" => "750GB 7.2K SATA/300 Hitachi Deskstar 7K1000.B 8MB",
	"HDT721010SLA360" => "1TB 7.2K SATA/300 Hitachi Deskstar 7K1000.B 8MB",

	"HDE721050SLA330" => "500GB 7.2K SATA/300 Hitachi Deskstar E7K1000 32MB",
	"HDE721075SLA330" => "750GB 7.2K SATA/300 Hitachi Deskstar E7K1000 32MB",
	"HDE721010SLA330" => "1TB 7.2K SATA/300 Hitachi Deskstar E7K1000 32MB",

	"HUA721050KLA330" => "500GB 7.2K SATA/300 Hitachi Ultrastar A7K1000 32MB",
	"HUA721075KLA330" => "750GB 7.2K SATA/300 Hitachi Ultrastar A7K1000 32MB",
	"HUA72107A74A" => "750GB 7.2K SATA/300 Hitachi Ultrastar A7K1000 32MB",		# megacli on momentum-pr-q95.data.corp.sp1.yahoo.com
	"HUA721010KLA330" => "1TB 7.2K SATA/300 Hitachi Ultrastar A7K1000 32MB",

	"IC35L010AVER07" => "10GB 7.2K ATA/100 IBM Deskstar 60GXP",
	"IC35L020AVER07" => "21GB 7.2K ATA/100 IBM Deskstar 60GXP",
	"IC35L030AVER07" => "31GB 7.2K ATA/100 IBM Deskstar 60GXP",
	"IC35L040AVER07" => "41GB 7.2K ATA/100 IBM Deskstar 60GXP",
	"IC35L060AVER07" => "61GB 7.2K ATA/100 IBM Deskstar 60GXP",

	"IC35L020AVVA07" => "20GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L020AVVN07" => "20GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L040AVVA07" => "40GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L040AVVN07" => "40GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L060AVVA07" => "60GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L060AVVN07" => "60GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L080AVVA07" => "80GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L080AVVN07" => "80GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L100AVVN07" => "100GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L100AVVA07" => "100GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L120AVVN07" => "120GB 7.2K ATA/100 IBM Deskstar 120GXP",
	"IC35L120AVVA07" => "120GB 7.2K ATA/100 IBM Deskstar 120GXP",

	"IC35L030AVV207" => "30GB 7.2K ATA/100 IBM Deskstar 180GXP",
	"IC35L060AVV207" => "40GB 7.2K ATA/100 IBM Deskstar 180GXP",
	"IC35L090AVV207" => "80GB 7.2K ATA/100 IBM Deskstar 180GXP",
	"IC35L120AVV207" => "120GB 7.2K ATA/100 IBM Deskstar 180GXP",
	"IC35L180AVV207" => "180GB 7.2K ATA/100 IBM Deskstar 180GXP",

	"DJNA-351010" => "10GB 5.4K ATA/66 IBM Deskstar 25GP",
	"DJNA-351520" => "15GB 5.4K ATA/66 IBM Deskstar 25GP",
	"DJNA-352030" => "20GB 5.4K ATA/66 IBM Deskstar 25GP",
	"DJNA-352500" => "25GB 5.4K ATA/66 IBM Deskstar 25GP",

	"DJNA-379100" => "9GB 7.2K ATA/66 IBM Deskstar 22GXP",
	"DJNA-371350" => "13GB 7.2K ATA/66 IBM Deskstar 22GXP",
	"DJNA-371800" => "18GB 7.2K ATA/66 IBM Deskstar 22GXP",
	"DJNA-372200" => "22GB 7.2K ATA/66 IBM Deskstar 22GXP",

	"DTTA-350320" => "3GB 5.4K ATA/33 IBM Deskstar 16GP",
	"DTTA-350403" => "4GB 5.4K ATA/33 IBM Deskstar 16GP",
	"DTTA-350640" => "6GB 5.4K ATA/33 IBM Deskstar 16GP",
	"DTTA-350840" => "8GB 5.4K ATA/33 IBM Deskstar 16GP",
	"DTTA-351010" => "10GB 5.4K ATA/33 IBM Deskstar 16GP",
	"DTTA-351290" => "12GB 5.4K ATA/33 IBM Deskstar 16GP",
	"DTTA-351680" => "16GB 5.4K ATA/33 IBM Deskstar 16GP",

	"DTTA-371010" => "10GB 7.2K ATA/33 IBM Deskstar 14GXP",
	"DTTA-371290" => "12GB 7.2K ATA/33 IBM Deskstar 14GXP",
	"DTTA-371440" => "14GB 7.2K ATA/33 IBM Deskstar 14GXP",

	"DTLA-305010" => "10GB 5.4K ATA/100 IBM Deskstar 40GV",
	"DTLA-305020" => "20GB 5.4K ATA/100 IBM Deskstar 40GV",
	"DTLA-305030" => "30GB 5.4K ATA/100 IBM Deskstar 40GV",
	"DTLA-305040" => "40GB 5.4K ATA/100 IBM Deskstar 40GV",

	"DTLA-307015" => "15GB 7.2K ATA/100 IBM Deskstar 75GXP",
	"DTLA-307020" => "20GB 7.2K ATA/100 IBM Deskstar 75GXP",
	"DTLA-307030" => "30GB 7.2K ATA/100 IBM Deskstar 75GXP",
	"DTLA-307045" => "45GB 7.2K ATA/100 IBM Deskstar 75GXP",
	"DTLA-307060" => "60GB 7.2K ATA/100 IBM Deskstar 75GXP",
	"DTLA-307075" => "75GB 7.2K ATA/100 IBM Deskstar 75GXP",

	"ST34501" => "4GB 10K Ultra Seagate Cheetah 4LP",

	"ST34573" => "4GB 7.2K Ultra2 Seagate Barracuda 9LP",
	"ST39173" => "9GB 7.2K Ultra2 Seagate Barracuda 9LP",

	"ST118273" => "18GB 7.2K Ultra2 Seagate Barracuda 18",

	"ST39175" => "9GB 7.2K Ultra2 Seagate Barracuda 18LP",
	"ST318275" => "18GB 7.2K Ultra2 Seagate Barracuda 18LP",

	"ST39236" => "9GB 7.2K U160 Seagate Barracuda 18XL",
	"ST318436" => "18GB 7.2K U160 Seagate Barracuda 18XL",

	"ST150176" => "50GB 7.2K Ultra2 Seagate Barracuda 50",

	"ST39102" => "9GB 10K Ultra2 Seagate Cheetah 9LP",

	"ST39103" => "9GB 10K Ultra2 Seagate Cheetah 18LP",
	"ST318203" => "18GB 10K Ultra2 Seagate Cheetah 18LP",

	"ST39204" => "9GB 10K U160 Seagate Cheetah 18XL",
	"ST318404" => "18GB 10K U160 Seagate Cheetah 18XL",
	"ST336704" => "36GB 10K U160 Seagate Cheetah 36LP",

	"ST318305" => "18GB 10K U160 Seagate Cheetah 18XL",

	"ST318405" => "18GB 10K U160 Seagate 36XL",
	"ST336705" => "36GB 10K U160 Seagate 36XL",

	"ST336605" => "36GB 10K U160 Seagate 73LP",
	"ST373405" => "73GB 10K U160 Seagate 73LP",

	"ST318406" => "18GB 10K U160 Seagate 36ES",
	"ST336706" => "37GB 10K U160 Seagate 36ES",

	"ST336607" => "37GB 10K U320 Seagate 10K.6",
	"ST373307" => "73GB 10K U320 Seagate 10K.6",
	"ST3146807" => "147GB 10K U320 Seagate 10K.6",

	"ST336807" => "36GB 10K U320 Seagate 10K.7",
	"ST373207" => "72GB 10K U320 Seagate 10K.7",
	"ST3146707" => "146GB 10K U320 Seagate 10K.7",
	"ST3300007" => "300GB 10K U320 Seagate 10K.7",

	"ST373355SS" => "72GB 15K SAS/3 Seagate T10",
	"ST3146755SS" => "146GB 15K SAS/3 Seagate T10",
	"ST3300555SS" => "300GB 15K SAS/3 Seagate T10",

	"ST318452" => "18GB 15K U160 Seagate X15 36LP",
	"ST336752" => "36GB 15K U160 Seagate X15 36LP",

	"ST318432" => "18GB 15K U320 Seagate X15 36LP",
	"ST336732" => "36GB 15K U320 Seagate X15 36LP",

	"ST318453" => "18GB 15K U320 Seagate 15K.3",
	"ST336753" => "37GB 15K U320 Seagate 15K.3",
	"ST373453" => "73GB 15K U320 Seagate 15K.3",

	"ST336754SS" => "37GB 15K SAS/3 Seagate 15K.4",
	"ST373454SS" => "73GB 15K SAS/3 Seagate 15K.4",
	"ST3146854SS" => "146GB 15K SAS/3 Seagate 15K.4",

	"ST336754" => "37GB 15K U320 Seagate 15K.4",
	"ST373454" => "73GB 15K U320 Seagate 15K.4",
	"ST3146854" => "146GB 15K U320 Seagate 15K.4",

	"ST373455LC" => "73GB 15K U320 Seagate 15K.5",
	"ST3146855LC" => "146GB 15K U320 Seagate 15K.5",
	"ST3300655LC" => "300GB 15K U320 Seagate 15K.5",

	"ST373455SS" => "73GB 15K SAS/3 Seagate 15K.5",
	"ST3146855SS" => "146GB 15K SAS/3 Seagate 15K.5",
	"ST3300655SS" => "300GB 15K SAS/3 Seagate 15K.5",

	"ST3400755SS" => "400GB 10K SAS/3 Seagate Cheetah NS",

	"ST3146356SS" => "146GB 15K SAS/3 Seagate 15K.6",
	"ST3300656SS" => "300GB 15K SAS/3 Seagate 15K.6",
	"ST3450856SS" => "450GB 15K SAS/3 Seagate 15K.6",

	"ST34520A" => "4GB 7.2K ATA/33 Seagate Medalist Pro",
	"ST36530A" => "6GB 7.2K ATA/33 Seagate Medalist Pro",
	"ST39140A" => "9GB 7.2K ATA/33 Seagate Medalist Pro",

	"ST310211A" => "10GB 5.4K ATA/100 Seagate U5",
	"ST315311A" => "15GB 5.4K ATA/100 Seagate U5",
	"ST320413A" => "20GB 5.4K ATA/100 Seagate U5",
	"ST330621A" => "30GB 5.4K ATA/100 Seagate U5",
	"ST340823A" => "40GB 5.4K ATA/100 Seagate U5",

	"ST320423A" => "10GB 5.4K ATA/66 Seagate U10",
	"ST315323A" => "15GB 5.4K ATA/66 Seagate U10",
	"ST310212A" => "20GB 5.4K ATA/66 Seagate U10",

	"ST36810A" => "7GB 7.2K ATA/66 Seagate Barracuda",
	"ST310220A" => "10GB 7.2K ATA/66 Seagate Barracuda",
	"ST313620A" => "14GB 7.2K ATA/66 Seagate Barracuda",
	"ST320430A" => "20GB 7.2K ATA/66 Seagate Barracuda",
	"ST328040A" => "28GB 7.2K ATA/66 Seagate Barracuda",

	"ST310210A" => "10GB 7.2K ATA/66 Seagate Barracuda II",
	"ST315320A" => "15GB 7.2K ATA/66 Seagate Barracuda II",
	"ST320420A" => "20GB 7.2K ATA/66 Seagate Barracuda II",
	"ST330630A" => "30GB 7.2K ATA/66 Seagate Barracuda II",

	"ST310216A" => "10GB 7.2K ATA/100 Seagate Barracuda II 100",
	"ST315324A" => "15GB 7.2K ATA/100 Seagate Barracuda II 100",
	"ST320424A" => "20GB 7.2K ATA/100 Seagate Barracuda II 100",
	"ST330631A" => "30GB 7.2K ATA/100 Seagate Barracuda II 100",

	"ST320410A" => "20GB 5.4K ATA/100 Seagate U Series",
	"ST330610A" => "30GB 5.4K ATA/100 Seagate U Series",
	"ST340810A" => "40GB 5.4K ATA/100 Seagate U Series",
	"ST360020A" => "60GB 5.4K ATA/100 Seagate U Series",
	"ST380020A" => "80GB 5.4K ATA/100 Seagate U Series",

	"ST310215A" => "10GB 7.2K ATA/100 Seagate Barracuda III",
	"ST315310A" => "15GB 7.2K ATA/100 Seagate Barracuda III",
	"ST320414A" => "20GB 7.2K ATA/100 Seagate Barracuda III",
	"ST330620A" => "30GB 7.2K ATA/100 Seagate Barracuda III",
	"ST340824A" => "40GB 7.2K ATA/100 Seagate Barracuda III",

	"ST320011A" => "20GB 7.2K ATA/100 Seagate Barracuda IV",
	"ST340016A" => "40GB 7.2K ATA/100 Seagate Barracuda IV",
	"ST360021A" => "60GB 7.2K ATA/100 Seagate Barracuda IV",
	"ST380021A" => "80GB 7.2K ATA/100 Seagate Barracuda IV",

	"ST340017A" => "40GB 7.2K ATA/100 Seagate Barracuda V",
	"ST360015A" => "60GB 7.2K ATA/100 Seagate Barracuda V",
	"ST380023A" => "80GB 7.2K ATA/100 Seagate Barracuda V",
	"ST3120023A" => "120GB 7.2K ATA/100 Seagate Barracuda V",

	"ST340014A" => "40GB 7.2K ATA/100 Seagate 7200.7",
	"ST380011A" => "80GB 7.2K ATA/100 Seagate 7200.7",
	"ST380013A" => "80GB 7.2K ATA/100 Seagate 7200.7",
	"ST3120022A" => "120GB 7.2K ATA/100 Seagate 7200.7",
	"ST3160021A" => "160GB 7.2K ATA/100 Seagate 7200.7",

	"ST3120026A" => "120GB 7.2K ATA/100 Seagate 7200.7 Plus",
	"ST3160023A" => "160GB 7.2K ATA/100 Seagate 7200.7 Plus",
	"ST3200021A" => "200GB 7.2K ATA/100 Seagate 7200.7 Plus",
	"ST3200822A" => "200GB 7.2K ATA/100 Seagate 7200.7 Plus",

	"ST340014AS" => "40GB 7.2K SATA/150 Seagate 7200.7 2MB",
	"ST380011AS" => "80GB 7.2K SATA/150 Seagate 7200.7 2MB",
	"ST3120022AS" => "120GB 7.2K SATA/150 Seagate 7200.7 2MB",
	"ST3160021AS" => "160GB 7.2K SATA/150 Seagate 7200.7 2MB",

	"ST380013AS" => "80GB 7.2K SATA/150 Seagate 7200.7 8MB",
	"ST3120026AS" => "120GB 7.2K SATA/150 Seagate 7200.7 8MB",
	"ST3160023AS" => "160GB 7.2K SATA/150 Seagate 7200.7 8MB",
	"ST3200822AS" => "200GB 7.2K SATA/150 Seagate 7200.7 8MB",

	"ST380817AS" => "80GB 7.2K SATA/150 Seagate 7200.7 NCQ 8MB",
	"ST3120827AS" => "120GB 7.2K SATA/150 Seagate 7200.7 NCQ 8MB",
	"ST3160827AS" => "160GB 7.2K SATA/150 Seagate 7200.7 NCQ 8MB",

	# Dell specials?
	"ST340212AS" => "40GB 7.2K SATA/150 Seagate 7200.7 NCQ 2MB",
	"ST340812AS" => "40GB 7.2K SATA/150 Seagate 7200.7 NCQ 8MB",
	"ST380219AS" => "80GB 7.2K SATA/150 Seagate 7200.7 NCQ 2MB",
	"ST380819AS" => "80GB 7.2K SATA/150 Seagate 7200.7 NCQ 8MB",
	"ST3120228AS" => "120GB 7.2K SATA/150 Seagate 7200.7 NCQ 2MB",
	"ST3120828AS" => "120GB 7.2K SATA/150 Seagate 7200.7 NCQ 8MB",
	"ST3160228AS" => "160GB 7.2K SATA/150 Seagate 7200.7 NCQ 2MB",
	"ST3160828AS" => "160GB 7.2K SATA/150 Seagate 7200.7 NCQ 8MB",

	"ST3250823A" => "250GB 7.2K ATA/100 Seagate 7200.8",
	"ST3300831A" => "300GB 7.2K ATA/100 Seagate 7200.8",
	"ST3400832A" => "400GB 7.2K ATA/100 Seagate 7200.8",

	"ST3200826AS" => "200GB 7.2K SATA/150 Seagate 7200.8 NCQ",
	"ST3250823AS" => "250GB 7.2K SATA/150 Seagate 7200.8 NCQ",
	"ST3300831AS" => "300GB 7.2K SATA/150 Seagate 7200.8 NCQ",
	"ST3400832AS" => "400GB 7.2K SATA/150 Seagate 7200.8 NCQ",

	"ST3402111A" => "40GB 7.2K ATA/100 Seagate 7200.9 2MB",
	"ST3802110A" => "80GB 7.2K ATA/100 Seagate 7200.9 2MB",
	"ST3120213A" => "120GB 7.2K ATA/100 Seagate 7200.9 2MB",
	"ST3120814A" => "120GB 7.2K ATA/100 Seagate 7200.9 8MB",
	"ST3160212A" => "160GB 7.2K ATA/100 Seagate 7200.9 2MB",
	"ST3160812A" => "160GB 7.2K ATA/100 Seagate 7200.9 8MB",
	"ST3200827A" => "200GB 7.2K ATA/100 Seagate 7200.9 8MB",
	"ST3250824A" => "250GB 7.2K ATA/100 Seagate 7200.9 8MB",
	"ST3250624A" => "250GB 7.2K ATA/100 Seagate 7200.9 16MB",
	"ST3300822A" => "300GB 7.2K ATA/100 Seagate 7200.9 8MB",
	"ST3300622A" => "300GB 7.2K ATA/100 Seagate 7200.9 16MB",
	"ST3400833A" => "400GB 7.2K ATA/100 Seagate 7200.9 8MB",
	"ST3400633A" => "400GB 7.2K ATA/100 Seagate 7200.9 16MB",
	"ST3500841A" => "500GB 7.2K ATA/100 Seagate 7200.9 8MB",
	"ST3500641A" => "500GB 7.2K ATA/100 Seagate 7200.9 16MB",

	"ST340211AS" => "40GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST380211AS" => "80GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST380811AS" => "80GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3120211AS" => "120GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST3120811AS" => "120GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3160211AS" => "160GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST3160811AS" => "160GB 7.2K SATA/300 Seagate 7200.9 8MB",

	"ST3402111AS" => "40GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST3402112AS" => "40GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST3802110AS" => "80GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST3808110AS" => "80GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3120213AS" => "120GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST3120813AS" => "120GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3160212AS" => "160GB 7.2K SATA/300 Seagate 7200.9 2MB",
	"ST3160812AS" => "160GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3200827AS" => "200GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3250824AS" => "250GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3250624AS" => "250GB 7.2K SATA/300 Seagate 7200.9 16MB",
	"ST3300822AS" => "300GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3300622AS" => "300GB 7.2K SATA/300 Seagate 7200.9 16MB",
	"ST3400833AS" => "400GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3400633AS" => "400GB 7.2K SATA/300 Seagate 7200.9 16MB",
	"ST3500841AS" => "500GB 7.2K SATA/300 Seagate 7200.9 8MB",
	"ST3500641AS" => "500GB 7.2K SATA/300 Seagate 7200.9 16MB",

	"ST340215A" => "40GB 7.2K ATA/100 Seagate 7200.10 2MB",
	"ST340815A" => "40GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST380215A" => "80GB 7.2K ATA/100 Seagate 7200.10 2MB",
	"ST380815A" => "80GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3120215A" => "120GB 7.2K ATA/100 Seagate 7200.10 2MB",
	"ST3120815A" => "120GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3160215A" => "160GB 7.2K ATA/100 Seagate 7200.10 2MB",
	"ST3160815A" => "160GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3200820A" => "200GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3250820A" => "250GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3250620A" => "250GB 7.2K ATA/100 Seagate 7200.10 16MB",
	"ST3300820A" => "300GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3300620A" => "300GB 7.2K ATA/100 Seagate 7200.10 16MB",
	"ST3320820A" => "320GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3320620A" => "320GB 7.2K ATA/100 Seagate 7200.10 16MB",
	"ST3400820A" => "400GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3400620A" => "400GB 7.2K ATA/100 Seagate 7200.10 16MB",
	"ST3500830A" => "500GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3500630A" => "500GB 7.2K ATA/100 Seagate 7200.10 16MB",
	"ST3750840A" => "750GB 7.2K ATA/100 Seagate 7200.10 8MB",
	"ST3750640A" => "750GB 7.2K ATA/100 Seagate 7200.10 16MB",


	"ST380215AS" => "80GB 7.2K SATA/300 Seagate 7200.10 2MB",
	"ST380815AS" => "80GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3160215AS" => "160GB 7.2K SATA/300 Seagate 7200.10 2MB",
	"ST3160815AS" => "160GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3160310AS" => "160GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3200820AS" => "200GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3250310AS" => "250GB 7.2K SATA/300 Seagate 7200.10 8MB",	# 1 platter
	"ST3250410AS" => "250GB 7.2K SATA/300 Seagate 7200.10 16MB",	# 1 platter
	"ST3250820AS" => "250GB 7.2K SATA/300 Seagate 7200.10 8MB",	# 2 platters
	"ST3250620AS" => "250GB 7.2K SATA/300 Seagate 7200.10 16MB",	# 2 platters
	"ST3300820AS" => "300GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3300620AS" => "300GB 7.2K SATA/300 Seagate 7200.10 16MB",
	"ST3320820AS" => "320GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3320620AS" => "320GB 7.2K SATA/300 Seagate 7200.10 16MB",
	"ST3400820AS" => "400GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3400620AS" => "400GB 7.2K SATA/300 Seagate 7200.10 16MB",
	"ST3500830AS" => "500GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3500630AS" => "500GB 7.2K SATA/300 Seagate 7200.10 16MB",
	"ST3750840AS" => "750GB 7.2K SATA/300 Seagate 7200.10 8MB",
	"ST3750640AS" => "750GB 7.2K SATA/300 Seagate 7200.10 16MB",

	"ST3160813AS" => "160GB 7.2K SATA/300 Seagate 7200.11 8MB",
	"ST3320613AS" => "320GB 7.2K SATA/300 Seagate 7200.11 16MB",
	"ST3500620AS" => "500GB 7.2K SATA/300 Seagate 7200.11 16MB",
	"ST3500320AS" => "500GB 7.2K SATA/300 Seagate 7200.11 32MB",
	"ST3640323AS" => "640GB 7.2K SATA/300 Seagate 7200.11 32MB",
	"ST3750630AS" => "750GB 7.2K SATA/300 Seagate 7200.11 16MB",
	"ST3750330AS" => "750GB 7.2K SATA/300 Seagate 7200.11 32MB",
	"ST31000333AS" => "1TB 7.2K SATA/300 Seagate 7200.11 32MB",
	"ST31000340AS" => "1TB 7.2K SATA/300 Seagate 7200.11 32MB",
	"ST31500341AS" => "1.5TB 7.2K SATA/300 Seagate 7200.11 32MB",

	"ST3250823NS" => "250GB 7.2K SATA/150 Seagate NL35 8MB",
	"ST3250623NS" => "250GB 7.2K SATA/150 Seagate NL35 16MB",
	"ST3400832NS" => "400GB 7.2K SATA/150 Seagate NL35 8MB",
	"ST3400632NS" => "400GB 7.2K SATA/150 Seagate NL35 16MB",

	"ST3250824NS" => "250GB 7.2K SATA/300 Seagate NL35 8MB",
	"ST3250624NS" => "250GB 7.2K SATA/300 Seagate NL35 16MB",
	"ST3400833NS" => "400GB 7.2K SATA/300 Seagate NL35 8MB",
	"ST3400633NS" => "400GB 7.2K SATA/300 Seagate NL35 16MB",
	"ST3500841NS" => "500GB 7.2K SATA/300 Seagate NL35 8MB",
	"ST3500641NS" => "500GB 7.2K SATA/300 Seagate NL35 16MB",

	"ST3250820NS" => "250GB 7.2K SATA/300 Seagate Barracuda ES 8MB",
	"ST3250620NS" => "250GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST3320620NS" => "320GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST3400620NS" => "400GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST3500630NS" => "500GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST3750640NS" => "750GB 7.2K SATA/300 Seagate Barracuda ES 16MB",

	"ST325082-0NS" => "250GB 7.2K SATA/300 Seagate Barracuda ES 8MB",
	"ST325062-0NS" => "250GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST332062-0NS" => "320GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST340062-0NS" => "400GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST350063-0NS" => "500GB 7.2K SATA/300 Seagate Barracuda ES 16MB",
	"ST375064-0NS" => "750GB 7.2K SATA/300 Seagate Barracuda ES 16MB",

	"ST3160815SV" => "160GB 7.2K SATA/300 Seagate SV35.2 8MB",
	"ST3250820SV" => "250GB 7.2K SATA/300 Seagate SV35.2 8MB",
	"ST3320620SV" => "320GB 7.2K SATA/300 Seagate SV35.2 16MB",
	"ST3500630SV" => "500GB 7.2K SATA/300 Seagate SV35.2 16MB",
	"ST3750640SV" => "750GB 7.2K SATA/300 Seagate SV35.2 16MB",

	"ST3250310SV" => "250GB 7.2K SATA/300 Seagate SV35.3 8MB",
	"ST3500320SV" => "500GB 7.2K SATA/300 Seagate SV35.3 32MB",
	"ST3750330SV" => "750GB 7.2K SATA/300 Seagate SV35.3 32MB",
	"ST31000340SV" => "1TB 7.2K SATA/300 Seagate SV35.3 32MB",

	"ST3250310NS" => "250GB 7.2K SATA/300 Seagate Barracuda ES.2 32MB",
	"ST3500320NS" => "500GB 7.2K SATA/300 Seagate Barracuda ES.2 32MB",
	"ST3750330NS" => "750GB 7.2K SATA/300 Seagate Barracuda ES.2 32MB",
	"ST31000340NS" => "1TB 7.2K SATA/300 Seagate Barracuda ES.2 32MB",

	"ST3500620SS" => "500GB 7.2K SAS/3 Seagate Barracuda ES.2 16MB",
	"ST3750630SS" => "750GB 7.2K SAS/3 Seagate Barracuda ES.2 16MB",
	"ST31000640SS" => "1TB 7.2K SAS/3 Seagate Barracuda ES.2 16MB",

	# http://www.seagate.com/support/disc/manuals/ata/100367120a.pdf
	"ST93811A" => "30GB 5.4K ATA/100 2.5\" Seagate Momentus 5400.2 8MB",
	"ST94813A" => "40GB 5.4K ATA/100 2.5\" Seagate Momentus 5400.2 8MB",
	"ST96812A" => "60GB 5.4K ATA/100 2.5\" Seagate Momentus 5400.2 8MB",
	"ST98823A" => "80GB 5.4K ATA/100 2.5\" Seagate Momentus 5400.2 8MB",
	"ST9100824A" => "100GB 5.4K ATA/100 2.5\" Seagate Momentus 5400.2 8MB",
	"ST9120821A" => "120GB 5.4K ATA/100 2.5\" Seagate Momentus 5400.2 8MB",

	"ST9120817AS" => "120GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.4 8MB",
	"ST9160827AS" => "160GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.4 8MB",
	"ST9200827AS" => "200GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.4 8MB",
	"ST9250827AS" => "250GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.4 8MB",

	"ST980310AS" => "80GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.5 8MB",
	"ST9120310AS" => "120GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.5 8MB",
	"ST9160310AS" => "160GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.5 8MB",
	"ST9320320AS" => "320GB 5.4K SATA/300 2.5\" Seagate Momentus 5400.5 8MB",

	"ST96023AS" => "60GB 7.2K SATA/150 2.5\" Seagate Momentus 7200.1 8MB",
	"ST98025AS" => "80GB 7.2K SATA/150 2.5\" Seagate Momentus 7200.1 8MB",
	"ST910021AS" => "100GB 7.2K SATA/150 2.5\" Seagate Momentus 7200.1 8MB",

	"ST980411AS" => "80GB 7.2K SATA/300 2.5\" Seagate Momentus 7200.3 16MB",
	"ST9120411AS" => "120GB 7.2K SATA/300 2.5\" Seagate Momentus 7200.3 16MB",
	"ST9160411AS" => "160GB 7.2K SATA/300 2.5\" Seagate Momentus 7200.3 16MB",
	"ST9250421AS" => "250GB 7.2K SATA/300 2.5\" Seagate Momentus 7200.3 16MB",
	"ST9320421AS" => "320GB 7.2K SATA/300 2.5\" Seagate Momentus 7200.3 16MB",

	"ST936701SS" => "37GB 10K SAS/3 2.5\" Seagate Savvio 8MB",
	"ST973401SS" => "73GB 10K SAS/3 2.5\" Seagate Savvio 8MB",

	"ST936701LC" => "37GB 10K U320 2.5\" Seagate Savvio 8MB",
	"ST973401LC" => "73GB 10K U320 2.5\" Seagate Savvio 8MB",

	"ST936751SS" => "37GB 15K SAS/3 2.5\" Seagate Savvio 15K 16MB",
	"ST973451SS" => "73GB 15K SAS/3 2.5\" Seagate Savvio 15K 16MB",

	"ST973402SS" => "73GB 10K SAS/3 2.5\" Seagate Savvio 10K.2 16MB",
	"ST9146802SS" => "146GB 10K SAS/3 2.5\" Seagate Savvio 10K.2 16MB",

	"MAG3091" => "9GB 10K Ultra2 Fujitsu MAG",
	"MAG3182" => "18GB 10K Ultra2 Fujitsu MAG",

	"MAH3091" => "9GB 7.2K U160 Fujitsu MAH",
	"MAH3182" => "18GB 7.2K U160 Fujitsu MAH",

	"MAJ3091" => "9GB 10K U160 Fujitsu MAJ",
	"MAJ3182" => "18GB 10K U160 Fujitsu MAJ",
	"MAJ3364" => "36GB 10K U160 Fujitsu MAJ",

	"MAN3184" => "18GB 10K U160 Fujitsu MAN",
	"MAN3367" => "36GB 10K U160 Fujitsu MAN",
	"MAN3735" => "73GB 10K U160 Fujitsu MAN",

	"MAM3184" => "18GB 15K U160 Fujitsu MAM",
	"MAM3367" => "36GB 15K U160 Fujitsu MAM",

	"MAS3184" => "18GB 15K U320 Fujitsu MAS",
	"MAS3367" => "36GB 15K U320 Fujitsu MAS",
	"MAS3735" => "73GB 15K U320 Fujitsu MAS",

	"MAP3367" => "36GB 10K U320 Fujitsu MAP",
	"MAP3735" => "73GB 10K U320 Fujitsu MAP",
	"MAP3147" => "147GB 10K U320 Fujitsu MAP",

	"MAT3073" => "73GB 10K U320 Fujitsu MAT",
	"MAT3147" => "147GB 10K U320 Fujitsu MAT",
	"MAT3300" => "300GB 10K U320 Fujitsu MAT",

	"MAW3073" => "73GB 10K U320 Fujitsu MAW",
	"MAW3147" => "147GB 10K U320 Fujitsu MAW",
	"MAW3300" => "300GB 10K U320 Fujitsu MAW",

	"MAU3036" => "36GB 15K U320 Fujitsu MAU",
	"MAU3073" => "73GB 15K U320 Fujitsu MAU",
	"MAU3147" => "147GB 15K U320 Fujitsu MAU",

	"MAX3036" => "36GB 15K U320 Fujitsu MAX",
	"MAX3073" => "73GB 15K U320 Fujitsu MAX",
	"MAX3147" => "147GB 15K U320 Fujitsu MAX",

	"MAX3036RC" => "36GB 15K SAS/3 Fujitsu MAX",
	"MAX3073RC" => "73GB 15K SAS/3 Fujitsu MAX",
	"MAX3147RC" => "147GB 15K SAS/3 Fujitsu MAX",

	"MBA3073NC" => "73GB 15K U320 Fujitsu MBA 16MB",
	"MBA3147NC" => "147GB 15K U320 Fujitsu MBA 16MB",
	"MBA3300NC" => "300GB 15K U320 Fujitsu MBA 16MB",

	"MBA3073RC" => "73GB 15K SAS/3 Fujitsu MBA 16MB",
	"MBA3147RC" => "147GB 15K SAS/3 Fujitsu MBA 16MB",
	"MBA3300RC" => "300GB 15K SAS/3 Fujitsu MBA 16MB",

	"MAV2036" => "36GB 10K SAS/3 2.5\" Fujitsu MAV",
	"MAV2073" => "73GB 10K SAS/3 2.5\" Fujitsu MAV",

	"MAY2036" => "36GB 10K SAS/3 2.5\" Fujitsu MAY",
	"MAY2073" => "73GB 10K SAS/3 2.5\" Fujitsu MAY",

	"MBB2073RC" => "73GB 10K SAS/3 2.5\" Fujitsu MBB 16MB",
	"MBB2147RC" => "147GB 10K SAS/3 2.5\" Fujitsu MBB 16MB",

	"MBC2036RC" => "36GB 15K SAS/3 2.5\" Fujitsu MBC 16MB",
	"MBC2073RC" => "73GB 15K SAS/3 2.5\" Fujitsu MBC 16MB",

	# HP/Compaq

	"BB01813467" => "18GB 7.2K Ultra2 Compaq",

	"BB01823469" => "18GB 7.2K Ultra2 Compaq",

	"BD0096349A" => "9GB 10K U160 Compaq",
	"BD0186349B" => "18GB 10K U160 Compaq",
	"BD0366349C" => "36GB 10K U160 Compaq",

	"BD009635C3" => "9GB 10K U160 Compaq/Fujitsu",
	"BD018635C4" => "18GB 10K U160 Compaq/Fujitsu",
	"BD036635C5" => "36GB 10K U160 Compaq/Fujitsu",

	"BD03663622" => "36GB 10K U160 Compaq",

	"BD01864544" => "18GB 10K U160 Compaq",
	"BD03664545" => "36GB 10K U160 Compaq",
	"BD07264546" => "72GB 10K U160 Compaq",

	"BD01864552" => "18GB 10K U320 Compaq",
	"BD03664553" => "36GB 10K U320 Compaq",

	"BD0186459A" => "18GB 10K U160 Compaq/Fujitsu MAN",
	"BD0366459B" => "36GB 10K U160 Compaq/Fujitsu MAN",
	"BD0726459C" => "72GB 10K U160 Compaq/Fujitsu MAN",

	"BD0366536B" => "36GB 10K U160 Compaq/Seagate 73LP",		# Seagate Cheetah 5
	"BD0726536C" => "72GB 10K U160 Compaq/Seagate 73LP",

	"BD036659CC" => "36GB 10K U160 Compaq",

	"BD07265A22" => "72GB 10K U160 Compaq",

	"BD01865CC4" => "18GB 10K U160 Compaq/Fujitsu MAP",		# MAP3367

	"BD01872377" => "18GB 10K U160 Compaq",

	"BD009734A3" => "9GB 10K U160 Compaq",
	"BD018734A4" => "18GB 10K U160 Compaq",

	"BD009735C6" => "9GB 10K U160 Compaq/Fujitsu",
	"BD018735C7" => "18GB 10K U160 Compaq/Fujitsu",
	"BD036735C8" => "36GB 10K U160 Compaq/Fujitsu",

	"BD01875CC7" => "18GB 10K U160 Compaq/Fujitsu MAP",		# related to BD01865CC4

	"BD03685A24" => "36GB 10K U320 Compaq/Seagate 10K.6",
	"BD07285A25" => "72GB 10K U320 Compaq/Seagate 10K.6",
	"BD14685A26" => "146GB 10K U320 Compaq/Seagate 10K.6",

	"BD03686223" => "36GB 10K U320 Compaq/Fujitsu MAP",
	"BD07286224" => "72GB 10K U320 Compaq/Fujitsu MAP",
	"BD14686225" => "146GB 10K U320 Compaq/Fujitsu MAP",

	"BD036863AC" => "36GB 10K U320 Compaq/Atlas 10K IV",
	"BD072863B2" => "72GB 10K U320 Compaq/Atlas 10K IV",
	"BD146863B3" => "146GB 10K U320 Compaq/Atlas 10K IV",

	"BD07287B4C" => "72GB 10K U320 Compaq/Atlas 10K V",
	"BD14687B52" => "146GB 10K U320 Compaq/Atlas 10K V",
	"BD30087B53" => "300GB 10K U320 Compaq/Atlas 10K V",

	"BD03688272" => "36GB 10K U320 Compaq/Seagate 10K.7",		# ST373207 - looks like a 72G drive!!
	"BD07288277" => "72GB 10K U320 Compaq/Seagate 10K.7",		# ST373207
	"BD14688278" => "146GB 10K U320 Compaq/Seagate 10K.7",		# ST3146707
	"BD30088279" => "300GB 10K U320 Compaq/Seagate 10K.7",		# ST3300007

	"BD300884C2" => "300GB 10K U320 Compaq",			# related to BD3008A527

	"BD0728856A" => "72GB 10K U320 Compaq/Fujitsu MAT",		# MAT3073
	"BD1468856B" => "146GB 10K U320 Compaq/Fujitsu MAT",		# MAT3147
	"BD3008856C" => "300GB 10K U320 Compaq/Fujitsu MAT",		# MAT3300

	"BD07289BB8" => "72GB 10K U320 Compaq/Fujitsu MAW",		# MAW3073
	"BD14689BB9" => "146GB 10K U320 Compaq/Fujitsu MAW",		# MAW3147
	"BD30089BBA" => "300GB 10K U320 Compaq/Fujitsu MAW",		# MAW3300

	"BD0728A4B4" => "72GB 10K U320 Compaq",				# related to BD0729A4B7
	"BD1468A4B5" => "146GB 10K U320 Compaq",
	"BD3008A4B6" => "300GB 10K U320 Compaq",

	"BD0728A4C4" => "72GB 10K U320 Compaq",				# related to BD0729A4C7
	"BD1468A4C5" => "146GB 10K U320 Compaq",
	"BD3008A4C6" => "300GB 10K U320 Compaq",

	"BD03695A27" => "36GB 10K U320 Compaq/Seagate 10K.6",		# related to BD03685A24

	"BD03695CC8" => "36GB 10K U320 Compaq/Fujitsu MAP",		# related to BD03686223
	"BD07296B44" => "72GB 10K U320 Compaq/Fujitsu MAP",		# related to BD07286224

	"BD03697633" => "36GB 10K U320 Compaq/Atlas 10K IV",		# related to BD036863AC

	"BD07297B57" => "72GB 10K U320 Compaq/Atlas 10K V",		# http://www.ad-min.com/productDetail.aspx?A_ID=2222
	"BD14697B58" => "146GB 10K U320 Compaq/Atlas 10K V",

	"BD03698276" => "36GB 10K U320 Compaq/Seagate 10K.7",		# related to BD03688272, real 72G drive
	"BD0729827A" => "72GB 10K U320 Compaq/Seagate 10K.7",
	"BD1469827B" => "146GB 10K U320 Compaq/Seagate 10K.7",
	"BD3009827C" => "300GB 10K U320 Compaq/Seagate 10K.7",

	"BD07298572" => "72GB 10K U320 Compaq/Fujitsu MAT",		# related to BD0728856A
	"BD14698573" => "146GB 10K U320 Compaq/Fujitsu MAT",
	"BD30098574" => "300GB 10K U320 Compaq/Fujitsu MAT",

	"BD0729A4C7" => "72GB 10K U320 Compaq",				# related to BD0728A4C4
	"BD1469A4C8" => "146GB 10K U320 Compaq",
	"BD3009A4C9" => "300GB 10K U320 Compaq",

	"BD07299BBB" => "72GB 10K U320 Compaq",

	# http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=PSD_CN0304W

	"BF01864663" => "18GB 15K U160 Compaq",			# WU3 == U160
	"BF03664664" => "36GB 15K U160 Compaq",			# WU3 == U160

	"BF01865222" => "18GB 15K U160 Compaq",
	"BF03665223" => "36GB 15K U160 Compaq",

	"BF03665A32" => "36GB 15K U160 Compaq",

	"BF07284961" => "72GB 15K U320 Compaq",

	"BF14684970" => "146GB 15K U320 Compaq",

	"BF01885A34" => "18GB 15K U320 Compaq/Seagate 15K.3",
	"BF03685A35" => "36GB 15K U320 Compaq/Seagate 15K.3",
	"BF07285A36" => "72GB 15K U320 Compaq/Seagate 15K.3",	# Seagate ST373453

	"BF018863B4" => "18GB 15K U320 Compaq",
	"BF036863B5" => "36GB 15K U320 Compaq",
	"BF072863B6" => "72GB 15K U320 Compaq",

	"BF018863B8" => "18GB 15K U320 Compaq",
	"BF036863B9" => "36GB 15K U320 Compaq",
	"BF072863BA" => "72GB 15K U320 Compaq",

	"BF0368683B" => "36GB 15K U160 Compaq",
	"BF0728683C" => "72GB 15K U160 Compaq",

	"BF03687B54" => "36GB 15K U320 Compaq",			# related to BF03697B5A
	"BF07287B55" => "72GB 15K U320 Compaq",
	"BF14687B56" => "146GB 15K U320 Compaq",

	"BF03688575" => "36GB 15K U320 Compaq/Fujitsu MAU",	# MAU3036
	"BF07288576" => "72GB 15K U320 Compaq/Fujitsu MAU",	# MAU3073
	"BF14688577" => "146GB 15K U320 Compaq/Fujitsu MAU",	# MAU3147

	"BF00988282" => "9GB 15K U320 Compaq",
	"BF01888283" => "18GB 15K U320 Compaq",

	"BF03688284" => "36GB 15K U320 Compaq/Seagate 15K.4",
	"BF07288285" => "72GB 15K U320 Compaq/Seagate 15K.4",	# ST373454
	"BF14688286" => "146GB 15K U320 Compaq/Seagate 15K.4",

	"BF03689BC3" => "36GB 15K U320 Compaq/Fujitsu MAX",
	"BF07289BC4" => "72GB 15K U320 Compaq/Fujitsu MAX",	# MAX3073
	"BF14689BC5" => "146GB 15K U320 Compaq/Fujitsu MAX",

	"BF0728A4B2" => "72GB 15K U320 Compaq",
	"BF1468A4B3" => "146GB 15K U320 Compaq",

	"BF0368A4B9" => "36GB 15K U320 Compaq",			# related to BF0369A4BC
	"BF0728A4BA" => "72GB 15K U320 Compaq",			# related to BF0729A4C2
	"BF1468A4BB" => "146GB 15K U320 Compaq",		# related to BF1469A4C3

	"BF0368A4CA" => "36GB 15K U320 Compaq/Seagate 15K.4",	#
	"BF0728A4CB" => "72GB 15K U320 Compaq/Seagate 15K.4",	# ST373454
	"BF1468A4CC" => "146GB 15K U320 Compaq/Seagate 15K.4",	#

	"BF0728AFEA" => "72GB 15K U320 Compaq",
	"BF1468AFEB" => "72GB 15K U320 Compaq",			# e.g. pud102.udb.hk2, related to BF14689BC5?

	"BF0368B269" => "36GB 15K U320 Compaq",
	"BF0728B26A" => "72GB 15K U320 Compaq",
	"BF1468B26B" => "146GB 15K U320 Compaq",

	"BF03697B5A" => "36GB 15K U320 Compaq",			# related to BF03687B54
	"BF07297B5B" => "72GB 15K U320 Compaq",
	"BF14697B5C" => "146GB 15K U320 Compaq",

	"BF03698287" => "36GB 15K U320 Compaq",
	"BF07298288" => "72GB 15K U320 Compaq",
	"BF14698289" => "146GB 15K U320 Compaq",

	"BF03698782" => "36GB 15K U320 Compaq/Fujitsu MAS",		# MAS3367

	#
	# http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?lang=en&cc=us&objectID=c00305257&jumpid=reg_R1002_USEN
	#

	# search for HP part number at - http://www.lamicrogroup.com/

	"DD400B8541" => "400GB 10K SAS HP/Seagate Cheetah NS",		# ST3400755SS

	"DF146A4941" => "146GB 15K SAS HP",

	"DF036A9843" => "36GB 15K SAS HP/Fujitsu MAX",
	"DF072A9844" => "72GB 15K SAS HP/Fujitsu MAX",			# MAX3073RC
	"DF146A9845" => "146GB 15K SAS HP/Fujitsu MAX",

	"DF036ABAA7" => "36GB 15K SAS HP/Seagate 15K.5",
	"DF072ABAA8" => "72GB 15K SAS HP/Seagate 15K.5",
	"DF146ABAA9" => "146GB 15K SAS HP/Seagate 15K.5",
	"DF300ABAAA" => "300GB 15K SAS HP/Seagate 15K.5",		# ST3300655SS

	"DF072BABUD" => "72GB 15K SAS HP/Hitachi Ultrastar 15K300",	# HUS153073VLS300
	"DF146BABUE" => "146GB 15K SAS HP/Hitachi Ultrastar 15K300",
	"DF300BABUF" => "300GB 15K SAS HP/Hitachi Ultrastar 15K300",

	"DF072BAFDT" => "72GB 15K SAS HP",
	"DF146BAFDU" => "146GB 15K SAS HP",
	"DF300BAFDV" => "300GB 15K SAS HP",

	"DF072BB6BC" => "72GB 15K SAS HP/Seagate 15K.5",
	"DF146BB6C2" => "146GB 15K SAS HP/Seagate 15K.5",		# ST3146855SS
	"DF300BB6C3" => "300GB 15K SAS HP/Seagate 15K.5",

	"DF0146B8052" => "146GB 15K SAS HP/Seagate 15K.6",
	"DF0300B8053" => "300GB 15K SAS HP/Seagate 15K.6",		# ST3300656SS
	"DF0450B8054" => "450GB 15K SAS HP/Seagate 15K.6",		# ST3450856SS

	"DG072A4951" => "73GB 10K SAS 2.5\" HP/Hitachi Ultrastar C10K147",	# HUC101473CSS300
	"DG146A4960" => "147GB 10K SAS 2.5\" HP/Hitachi Ultrastar C10K147",

	"DG072A3515" => "72GB 10K SAS 2.5\" HP/Seagate Savvio 10K.2 16MB",	# ST973402SS
	"DG146A3516" => "146GB 10K SAS 2.5\" HP/Seagate Savvio 10K.2 16MB",

	"DG036A8B53" => "36GB 10K SAS 2.5\" HP/Seagate Savvio 8MB",
	"DG072A8B54" => "72GB 10K SAS 2.5\" HP/Seagate Savvio 8MB",	# ST973401SS

	"DG036A8B5B" => "36GB 10K SAS 2.5\" HP/Fujitsu MAV",		# MAV2036RC
	"DG072A8B5C" => "72GB 10K SAS 2.5\" HP/Fujitsu MAV",

	"DG036A9BB6" => "36GB 10K SAS 2.5\" HP/Fujitsu MAY",
	"DG072A9BB7" => "72GB 10K SAS 2.5\" HP/Fujitsu MAY",		# MAY2073RC

	"DG072ABAB3" => "73GB 10K SAS 2.5\" HP/Seagate Savvio 10K.2 16MB",	# ST973402SS
	"DG146ABAB4" => "146GB 10K SAS 2.5\" HP/Seagate Savvio 10K.2 16MB",

	"DG072BB975" => "73GB 10K SAS 2.5\" HP/Seagate Savvio 10K.2 16MB",
	"DG146BB976" => "146GB 10K SAS 2.5\" HP/Seagate Savvio 10K.2 16MB",	# ST9146802SS

	"DH036ABAA5" => "36GB 15K SAS 2.5\" HP/Seagate Savvio 15K 16MB",
	"DH072ABAA6" => "72GB 15K SAS 2.5\" HP/Seagate Savvio 15K 16MB",	# ST973451SS

	"DH036BB977" => "36GB 15K SAS 2.5\" HP/Seagate Savvio 15K 16MB",
	"DH072BB978" => "72GB 15K SAS 2.5\" HP/Seagate Savvio 15K 16MB",	# ST973451SS

	# SATA

	# FB drives are SATA/150, but these are definitely seagate 7200.10 drives SATA/300
	"FB080C4080" => "80GB 7.2K SATA/150 HP/Seagate 7200.10",	# ST380815AS
	"FB160C4081" => "160GB 7.2K SATA/150 HP/Seagate 7200.10",	# ST3160815AS

	"FJ060C4980" => "60GB 5.4K SATA/150 HP",
	"FJ120C4981" => "120GB 5.4K SATA/150 HP",

	"GB0500C4413" => "500GB 7.2K SATA/300 HP/Seagate Barracude ES 16MB",
	"GB0750C4414" => "750GB 7.2K SATA/300 HP/Seagate Barracuda ES 16MB",	# ST3750640NS

	# looks like GB can be mix of 1.5/3.0 sata
	"GB0250C8045" => "250B 7.2K SATA/150 HP",
	"GB0500C8046" => "500B 7.2K SATA/150 HP",
	"GB0750C8047" => "750B 7.2K SATA/150 HP",

	"GB0160CAABV" => "160GB 7.2K SATA/300 HP",

	"GB0160EAFJE" => "160GB 7.2K SATA/300 HP",
	"GB0250EAFJF" => "250GB 7.2K SATA/300 HP",
	"GB0500EAFJH" => "500GB 7.2K SATA/300 HP",
	"GB0750EAFJK" => "750GB 7.2K SATA/300 HP",
	"GB1000EAFJL" => "1TB 7.2K SATA/300 HP",

	# http://www.maxtor.com/portal/site/Maxtor/menuitem.2d55600ff3a2577ee77bd88591346068/?channelpath=/en_us/Support/Product%20Support/Enterprise%20Storage

	"ATLAS-IV-9" => "9GB 7.2K U160 Quantum Atlas IV",
	"ATLAS-IV-18" => "18GB 7.2K U160 Quantum Atlas IV",
	"ATLAS-IV-36" => "36GB 7.2K U160 Quantum Atlas IV",

	"ATLAS-V-9" => "9GB 7.2K U160 Quantum Atlas V",
	"ATLAS-V-18" => "18GB 7.2K U160 Quantum Atlas V",
	"ATLAS-V-36" => "36GB 7.2K U160 Quantum Atlas V",

	"ATLASU320-18" => "18GB 10K U320 Maxtor Atlas ??",
	"ATLASU320-36" => "36GB 10K U320 Maxtor Atlas ??",

	"ATLAS10K2-TY092" => "9GB 10K U160 Maxtor Atlas 10K II",
	"ATLAS10K2-TY184" => "18GB 10K U160 Maxtor Atlas 10K II",
	"ATLAS10K2-TY367" => "36GB 10K U160 Maxtor Atlas 10K II",
	"ATLAS10K2-TY734" => "73GB 10K U160 Maxtor Atlas 10K II",

	"ATLAS10K3-18" => "18GB 10K U320 Maxtor Atlas 10K III",
	"ATLAS10K3-36" => "36GB 10K U320 Maxtor Atlas 10K III",
	"ATLAS10K3-73" => "73GB 10K U320 Maxtor Atlas 10K III",

	"ATLAS10K4-36" => "36GB 10K U320 Maxtor Atlas 10K IV",
	"ATLAS10K4-73" => "73GB 10K U320 Maxtor Atlas 10K IV",

	"ATLAS10K5-73" => "73GB 10K U320 Maxtor Atlas 10K V",
	"ATLAS10K5-146" => "146GB 10K U320 Maxtor Atlas 10K V",
	"ATLAS10K5-300" => "300GB 10K U320 Maxtor Atlas 10K V",

	# 16MB cache
	"ATLAS10K5-073SAS" => "73GB 10K SAS Maxtor Atlas 10K V",
	"ATLAS10K5-147SAS" => "147GB 10K SAS Maxtor Atlas 10K V",
	"ATLAS10K5-300SAS" => "300GB 10K SAS Maxtor Atlas 10K V",

	# http://www.seagate.com/staticfiles/maxtor/en_us/documentation/manuals/atlas_15k_product_manual.pdf
	"ATLAS15K-18" => "18GB 15K U320 Maxtor Atlas 15K",
	"ATLAS15K-36" => "36GB 15K U320 Maxtor Atlas 15K",
	"ATLAS15K-73" => "73GB 15K U320 Maxtor Atlas 15K",

	"ATLAS15K2-36" => "36GB 15K U320 Maxtor Atlas 15K II",
	"ATLAS15K2-73" => "73GB 15K U320 Maxtor Atlas 15K II",
	"ATLAS15K2-146" => "146GB 15K U320 Maxtor Atlas 15K II",

	# http://www.maxtor.com/_files/maxtor/en_us/documentation/data_sheets/product_line_card.pdf

	"QUANTUM-FIREBALLP-AS10" => "10GB 7.2K ATA/100 Fireball Plus AS",
	"QUANTUM-FIREBALLP-AS20" => "20GB 7.2K ATA/100 Fireball Plus AS",
	"QUANTUM-FIREBALLP-AS30" => "30GB 7.2K ATA/100 Fireball Plus AS",
	"QUANTUM-FIREBALLP-AS40" => "40GB 7.2K ATA/100 Fireball Plus AS",
	"QUANTUM-FIREBALLP-AS60" => "60GB 7.2K ATA/100 Fireball Plus AS",

	"2F020J0" => "20GB 5.4K ATA/133 Quantum Fireball 3",
	"2F020L0" => "20GB 5.4K ATA/133 Quantum Fireball 3",
	"2F030J0" => "30GB 5.4K ATA/133 Quantum Fireball 3",
	"2F030L0" => "30GB 5.4K ATA/133 Quantum Fireball 3",
	"2F040J0" => "40GB 5.4K ATA/133 Quantum Fireball 3",
	"2F040L0" => "40GB 5.4K ATA/133 Quantum Fireball 3",

	"2B010H1" => "10GB 5.4K ATA/100 Maxtor Fireball 541DX 2MB",
	"2B015H1" => "15GB 5.4K ATA/100 Maxtor Fireball 541DX 2MB",
	"2B020H1" => "20GB 5.4K ATA/100 Maxtor Fireball 541DX 2MB",

	"93073H4" => "30GB 7.2K ATA/100 Maxtor DiamondMax 60",
	"98196H8" => "80GB 5.4K ATA/100 Maxtor DiamondMax 80",

	"51024U2" => "10GB 7.2K ATA/66 Maxtor DiamondMax Plus 40",
	"51369U3" => "13GB 7.2K ATA/66 Maxtor DiamondMax Plus 40",
	"51536U3" => "15GB 7.2K ATA/66 Maxtor DiamondMax Plus 40",
	"52049U4" => "20GB 7.2K ATA/66 Maxtor DiamondMax Plus 40",
	"52732U6" => "27GB 7.2K ATA/66 Maxtor DiamondMax Plus 40",
	"53073U6" => "30GB 7.2K ATA/66 Maxtor DiamondMax Plus 40",
	"54098U8" => "40GB 7.2K ATA/66 Maxtor DiamondMax Plus 40",

	"51024H2" => "10GB 7.2K ATA/100 Maxtor DiamondMax Plus 40",
	"51369H3" => "13GB 7.2K ATA/100 Maxtor DiamondMax Plus 40",
	"51536H3" => "15GB 7.2K ATA/100 Maxtor DiamondMax Plus 40",
	"52049H4" => "20GB 7.2K ATA/100 Maxtor DiamondMax Plus 40",
	"52732H6" => "27GB 7.2K ATA/100 Maxtor DiamondMax Plus 40",
	"53073H6" => "30GB 7.2K ATA/100 Maxtor DiamondMax Plus 40",
	"54098H8" => "40GB 7.2K ATA/100 Maxtor DiamondMax Plus 40",

	"5T020H2" => "20GB 7.2K ATA/100 Maxtor DiamondMax Plus 60",
	"5T030H3" => "30GB 7.2K ATA/100 Maxtor DiamondMax Plus 60",
	"5T040H4" => "40GB 7.2K ATA/100 Maxtor DiamondMax Plus 60",

	"6E020L0" => "20GB 7.2K ATA/133 Maxtor DiamondMax Plus 8",
	"6E030L0" => "30GB 7.2K ATA/133 Maxtor DiamondMax Plus 8",
	"6E040L0" => "40GB 7.2K ATA/133 Maxtor DiamondMax Plus 8",
	"6K040L0" => "40GB 7.2K ATA/133 Maxtor DiamondMax Plus 8",	# RoHS

	"6N040T0" => "40GB 7.2K SATA/150 Maxtor DiamondMax 8S 2MB",
	"6E040T0" => "40GB 7.2K SATA/150 Maxtor DiamondMax 8S 2MB",

	"6Y060L0" => "60GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 2MB",
	"6Y080L0" => "80GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 2MB",
	"6Y120L0" => "120GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 2MB",
	"6Y160L0" => "160GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 2MB",

	"6Y060P0" => "60GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 8MB",
	"6Y080P0" => "80GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 8MB",
	"6Y120P0" => "120GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 8MB",
	"6Y160P0" => "160GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 8MB",
	"6Y200P0" => "200GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 8MB",
	"6Y250P0" => "250GB 7.2K ATA/133 Maxtor DiamondMax Plus 9 8MB",

	"6Y060M0" => "60GB 7.2K SATA/150 Maxtor DiamondMax Plus 9 8MB",
	"6Y080M0" => "80GB 7.2K SATA/150 Maxtor DiamondMax Plus 9 8MB",
	"6Y120M0" => "120GB 7.2K SATA/150 Maxtor DiamondMax Plus 9 8MB",
	"6Y160M0" => "160GB 7.2K SATA/150 Maxtor DiamondMax Plus 9 8MB",
	"6Y200M0" => "200GB 7.2K SATA/150 Maxtor DiamondMax Plus 9 8MB",
	"6Y250M0" => "250GB 7.2K SATA/150 Maxtor DiamondMax Plus 9 8MB",

	# http://www.seagate.com/staticfiles/maxtor/en_us/documentation/data_sheets/diamondmax_10_data_sheet.pdf
	"6L080L0" => "80GB 7.2K ATA/133 Maxtor DiamondMax 10 2MB",
	"6B080P0" => "80GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",
	"6L080P0" => "80GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",
	"6B120P0" => "120GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",
	"6L120P0" => "120GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",
	"6B160P0" => "160GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",
	"6L160P0" => "160GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",
	"6B200P0" => "200GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",
	"6L200P0" => "200GB 7.2K ATA/133 Maxtor DiamondMax 10 8MB",

	"6B200R0" => "200GB 7.2K ATA/133 Maxtor DiamondMax 10 16MB",
	"6L200R0" => "200GB 7.2K ATA/133 Maxtor DiamondMax 10 16MB",
	"6B250R0" => "250GB 7.2K ATA/133 Maxtor DiamondMax 10 16MB",
	"6L250R0" => "250GB 7.2K ATA/133 Maxtor DiamondMax 10 16MB",
	"6B300R0" => "300GB 7.2K ATA/133 Maxtor DiamondMax 10 16MB",
	"6L300R0" => "300GB 7.2K ATA/133 Maxtor DiamondMax 10 16MB",

	"6B080M0" => "80GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",
	"6L080M0" => "80GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",
	"6B120M0" => "120GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",
	"6L120M0" => "120GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",
	"6B160M0" => "160GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",
	"6L160M0" => "160GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",
	"6B200M0" => "200GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",
	"6L200M0" => "200GB 7.2K SATA/150 Maxtor DiamondMax 10 8MB",

	"6B200S0" => "200GB 7.2K SATA/150 Maxtor DiamondMax 10 16MB",
	"6L200S0" => "200GB 7.2K SATA/150 Maxtor DiamondMax 10 16MB",
	"6B250S0" => "250GB 7.2K SATA/150 Maxtor DiamondMax 10 16MB",
	"6L250S0" => "250GB 7.2K SATA/150 Maxtor DiamondMax 10 16MB",
	"6B300S0" => "300GB 7.2K SATA/150 Maxtor DiamondMax 10 16MB",
	"6L300S0" => "300GB 7.2K SATA/150 Maxtor DiamondMax 10 16MB",

	"6V080E0" => "80GB 7.2K SATA/300 Maxtor DiamondMax 10 8MB",
	"6V160E0" => "160GB 7.2K SATA/300 Maxtor DiamondMax 10 8MB",
	"6V200E0" => "200GB 7.2K SATA/300 Maxtor DiamondMax 10 8MB",

	"6V250F0" => "250GB 7.2K SATA/300 Maxtor DiamondMax 10 16MB",
	"6V300F0" => "300GB 7.2K SATA/300 Maxtor DiamondMax 10 16MB",
	"6V320F0" => "320GB 7.2K SATA/300 Maxtor DiamondMax 10 16MB",

	"6H400R0" => "400GB 7.2K ATA/133 Maxtor DiamondMax 11 16MB",
	"6H500R0" => "500GB 7.2K ATA/133 Maxtor DiamondMax 11 16MB",

	"6H400F0" => "400GB 7.2K SATA/300 Maxtor DiamondMax 11 16MB",
	"6H500F0" => "500GB 7.2K SATA/300 Maxtor DiamondMax 11 16MB",

	"STM3802110A" => "80GB 7.2K ATA/100 Maxtor DiamondMax 20 2MB",

	"STM340211AS" => "40GB 7.2K SATA/300 Maxtor DiamondMax 20 2MB",
	"STM380211AS" => "80GB 7.2K SATA/300 Maxtor DiamondMax 20 2MB",
	"STM380811AS" => "80GB 7.2K SATA/300 Maxtor DiamondMax 20 8MB",
	"STM380811" => "80GB 7.2K SATA/300 Maxtor DiamondMax 20 8MB",
	"STM3160211AS" => "160GB 7.2K SATA/300 Maxtor DiamondMax 20 2MB",

	"STM3250820AS" => "250GB 7.2K SATA/300 Maxtor DiamondMax 21 8MB",
	"STM3320820AS" => "250GB 7.2K SATA/300 Maxtor DiamondMax 21 8MB",

	"STM340215AS" => "40GB 7.2K SATA/300 Maxtor DiamondMax 21 2MB",
	"STM380215AS" => "80GB 7.2K SATA/300 Maxtor DiamondMax 21 2MB",
	"STM3160215AS" => "160GB 7.2K SATA/300 Maxtor DiamondMax 21 2MB",

	"7H500R0" => "500GB 7.2K ATA/133 Maxtor MaXLine Pro 500 16MB",
	"7H500F0" => "500GB 7.2K SATA/300 Maxtor MaXLine Pro 500 16MB",

	"4W100H6" => "100GB 5.4K ATA/100 Maxtor DiamondMax 536DX",

	"4D020H1" => "20GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4D040H2" => "40GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4D060H3" => "60GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4D080H4" => "80GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4K020H1" => "20GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4K040H2" => "40GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4K060H3" => "60GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4K080H4" => "80GB 5.4K ATA/100 Maxtor DiamondMax D540X",
	"4G120J6" => "120GB 5.4K ATA/133 Maxtor DiamondMax D540X",
	"4G160J8" => "160GB 5.4K ATA/133 Maxtor DiamondMax D540X",

	"4R060L0" => "60GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4R060J0" => "60GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4R080L0" => "80GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4R080J0" => "80GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4R120L0" => "120GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4R160L0" => "160GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4A160J0" => "160GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4A250J0" => "250GB 5.4K ATA/133 Maxtor DiamondMax 16",
	"4A300J0" => "300GB 5.4K ATA/133 Maxtor DiamondMax 16",

	"6G080E0" => "80GB 7.2K SATA/300 Maxtor DiamondMax 17 8MB",
	"6G160E0" => "160GB 7.2K SATA/300 Maxtor DiamondMax 17 8MB",
	"6G250E0" => "250GB 7.2K SATA/300 Maxtor DiamondMax 17 8MB",
	"6G320E0" => "320GB 7.2K SATA/300 Maxtor DiamondMax 17 8MB",

	"6L020J1" => "20GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",
	"6L040J2" => "40GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",
	"6L060J3" => "60GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",
	"6L080J4" => "80GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",

	"6L020L1" => "20GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",
	"6L040L2" => "40GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",
	"6L060L3" => "60GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",
	"6L080L4" => "80GB 7.2K ATA/133 Maxtor DiamondMax Plus D740X",

	"5A250J0" => "250GB 5.4K ATA/133 Maxtor MaXLine II 2MB",
	"5A300J0" => "300GB 5.4K ATA/133 Maxtor MaXLine II 2MB",
	"5A320J0" => "320GB 5.4K ATA/133 Maxtor MaXLine II 2MB",

	"7Y250P0" => "250GB 7.2K ATA/133 Maxtor MaXLine Plus II 8MB",
	"7Y250M0" => "250GB 7.2K SATA/150 Maxtor MaXLine Plus II 8MB",

	"7L250R0" => "250GB 7.2K ATA/133 Maxtor MaXLine III 16MB",
	"7L300R0" => "300GB 7.2K ATA/133 Maxtor MaXLine III 16MB",

	"7L250S0" => "250GB 7.2K SATA/150 Maxtor MaXLine III 16MB",
	"7L300S0" => "300GB 7.2K SATA/150 Maxtor MaXLine III 16MB",

	"7V250F0" => "250GB 7.2K SATA/300 Maxtor MaXLine III 16MB",
	"7V300F0" => "300GB 7.2K SATA/300 Maxtor MaXLine III 16MB",

	# SSD

	"MTRON-MSP-SATA3025-032" => "32GB SSD SATA/150 2.5\" Mtron SSD MOBI 3000",
	"MTRON-MSP-SATA3035-032" => "32GB SSD SATA/150 Mtron SSD MOBI 3000",

	"MTRON-MSP-SATA7025-032" => "32GB SSD SATA/150 2.5\" Mtron SSD Pro 7000",
	"MTRON-MSP-SATA7035-032" => "32GB SSD SATA/150 Mtron SSD Pro 7000",

	"MTRON-MSP-SATA7525-032" => "32GB SSD SATA/300 2.5\" Mtron SSD Pro 7500",
	"MTRON-MSP-SATA7535-032" => "32GB SSD SATA/300 Mtron SSD Pro 7500",

	"SanDisk-SSD-SATA-5000-2.5" => "32GB SSD SATA/150 2.5\" SanDisk SSD 5000",
);

my(@system_models) = (
	{
		"model" => "AOpen i965GMt-LA",
		"ids" => [
			"0x063ea0a0,0x816810ec,0x02",		# Network
			"0x063ea0a0,0x28288086,0x03",		# SATA
		],
		"sockets" => 1,
		"socket-type" => "Intel-mPGA478 - Socket P",
		"dimms" => 2,
		"memory-type" => "DDR2-667 non-ECC Unbuffered",
		"memory-form" => "200pin SO-DIMM DDR2",
		"max-memory" => "4G",
	},
	{
		"model" => "MSI GM965",
		"ugly-model" => "MSI MS-9803",
		"ids" => [
			"0x80301462,0x109a8086,0x00",		# Network
			"0x80301462,0x282[89]8086,0x03",	# SATA
		],
		"sockets" => 1,
		"socket-type" => "Intel-mPGA478 - Socket P",
		"dimms" => 2,
		"memory-type" => "DDR2-667 non-ECC Unbuffered",
		"memory-form" => "240pin DDR2",
		"max-memory" => "4G",
	},
	{
		"model" => "Intel X38ML",
		"ids" => [
			"0x34ce8086,0x10a78086,0x02",		# Network
			"0x34d08086,0x292[02]8086,0x02",	# SATA
		],
		"sockets" => 1,
		"socket-type" => "Intel-LGA775",
		"dimms" => 4,
		"memory-type" => "DDR2-800 ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "Intel D945GCLF",
		"ids" => [
			"0x00018086,0x813610ec,0x02",		# Network
			"0x464c8086,0x27c08086,0x01",		# SATA
		],
		"sockets" => 1,
		"dimms" => 1,
		"memory-type" => "DDR2-667 ECC Unbuffered",
		"max-memory" => "2G",
	},
	{
		"model" => "Quanta SU2-3000",
		"ids" => [
			"0x00000000,0x02111166,0x00",		# ATA
			"0x8917152d,0x12298086,0x08",		# Network
			"0x8917152d,0x008f9005,0x02",		# SCSI
		],
		"sockets" => 2,
	},
	{
		"model" => "Gateway SU2-4200",
		"ids" => [
			"0x8920107b,0x02131166,0xa0",			# ATA
			"0x(892d152d|01341028),0x100e8086,0x02",	# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Quanta SU2-4200",
		"ids" => [
			"0x02201166,0x02131166,0xa0",		# ATA
			"0x892d152d,0x100e8086,0x02",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Asus P5Q-Deluxe",
		"ids" => [
			"0x811a1043,0x432011ab,0x14",		# Network
			"0x81f81043,0x436411ab,0x12",		# Network
			"0x82d41043,0x3a228086,0x00",		# SATA
		],
		"sockets" => 1,
		"socket-type" => "Intel-LGA775",
		"dimms" => 4,
		"memory-type" => "DDR2-1200 non-ECC Unbuffered",
		"max-memory" => "16G",
	},
	{
		"model" => "Asus P5BV-C",
		"ids" => [
			"0x819e1043,0x27df8086,0x01",		# ATA
			"0x81901043,0x27c[013]8086,0x01",	# SATA
			"0x826e1043,0x436411ab,0x12",		# Network
		],
		"sockets" => 1,
		"socket-type" => "Intel-LGA775",
		"dimms" => 4,
		"memory-type" => "DDR2-800 ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "Gigabyte 945GCM-S2C",
		"ids" => [
			"0xe0001458,0x813610ec,0x01",		# Network
			"0xb0021458,0x27c08086,0x01",		# SATA
		],
		"sockets" => 1,
		"socket-type" => "Intel-LGA775",
		"dimms" => 2,
		"memory-type" => "DDR2-667 non-ECC Unbuffered",
		"max-memory" => "4G",
	},
	{
		"model" => "Gigabyte G33M-S2L",			# needs to come before Gigabyte P35-S3G
		"ids" => [
			"0xd0001458,0x29c28086,0x02",		# VGA
			"0xb0051458,0x29238086,0x02",		# SATA
			"0xe0001458,0x816810ec,0x01",		# Network
		],
		"sockets" => 1,
		"socket-type" => "Intel-LGA775",
		"dimms" => 4,
		"memory-type" => "DDR2-1066 non-ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "Gigabyte P35-S3G",
		"ids" => [
			"0xb0051458,0x29238086,0x02",		# SATA
			"0xe0001458,0x816810ec,0x01",		# Network
		],
		"sockets" => 1,
		"socket-type" => "Intel-LGA775",
		"dimms" => 4,
		"memory-type" => "DDR2-1066 non-ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "Gigabyte EP45-DS3R",
		"ids" => [
			"(0xb0051458,0x3a228086,0x00|0xb0021458,0x3a208086,0x00)",	# SATA
			"0xe0001458,0x816810ec,0x02",					# Network
		],
		"sockets" => 1,
		"socket-type" => "Intel-LGA775",
		"dimms" => 4,
		"memory-type" => "DDR2-1200 non-ECC Unbuffered",
		"max-memory" => "16G",
	},
	{
		"model" => "Gigabyte GA-7A8DRH",
		"ids" => [
			"0xb0101458,0x80[19]d9005,0x10",	# SCSI
			"0x30001458,0x10268086,0x04",		# Network
			"0x10761458,0x10768086,0x00",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "MSI 8480000",
		"ids" => [
			"0x24501462,0x164814e4,0x03",		# Network
			"0x7469(1462|1022),0x74691022,0x03",	# ATA
		],
		"dimms" => 8,
		"memory-type" => "DDR-333 ECC Registered",
		"max-memory" => "16G",
		"sockets" => 2,
		"socket-type" => "AMD-Socket 940",
	},
	{
		"model" => "Tyan S5397",
		"ids" => [
			"0x10001000,0x00581000,0x04",		# SAS
			"0x00008086,0x10968086,0x01",		# Network
			"0x539710f1,0x268[012]08086,0x09",	# SATA
		],
		"dimms" => 16,
		"memory-type" => "DDR2-800 ECC Fully Buffered",
		"max-memory" => "128G",
		"sockets" => 2,
		"socket-type" => "Intel-LGA771",
		'url' => 'http://www.tyan.com/product_board_detail.aspx?pid=560',
	},
	{
		"model" => "Tyan S5376",
		"ids" => [
			"0x521110f1,0x108b8086,0x03",		# Network
			"0x537610f1,0x292[02]8086,0x02",	# SATA
		],
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "48G",
		"max-ranks" => 12,
		"sockets" => 2,
	},
	{
		"model" => "Intel SRSH4",
		"ids" => [
			"0x34128086,0x12298086,0x0d",		# Network
			"0x34128086,0x100d8086,0x02",		# Network
			"0x34128086,0x801f9005,0x03",		# SCSI
		],
		"sockets" => 4,
	},
	{
		"model" => "Tyan S4881",
		"ids" => [
			"0x289110f1,0x005310de,0xf2",		# ATA
			"0x164814e4,0x164814e4,0x03",		# Network
		],
		"sockets" => 4,
	},
	{
		"model" => "Tyan S4882",
		"ids" => [
			"0x(2b80|36c0)1022,0x74691022,0x03",	# ATA
			"0x10001000,0x00301000,0x07",		# MPT
			"0x164[48]14e4,0x164814e4,0x03",	# Network
		],
		"sockets" => 4,
	},
	{
		"model" => "Tyan S2880",
		"ids" => [
			"0x6619105a,0x3373105a,0x02",		# SATA
			"0x164414e4,0x164814e4,0x(03|10)",	# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Tyan S2882",			# Must come before Tyan S2881
		"ids" => [
			"0x74691022,0x74691022,0x03",		# ATA
			"0x164414e4,0x164814e4,0x(03|10)",	# Network
			"0x10408086,0x12298086,0x10",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Tyan S2881",
		"ids" => [
			"0x74691022,0x74691022,0x03",		# ATA
			"0x164414e4,0x164814e4,0x(03|10)",	# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Tyan S2865",
		"ids" => [
			"0x286510f1,0x005710de,0xa3",
		],
		"sockets" => 1,
	},
	{
		"model" => "Tyan S2722",
		"ids" => [
			"0x10408086,0x12298086,0x10",		# Network
			"0x10018086,0x100f8086,0x01",		# Network
			"0x00000000,0x248b8086,0x02",		# ATA
		],
		"sockets" => 2,
	},
	{
		"model" => "Tyan S2510",
		"ids" => [
			"0x000c8086,0x12298086,0x08",		# Network
			"0x00000000,0x02111166,0x00",		# ATA
			"0x10001000,0x00201000,0x01",		# SCSI
		],
		"sockets" => 2,
		"socket-type" => "Intel-PGA370",
		"dimms" => 4,
		"memory-type" => "PC133 SDRAM ECC Registered",
		"max-memory" => "4G",
	},
	{
		"model" => "Dell GX620",
		"ids" => [
			"0x01ad1028,0x27df8086,0x01",		# ATA
			"0x01ad1028,0x27c[013]8086,0x01",	# SATA
			"0x01ad1028,0x167714e4,0x01",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "Dell GX280",
		"ids" => [
			"0x01791028,0x266f8086,0x03",		# ATA
			"0x01791028,0x26518086,0x03",		# SATA
			"0x01791028,0x167714e4,0x01",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "Dell Precision 360",
		"ids" => [
			"0x01561028,0x24d18086,0x02",		# SATA
			"0x01561028,0x24db8086,0x02",		# ATA
		],
		"sockets" => 1,
		"dimms" => 4,
		"memory-type" => "DDR-400 ECC Unbuffered",
		"max-memory" => "4G",
	},
	{
		"model" => "Dell SX260",
		"ids" => [
			"0x01381028,0x24cb8086,0x01",		# SATA
			"0x01381028,0x100e8086,0x02",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "Dell GX270",
		"ids" => [
			"0x01511028,0x24d18086,0x02",		# SATA
			"0x01511028,0x24db8086,0x02",		# ATA
		],
		"sockets" => 1,
	},
	{
		"model" => "Dell 530",
		"ids" => [
			"0x00d81028,0x244b8086,0x04",		# ATA
			"0x00d81028,0x920010b7,0x78",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "Dell GX240",
		"ids" => [
			"0x010e1028,0x244b8086,0x12",		# ATA
			"0x00fe1028,0x920010b7,0x78",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DX2255",
		"ids" => [
			"0x3024103c,0x05711106,0x80",		# ATA
			"0x3024103c,0x813910ec,0x10",		# Network
		],
		"sockets" => 1,
		"dimms" => 2,
		"memory-type" => "DDR2-667 non-ECC Unbuffered",
		"max-memory" => "2G",
	},
	{
		"model" => "HP DX2180",
		"ids" => [
			"0x301e103c,0x26518086,0x04",		# SATA
			"0x301e103c,0x813910ec,0x10",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP D290",
		"ids" => [
			"0x301f103c,0x26518086,0x04",		# SATA
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DX2280",
		"ids" => [
			"0x3022103c,0x27c[013]8086,0x01",	# SATA
			"0x3022103c,0x(8167|8169)10ec,0x10",	# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DX5150",
		"ids" => [
			"0x3009103c,0x43791002,0x00",		# SATA
			"0x3009103c,0x167714e4,0x20",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DC5800",
		"ids" => [
			"0x281e103c,0x10bd8086,0x02",		# Network
		],
		"sockets" => 1,
		"dimms" => 4,
		"memory-type" => "DDR2-800 non-ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "HP DC7600",
		"ids" => [
			"0x3011103c,0x160014e4,0x01",		# Network
			"0x3011103c,0x27c[013]8086,0x01",	# SATA
		],
		"sockets" => 1,
	},
	{
		"model" => "HP D510",
		"ids" => [
			"0x00b[89]0e11,0x24cb8086,0x01",	# ATA
			"0x00120e11,0x103b8086,0x81",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP D530",				# must come before HP DC5000
		"ids" => [
			"0x12bc103c,0x24d18086,0x02",		# SATA
			"0x12bc103c,0x24db8086,0x02",		# ATA
			"0x12bc103c,0x169614e4,0x03",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DC5000",
		"ids" => [
			"0x12bc103c,0x24db8086,0x02",		# ATA
			"0x12bc103c,0x169614e4,0x03",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DX6120",
		"ids" => [
			"0x300a103c,0x266f8086,0x03",		# ATA
			"0x300a103c,0x26518086,0x03",		# SATA
			"0x3005103c,0x167714e4,0x01",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DC7100",
		"ids" => [
			"0x3005103c,0x26518086,0x03",		# SATA
			"0x3005103c,0x167714e4,0x01",		# Network
		],
		"sockets" => 1,
	},
	{
		# http://h18000.www1.hp.com/products/quickspecs/12145_ca/12145_ca.pdf
		"model" => "HP DC5100",
		"ids" => [
			"0x300[cd]103c,0x26518086,0x03",		# SATA
			"0x300[56]103c,0x167714e4,0x01",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DC5700",
		"ids" => [
			"0x2808103c,0x28208086,0x02",			# SATA
			"0x2808103c,0x28258086,0x02",			# SATA
			"0x2808103c,0x167b14e4,0x02",			# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "Quanta QSSC-295MB",
		"ids" => [
			"0x00008086,0x10bd8086,0x02",		# Network
			"0x8954152d,0x292[02]8086,0x02",	# SATA
			"0x8954152d,0x29268086,0x02",		# SATA
		],
		"sockets" => 2,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "48G",
		"max-ranks" => 12,
	},
	{
		"model" => "eSlim SU4-1320",			# Quanta S47 board
		"ugly-model" => "Quanta S47",
		"ids" => [
			"0x8959152d,0x167814e4,0xa3",		# Network
			"0x8959152d,0x292[02]8086,0x02",	# SATA
		],
		"dimms" => 4,
		"memory-type" => "DDR2-800 ECC Unbuffered",
		"max-memory" => "8G",
		"sockets" => 1,
	},
	{
		"model" => "Quanta S45",
		"ids" => [
			"(0x00008086,0x10bd8086,0x02|0x00008086,0x108b8086,0x03)",		# Network
			"0x8954152d,0x292[02]8086,0x02",					# SATA
		],
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "48G",
		"max-ranks" => 12,
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S45",
		"ids" => [
			"(0x00008086,0x10bd8086,0x02|0x00008086,0x108b8086,0x03)",		# Network
			"0x2[89]2[02]8086,0x2[89]2[02]8086,0x02",				# SATA
		],
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "48G",
		"max-ranks" => 12,
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S29S-SAS",
		"ugly-model" => "Dell (F1CH|DCS Platform)",
		"ids" => [
			"0x80868086,0x25d48086,0xb1",		# Chipset
			"0x894b152d,0x00561000,0x02",		# SAS
			"0x8949152d,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S29S",
		"ugly-model" => "Dell (F1CH|DCS Platform)",
		"ids" => [
			"0x80868086,0x25d48086,0xb1",		# Chipset
			"0x8949152d,0x268[012]8086,0x09",	# SATA
			"0x8949152d,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S29R",
		"ugly-model" => "Dell BlackfordESB2",
		"ids" => [
			"0x8949152d,0x25d48086,0xb1",		# Chipset
			"0x8949152d,0x268[012]8086,0x09",	# SATA
			"0x8949152d,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S58R-SAS",			# must come before Dell DCS S58R
		"ugly-model" => "(Intel|Dell) BlackfordESB2",
		"ids" => [
			"0x894b152d,0x25d88086,0xb1",		# Chipset
			"0x894b152d,0x00561000,0x02",		# SAS
			"0x8949152d,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S58R",
		"ugly-model" => "(Intel|Dell) BlackfordESB2",
		"ids" => [
			"0x894b152d,0x25d88086,0xb1",		# Chipset
			"0x894b152d,0x268[012]8086,0x09",	# SATA
			"0x8949152d,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S58S-SAS",				# must come before Dell DCS S58S
		"ugly-model" => "Quanta S58A?",
		"ids" => [
			"0x80868086,0x25d88086,0xb1",			# Chipset
			"0x894b152d,0x00561000,0x02",			# SAS
			"0x8949152d,0x10968086,0x01",			# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell DCS S58S",
		"ugly-model" => "Quanta S58A?",
		"ids" => [
			"0x80868086,0x25d88086,0xb1",			# Chipset
			"0x(894b|8950)152d,0x268[012]8086,0x09",	# SATA
			"0x8949152d,0x10968086,0x01",			# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Inventec 5220",
		"ids" => [
			"0x003a1170,0x10968086,0x01",			# Network
			"0x003a1170,0x268[012]8086,0x09",		# SATA
		],
		"dimms" => 8,
		"memory-type" => "DDR2 ECC Fully Buffered",
		"max-memory" => "32G",
		"sockets" => 2,
	},
	{
		"model" => "Inventec Seabream",
		"ids" => [
			"0x00231170,0x(268[012]|269e)8086,0x09",	# ATA/SATA
			"0x00231170,0x10968086,0x01",			# Network
		],
		"sockets" => 2,
		"ymodel-type" => "J",
	},
	{
		"model" => "Inventec IR1250",
		"ids" => [
			"0x00221170,0x27df8086,0x01",		# ATA
			"0x00221170,0x165914e4,0x11",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "Inventec IR2100",
		"ids" => [
			"0x000c1170,0x47521002,0x27",		# VGA
			"0x165914e4,0x165914e4,0x11",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Inventec IR2300",
		"ugly-model" => "Inventec Tuna",
		"ids" => [
			"0x00161170,0x005310de,0xa2",		# ATA
			"0x165914e4,0x165914e4,0x11",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Inventec IR2350",
		"ids" => [
			"0x00171170,0x005310de,0xa2",		# ATA
			"0x165914e4,0x165914e4,0x11",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Inventec IR2400",
		"ids" => [
			"0x00261170,0x037f10de,0xa3",
			"0x00261170,0x037310de,0xa3",
		],
		"sockets" => 2,		# Socket F
	},
	{
		"model" => "Supermicro X8DTN",
		"ids" => [
			"0x10a715d9,0x10a78086,0x02",			# Network
			"0x2[89]2[02]8086,0x2[89]2[02]8086,0x02",	# SATA
			"0x515e1002,0x515e1002,0x02",			# VGA
		],
		"sockets" => 2,
		"dimms" => 18,
		"memory-type" => "DDR3",
	},
	{
		"model" => "Supermicro X5DP8",
		"ids" => [
			"0x348015d9,0x248b8086,0x02",		# ATA
			"0x005e9005,0x801d9005,0x10",		# SCSI
			"0x10118086,0x10108086,0x01",		# Network
		],
		"sockets" => 2,
		"dimms" => 8,
		"memory-type" => "DDR-266 ECC Registered",
		"max-memory" => "16G",
	},
	{
		"model" => "Supermicro P4DP6",
		"ids" => [
			"0x348015d9,0x248b8086,0x02",		# ATA
			"0x900515d9,0x00cf9005,0x01",		# SCSI
			"0x10508086,0x12298086,0x0d",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro PDSBM",
		"ids" => [
			"0xb58015d9,0x27c[013]8086,0x01",	# SATA
			"0x109a15d9,0x109a8086,0x00",		# Network
		],
		"sockets" => 1,
		"dimms" => 2,
		"memory-type" => "DDR2-667 non-ECC Unbuffered",
		"max-memory" => "4G",
	},
	{
		"model" => "Supermicro X7SBL",
		"ids" => [
			"0x109a15d9,0x109a8086,0x00",		# Network
			"0xd88015d9,0x292[02]8086,0x02",	# SATA
		],
		"sockets" => 1,
		"dimms" => 4,
		"memory-type" => "DDR2-800 ECC Registered",
		"max-memory" => "8G",
		"sata-ports" => 6,
		"form-factor" => "Micro ATX",
	},
	{
		"model" => "Supermicro X7DBP",
		"ids" => [
			"0x838015d9,0x269e8086,0x09",		# ATA
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
		],
		"sockets" => 2,
		"socket-type" => "Intel-LGA771",
		"dimms" => 8,
		"memory-type" => "DDR2-667 ECC Fully Buffered",
		"max-memory" => "32G",
		"sata-ports" => 6,
		"lan-ports" => 2,
		"serial-ports" => 2,
		"PCI-X" => 2,
		"PCI-E x8" => 2,
		"form-factor" => "41.25cm x 28.52cm",
	},
	{
		"model" => "Supermicro X7DCA-L",
		"ids" => [
			"0x109a15d9,0x109a8086,0x00",		# Network
			"0xdc8015d9,0x292[02]8086,0x02",	# SATA
		],
		"sockets" => 2,
		"socket-type" => "Intel-LGA771",
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "48G",
		"sata-ports" => 6,
		"form-factor" => "Micro ATX",
	},
	{
		"model" => "Supermicro X7DCL-3",		# needs to come before X7DCL-i
		"ids" => [
			"0xa48015d9,0x005[89]1000,0x08",	# SAS
			"0xa48015d9,0x292[02]8086,0x02",	# SATA
			"0x108c15d9,0x108c8086,0x03",		# Netwwork
		],
		"sockets" => 2,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "32G",
	},
	{
		"model" => "Supermicro X7DCL-i",
		"ugly-model" => '^Supermicro X7DCL$',
		"ids" => [
			"0xa48015d9,0x292[02]8086,0x02",	# SATA
			"0x108c15d9,0x108c8086,0x03",		# Netwwork
		],
		"sockets" => 2,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "32G",
	},
	{
		"model" => "Supermicro X7DAL",
		"ids" => [
			"0x948015d9,0x25c08086,0x13",		# Chipset
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
			"0x948015d9,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro B7DBE",
		"ids" => [
			"0xb08015d9,0x268[012]8086,0x09",	# SATA IDE|AHCI|RAID
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro X7DBU",
		"ids" => [
			# have some showing up as 0x868015d9,0x26818086,0x09, e.g. fe35.yosup.re1.yahoo.com
			# looks like mistake, so ignore for now
			"0x978[01]15d9,0x268[012]8086,0x09",	# SATA IDE|AHCI|RAID
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro X7DVL-L",
		"ids" => [
			"0xb28015d9,0x25d48086,0xb1",		# Chipset
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
			"0xb28015d9,0x268[012]8086,0x09",	# SATA IDE|AHCI|RAID
			"0xb28015d9,0x002018ca,0x00",		# VGA
		],
		"sockets" => 2,
		"dimms" => 4,
		"memory-type" => "DDR2-667 ECC Fully Buffered",
		"max-memory" => "16G",
		"sata-ports" => 4,
	},
	{
		"model" => "Supermicro X7DVL-E",
		"ids" => [
			"0x868015d9,0x25d48086,0xb1",		# Chipset
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
			"0x868015d9,0x268[012]8086,0x09",	# SATA IDE|AHCI|RAID
			"0x868015d9,0x515e1002,0x02",		# VGA
		],
		"sockets" => 2,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Fully Buffered",
		"max-memory" => "16G",
		"sata-ports" => 6,
	},
	{
		"model" => "Supermicro X7DBR-3 SAS",
		"ids" => [
			"0x928015d9,0x25d88086,0xb1",		# Chipset
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
			"0x928015d9,0x041e9005,0x09",		# SAS
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro X7DBR-3 SATA",
		"ids" => [
			"0x928015d9,0x25d88086,0xb1",		# Chipset
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
			"0x928015d9,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro X7DBi+",
		"ids" => [
			"0xb18015d9,0x269e8086,0x09",		# ATA
		],
		"sockets" => 2,
		"dimms" => 16,
		"memory-type" => "DDR2-667 ECC Fully Buffered",
		"max-memory" => "64G",
		"sata-ports" => 6,
	},
	{
		"model" => "Supermicro X7DBR",
		"ids" => [
			"0x848015d9,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 2,
		"dimms" => 16,
		"memory-type" => "DDR2-667 ECC Fully Buffered",
	},
	{
		"model" => "Supermicro X7DB8",
		"ids" => [
			"0x808015d9,0x25d88086,0x92",		# Chipset
			"0x808015d9,0x269b8086,0x09",		# SMBus
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
			"0x808015d9,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro X7DB8",
		"ids" => [
			"0x808015d9,0x25d88086,0xb1",		# Chipset
			"0x808015d9,0x269b8086,0x09",		# SMBus
			"0x(0000|1096)15d9,0x10968086,0x01",	# Network
			"0x808015d9,0x269e8086,0x09",		# ATA
		],
		"sockets" => 2,
	},
	{
		"model" => "IBM dx340",
		"ids" => [
			"0x03251014,0x268[012]8086,0x09",	# SATA
			"0x82171043,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "IBM x3850 M2",
		"ids" => [
			"0x03811014,0x27df8086,0x01",		# ATA
			"0x037c1014,0x163914e4,0x01",		# Network
			"0x03661014,0x00621000,0x03",		# SAS
		],
		"sockets" => 4,
		"dimms" => 32,
		"memory-type" => "DDR2-533 ECC Registered",
		"max-memory" => "256G",
		"chipset" => "IBM X4",
	},
	{
		"model" => "IBM x3550",
		"ids" => [
			"0x02dd1014,0x268[012]8086,0x09",	# SATA
			"0x03421014,0x164c14e4,0x11",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "IBM eServer 306m",
		"ids" => [
			"0x02fd1014,0x27c[013]8086,0x01",	# SATA
			"0x02c61014,0x165914e4,0x11",		# Network
		],
		"sockets" => 1,
	},
	{
		"model" => "Sun Fire X2100",
		"ids" => [
			"0x5348108e,0x005410de,0xf3",
			"0x5348108e,0x005710de,0xa3",
		],
		"sockets" => 1,
	},
	{
		"model" => "Sun Fire V20z",
		"ids" => [
			"0x001017c2,0x74691022,0x03",		# ATA
			"0x001017c2,0x16..14e4,0x0.",		# Network
			"0x001017c2,0x00301000,0x0.",		# SCSI, revs 0x07, 0x08
		],
		"sockets" => 2,
	},
	{
		"model" => "Sun Fire V40z",
		"ids" => [
			"0x002017c2,0x74691022,0x03",		# ATA
			"0x002017c2,0x16a714e4,0x02",		# Network
			"0x002017c2,0x00301000,0x0.",		# SCSI, revs 0x07, 0x08
		],
		"sockets" => 4,
	},
	{
		"model" => "Asus K8N-DRE",
		"ids" => [
			"0x81491043,0x165914e4,0x.1",		# revs 0x11, 0x21
			"0x81621043,0x005510de,0xf3",
		],
		"sockets" => 2,
	},
	{
		"model" => "Arima HDAMA-I SATA",		# before HDAMA-I
		"ids" => [
			"0x164814e4,0x164814e4,0x10",
			"0x3016161f,0x74691022,0x03",
			"0x61141095,0x31141095,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Arima HDAMA-I",
		"ids" => [
			"0x164814e4,0x164814e4,0x10",
			"0x3016161f,0x74691022,0x03",
		],
		"sockets" => 2,
	},
	{
		"model" => "Arima HDAMA SATA",			# before HDAMA
		"ids" => [
			"0x000c14e4,0x16a614e4,0x02",
			"0x3016161f,0x74691022,0x03",
			"0x61141095,0x31141095,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Arima HDAMA",			# before HDAMA
		"ids" => [
			"0x000c14e4,0x16a614e4,0x02",
			"0x3016161f,0x74691022,0x03",
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE1550",
		"ugly-model" => "Dell Cleared System/1000",
		"ids" => [
			"0x00da1028,0x12298086,0x08",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE2550",
		"ids" => [
			"0x00d11028,0x164414e4,0x(10|12)",	# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE2450",
		"ids" => [
			"0x009b1028,0x12298086,0x08",		# Network
			"0x00a61028,0x00cf9005,0x01",		# SCSI
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE1650",
		"ids" => [
			"0x810b1028,0x02121166,0x93",
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE2650",
		"ids" => [
			"0x01211028,0x164514e4,0x15",		# network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE2650",
		"ids" => [
			"0x01211028,0x16a714e4,0x02",		# network
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE750",
		"ids" => [
			"0x01651028,0x25a38086,0x02",
		],
		"sockets" => 1,
	},
	{
		"model" => "Dell PE1750",
		"ids" => [
			"0x014a1028,0x02121166,0x93",
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell SC1435",
		"ids" => [
			"0x01eb1028,0x024b1166,0x00",
		],
		"sockets" => 2,
		"ymodel-type" => "J",
	},
	{
		"model" => "Dell R200",
		"ids" => [
			"0x023c1028,0x165914e4,0x21",		# network
		],
		"sockets" => 1,
		"dimms" => 4,
		"memory-type" => "DDR2-800 ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "Dell R300",
		"ids" => [
			"0x020f1028,0x165a14e4,0x00",		# network
		],
		"sockets" => 1,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Unbuffered",
		"max-memory" => "24G",
	},
	{
		"model" => "Dell PE860",
		"ids" => [
			"0x01e61028,0x27788086,0x00",
		],
		"sockets" => 1,
	},
	{
		"model" => "Dell PE850",
		"ids" => [
			"0x01b61028,0x27788086,0x00",
		],
		"sockets" => 1,
		"ymodel-type" => "F",
	},
	{
		"model" => "Dell PE1850",
		"ids" => [
			"0x016c1028,0x51591002,0x00",		# VGA
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE1955",
		"ids" => [
			"0x01bb1028,0x16ac14e4,0x12", 		# Network
			"0x1f081028,0x00541000,0x01", 		# SAS
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE1855",
		"ids" => [
			"0x018a1028,0x51591002,0x00",		# VGA
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE2850",
		"ids" => [
			"0x016d1028,0x51591002,0x00",		# VGA
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE6850",
		"ids" => [
			"0x01701028,0x164814e4,0x10",		# Network
		],
		"sockets" => 4,
	},
	{
		"model" => "Dell R900",
		"ids" => [
			"0x80868086,0x36008086,0x..",		# Chipset
			"0x01f01028,0x164c14e4,0x12",		# Network
		],
		"sockets" => 4,
	},
	{
		"model" => "Dell PE6950",
		"ids" => [
			"0x01ea1028,0x02141166,0x00",
			"0x01ea1028,0x164c14e4,0x12",		# Network
		],
		"sockets" => 4,
	},
	{
		"model" => "Dell PE1950 III",
		"ugly-model" => "Dell PowerEdge 1950",
		"ids" => [
			"0x1f101028,0x00581000,0x08",		# SAS
			"0x01b31028,0x164c14e4,0x1.",		# Network - revs 0x11, 0x12
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE1950",
		"ids" => [
			"0x01b31028,0x164c14e4,0x1.",		# Network - revs 0x11, 0x12
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE2970",
		"ids" => [
			"0x02051028,0x164c14e4,0x1.",
			"0x02051028,0x024b1166,0x00",
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE2950",
		"ids" => [
			"0x01b21028,0x164c14e4,0x1.",		# Network - revs 0x11, 0x12
		],
		"sockets" => 2,
		"ymodel-type" => "M",
	},
	{
		"model" => "Dell 1600SC",
		"ids" => [
			"0x01351028,0x100e8086,0x02",		# Network
			"0x41351028,0x02121166,0x93",		# ATA
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell SC1425",
		"ids" => [
			"0x019a1028,0x51591002,0x00",		# VGA
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL365 G1",
		"ids" => [
			"0x7038103c,0x164c14e4,0x1.",		# network
			"0x320b103c,0x02141166,0x00",		# ata
		],
		"model-match" => "DL365",
		"sockets" => 2,
	},
	{
		"model" => "HP DL385 G2",
		"ids" => [
			"0x7038103c,0x164c14e4,0x1.",		# network
			"0x320b103c,0x02141166,0x00",		# ata
		],
		"model-match" => "DL385",
		"sockets" => 2,
	},
	{
		"model" => "HP DL365 G1/DL385 G2",
		"ids" => [
			"0x7038103c,0x164c14e4,0x1.",		# network
			"0x320b103c,0x02141166,0x00",		# ata
		],
		"sockets" => 2,
	},
	{
		"model" => "HP BL480c G1",			# needs to go before DL380 G5
		"ids" => [
			"0x703b103c,0x16ac14e4,0x12",		# Network
			"0x703c103c,0x167914e4,0xa3",		# Network
			"0x31fd103c,0x25d88086,0x(93|b1)",	# chipset
		],
		"sockets" => 2,
		"dimms" => 12,
		"memory-type" => "DDR2 ECC Fully Buffered",
		"max-memory" => "48G",
	},
	{
		"model" => "HP DL120 G5",
		"ids" => [
			"0x........,0x29f08086,0x..",		# Chipset
			"0x31f4103c,0x292[02]8086,0x02",	# SATA
			"0x7051103c,0x165a14e4,0x00",		# Network
		],
		"sockets" => 1,
		"dimms" => 4,
	},
	{
		"model" => "HP DL160 G5",
		"ids" => [
			"0x........,0x40038086,0x..",		# Chipset
			"0x7051103c,0x165a14e4,0x00",		# Network
# SAS version hides the following:
#			"0x31fe103c,0x269e8086,0x09",		# ATA
#			"0x31f6103c,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 2,
		"dimms" => 8,
		"memory-type" => "DDR2-800 ECC Fully Buffered",
		"max-memory" => "64G",
	},
	{
		"model" => "HP DL180 G5",			# must come before HP SE1101
		"ugly-model" => "HP ProLiant ML150 G5",
		"ids" => [
			"0x........,0x65c08086,0x80",		# Chipset
			"0x7051103c,0x165a14e4,0x00",		# Network
			"0x00000000,0x65e38086,0x80",		# PCI - needed to distinguish from HP SE1101
		],
		"sockets" => 2,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "32G",
		"max-ranks" => 8,
	},
	{
		"model" => "HP SE1101",
		"ids" => [
			"0x........,0x65c08086,0x80",		# Chipset
			"0x7051103c,0x165a14e4,0x00",		# Network
		],
		"sockets" => 2,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "32G",
		"max-ranks" => 8,
	},
	{
		"model" => "HP SB600c",				# needs to go before DL380 G5
		"ids" => [
			"0x31fd103c,0x25d88086,0x(93|b1)",	# chipset
			"0x000000(00|40),0x26908086,0x09",
			"0x3211103c,0x3238103c,0x00",		# CISS
			"0x703b103c,0x16ac14e4,0x12",		# Network
		],
		"sockets" => 2,
		"dimms" => 8,
		"memory-type" => "DDR2 ECC Fully Buffered",
		"max-memory" => "32G",
	},
	{
		"model" => "HP BL260c G5",
		"ids" => [
			"0x703c103c,0x167914e4,0xa3",		# Network
			"0x31fe103c,0x292[02]8086,0x02",	# SATA
		],
		"sockets" => 2,
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "16G",
	},
	{
		"model" => "HP DL380 G5",			# needs to go before DL360 G5
		"ids" => [
			"0x31fd103c,0x25d88086,0x(93|b1)",	# chipset
			"0x000000(00|40),0x26908086,0x09",
		],
		"sockets" => 2,
	},
	{
		"model" => "HP BL460c G1",			# needs to go before DL360 G5
		"ids" => [
			"0x703b103c,0x16ac14e4,0x12",		# Network
			"0x31fd103c,0x25d88086,0x(93|b1)",	# chipset
		],
		"sockets" => 2,
		"dimms" => 8,
		"memory-type" => "DDR2 ECC Fully Buffered",
		"max-memory" => "32G",
	},
	{
		"model" => "HP DL360 G5",
		"ids" => [
			"0x31fd103c,0x25d88086,0x(93|b1)",
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL380 G4",
		"ids" => [
			"0x32010e11,0x24db8086,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL360 G4p",
		"ids" => [
			"0x32000e11,0x35908086,0x0c",
			"0x32010e11,0x25a28086,0x02",
		],
		"model-match" => "G4p",
		"sockets" => 2,
	},
	{
		"model" => "HP DL360 G4",
		"ids" => [
			"0x32000e11,0x35908086,0x0c",
			"0x32010e11,0x25a28086,0x02",
		],
		"model-match" => "G4",
		"sockets" => 2,
	},
	{
		"model" => "HP DL360 G4/G4p",			# only diff is 4 vs. 6 dimms
		"ids" => [
			"0x32000e11,0x35908086,0x0c",
			"0x32010e11,0x25a28086,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL360 G4",
		"ids" => [
			"0x32000e11,0x35908086,0x0a",
			"0x32010e11,0x25a28086,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel S7000FC4UR",
		"ugly-model" => "Intel MP",
		"ids" => [
			"0x34cc8086,0x515e1002,0x02",		# VGA
			"0x34cc8086,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 4,
		"dimms" => 32,
		"memory-type" => "DDR2-667 ECC Fully Buffered",
		"max-memory" => "256G",
	},
	{
		"model" => "HP DL580 G5",
		"ids" => [
			"0x31f2103c,0x36008086,0x01",		# Chipset
			"0x31fe103c,0x269e8086,0x09",		# ATA
			"0x7038103c,0x164c14e4,0x1.",		# Network
		],
		"sockets" => 4,
	},
	{
		"model" => "HP DL580 G4",
		"ids" => [
			"0x3201103c,0x24db8086,0x02",		# ATA
			"0x1709103c,0x164a14e4,0x02",		# Network
		],
		"sockets" => 4,
	},
	{
		"model" => "Compaq ML530 G2",			# needs to come before DL580 G2
		"ids" => [
			"0x00000000,0x00111166,0x22",		# Chipset
			"0xa2fe0e11,0xa0f70e11,0x14",		# PCI
			"0xf6200e11,0x00c09005,0x01",		# SCSI
			"0xb1340e11,0x12298086,0x08",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Compaq DL580 G2",
		"ids" => [
			"0x00000000,0x00111166,0x22",		# Chipset
			"0xa2fe0e11,0xa0f70e11,0x14",		# PCI
		],
		"sockets" => 4,
	},
	{
		"model" => "Compaq ML570",			# needs to go before DL380 G2
		"ids" => [
			"0xa2f90e11,0xa0f70e11,0x12",		# PCI
			"0xb1430e11,0x000a1000,0x02",		# SCSI
		],
		"sockets" => 4,
	},
	{
		"model" => "Compaq ML370 G2",			# identical to 380g2
		"ids" => [
			"0xa2f90e11,0xa0f70e11,0x12",		# PCI
		],
		"model-match" => "ML370",
		"sockets" => 2,
	},
	{
		"model" => "Compaq DL380 G2",
		"ids" => [
			"0xa2f90e11,0xa0f70e11,0x12",		# PCI
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL180",				# needs to go before DL360 G3
		"ids" => [
			"0x00cb0e11,0x16a714e4,0x02",		# Network
			"0x31fe103c,0x269e8086,0x09",		# ATA
			"0x31f6103c,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL380 G3",			# needs to go before DL360 G3
		"ids" => [
			"0x00cb0e11,0x16a714e4,0x02",		# Network
			"0xa2fe0e11,0xa0f70e11,0x14",		# PCI
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL560 G1",			# identical to 360g3
		"ids" => [
			"0x00cb0e11,0x16a714e4,0x02",		# Network
			"0x40800e11,0xb1780e11,0x01",		# ciss
		],
		"model-match" => "DL560",
		"sockets" => 4,
	},
	{
		"model" => "HP DL360 G3",			# identical to 560g1
		"ids" => [
			"0x00cb0e11,0x16a714e4,0x02",		# Network
			"0x40800e11,0xb1780e11,0x01",		# ciss
		],
		"sockets" => 2,
	},
	{
		"model" => "Compaq DL360 G2",
		"ids" => [
			"0x00850e11,0x164514e4,0x15",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL320 G5",			# could also be a HP DL320s
		"ids" => [
			"0x31fe103c,0x27df8086,0x01",		# ATA
			"0x7039103c,0x166814e4,0xa3",		# Network
		],
		"sockets" => 1,
		"dimms" => 4,
		"memory-type" => "DDR2-667 ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "HP DL320 G3",
		"ids" => [
			"0x32000e11,0x25888086,0x05",
			"0x32010e11,0x26528086,0x03",
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DL320 G2",
		"ids" => [
			"0x00bb0e11,0x16a614e4,0x02",
		],
		"sockets" => 1,
	},
	{
		"model" => "Compaq DL360",
		"ids" => [
			"0xb1340e11,0x12298086,0x08",		# Network
			"0x....0e11,0x00..1000,0x02",		# SCSI
			"0xb0f30e11,0xa0f00e11,0x00",		# Advanced System Management Controller
		],
		"sockets" => 2,
	},
	{
		"model" => "Compaq DL320",
		"ids" => [
			"0xb1340e11,0x12298086,0x08",		# Network
			"0x00000000,0x02111166,0x00",		# ATA
			"0x001e0e11,0x47521002,0x27",		# VGA
		],
		"sockets" => 1,
	},
	{
		"model" => "HP DL140 G2",
		"ids" => [
			"0x3208103c,0x24d[1b]8086,0x02",	# SATA/ATA
			"0x1659....,0x165914e4,0x11",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL140 G3",
		"ids" => [
			"0x25c08086,0x25c08086,0x..",		# Chipset
			"0x31fe103c,0x269b8086,0x09",		# SMBus
			"0x3260103c,0x165914e4,0x11",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL145 G3",
		"ids" => [
			"0x703e103c,0x167814e4,0xa3",		# Network
			"0x320b103c,0x02141166,0x00",		# ATA
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL185 G5",
		"ids" => [
			"0x320b103c,0x02141166,0x00",		# SATA
			"0x310f103c,0x164814e4,0x10",		# Network
		],
		"sockets" => 2,
		"dimms" => 8,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "32G",
		"max-ranks" => 8,
	},
	{
		"model" => "HP DL145 G2",
		"ids" => [
			"0x3209103c,0x165914e4,0x11",
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL145 G1",
		"ids" => [
			"0x36c01022,0x74691022,0x03",		# ATA
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL585 G2",
		"ids" => [
			"0x1709103c,0x164a14e4,0x02",
			"0x31f8103c,0x005310de,0xa3",
		],
		"sockets" => 4,
	},
	{
		"model" => "HP DL585 G1",
		"ids" => [
			"0x32040e11,0x74691022,0x03",
			"0x40800e11,0xb1780e11,0x01",		# ciss
		],
		"sockets" => 4,
	},
	{
		"model" => "HP DL385 G1",
		"ids" => [
			"0x32040e11,0x74691022,0x03",
			"0x40910e11,0x00460e11,0x01",
		],
		"sockets" => 2,
	},
	{
		"model" => "HP DL140",
		"ids" => [
			"0x001e0e11,0x47521002,0x27",
			"0x164814e4,0x164814e4,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro PDSMi",
		"ids" => [
			"0x109a15d9,0x109a8086,0x00",		# Network
			"0x798015d9,0x27c[013]8086,0x01",	# SATA
		],
		"sockets" => 1,
	},
	{
		"model" => "Supermicro PDSML",
		"ids" => [
			"0x109a15d9,0x109a8086,0x00",		# Network
			"0x858015d9,0x27c[013]8086,0x01",	# SATA
		],
		"sockets" => 1,
		"dimms" => 4,
		"memory-type" => "DDR2-667 ECC Unbuffered",
		"max-memory" => "8G",
	},
	{
		"model" => "Supermicro X6DLP",
		"ids" => [
			"0x109a15d9,0x109a8086,0x00",		# Network
			"0x108c15d9,0x108c8086,0x03",
			"0x698015d9,0x25a38086,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Supermicro X7DCT",
		"ids" => [
			"0x108c15d9,0x108c8086,0x03",		# Netwwork
			"0xde8015d9,0x515e1002,0x02",		# VGA
		],
		"sockets" => 2,
		"socket-type" => "Intel-LGA771",
		"dimms" => 6,
		"memory-type" => "DDR2-667 ECC Registered",
		"max-memory" => "48G",
		"sata-ports" => 4,
		"lan-ports" => 2,
		"serial-ports" => 1,
		"PCI-e x16" => 1,
		"form-factor" => "41.66cm x 16.51cm",
	},
	{
		"model" => "Intel S3000PT",
		"ids" => [
			"0x349[ab]8086,0x27c[013]8086,0x01",	# SATA
			"0x349b8086,0x108b8086,0x03",
		],
		"sockets" => 1,
	},
	{
		"model" => "Intel S5000PAL",
		"ids" => [
			"0x346[cd]8086,0x25d88086,0xb1",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel S5400SF",
		"ids" => [
			"0x34d48086,0x268[012]8086,0x09",	# SATA
			"0x34d48086,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
		"dimms" => 16,
		"memory-type" => "DDR2-667 ECC Fully Buffered",
		"max-memory" => "64G",
	},
	{
		"model" => "Intel S5000XSL",
		"ids" => [
			"0x347a8086,0x268[012]8086,0x09",	# SATA
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel S5000VSA",
		"ids" => [
			"0x34848086,0x10968086,0x01",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel S5000PSL",
		"ids" => [
			"0x34768086,0x25d88086,0x(92|b1)",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel S5000PSL-SAS",
		"ids" => [
			"0x34788086,0x25d88086,0xb1",
		],
		"sockets" => 2,
	},
	{							# with scsi
		"model" => "Intel SE7520JR2S",			# needs to go before SE7520JR2
		"ids" => [
			"0x10798086,0x10798086,0x03",
			"0x34358086,0x00301000,0x08",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7520JR2",
		"ids" => [
			"0x10798086,0x10798086,0x03",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7520BB2",
		"ugly-model" => "Intel SE7520BB2..?",
		"ids" => [
			"0x347e8086,0x10768086,0x05",		# Network
		],
		"sockets" => 2,
		"ymodel-type" => "J",
	},
	{							# with scsi
		"model" => "Intel SE7520BD2S",			# needs to go before SE7520BD2
		"ids" => [
			"0x34398086,0x10768086,0x05",		# Network
			"0x34628086,0x00301000,0xc1",
		],
		"sockets" => 2,
	},
	{							# with sata
		"model" => "Intel SE7520BD2D",			# needs to go before SE7520BD2
		"ids" => [
			"0x34398086,0x10768086,0x05",		# Network
			"0x34398086,0x24d18086,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7520BD2",
		"ids" => [
			"0x34398086,0x10768086,0x05",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7320VP2",
		"ids" => [
			"0x34448086,0x10768086,0x05",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7320VP2",			# 2nd version of SE7320VP2
		"ids" => [
			"0x34508086,0x10768086,0x05",		# Network
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7500WV2",			# scsi version of SE7500WV2A
		"ids" => [
			"0x34158086,0x25408086,0x03",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7500WV2A",
		"ids" => [
			"0x34168086,0x25408086,0x03",
		],
		"sockets" => 2,
	},
	{							# with scsi
		"model" => "Intel SE7501WV2S",			# needs to go before SE7501WV2
		"ids" => [
			"0x341a8086,0x254c8086,0x01",
			"0x341a8086,0x801f9005,0x03",
		],
		"sockets" => 2,
	},
	{							# we had some SE7501WV2S without scsi ids
		"model" => "Intel SE7501WV2",
		"ids" => [
			"0x341a8086,0x254c8086,0x01",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7501WV2A",
		"ids" => [
			"0x341b8086,0x254c8086,0x01",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7501CW2",
		"ids" => [
			"0x34258086,0x248b8086,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7500CW2",
		"ids" => [
			"0x34198086,0x248b8086,0x02",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SE7501BR2",
		"ids" => [
			"0x34188086,0x254c8086,0x01",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel S875WP1",
		"ids" => [
			"0x34288086,0x24d18086,0x02",
		],
		"sockets" => 1,
	},
	{
		"model" => "Intel CA810E",
		"ids" => [
			"0x43328086,0x71258086,0x03",
		],
		"sockets" => 1,
	},
	{
		"model" => "Intel TR440BX",
		"ids" => [
			"0x71908086,0x71928086,0x03",		# Chipset
			"0x30008086,0x12298086,0x08",		# Network
			"0x00000000,0x71118086,0x01",		# ATA
		],
		"sockets" => 1,
	},
	{
		"model" => "Intel L440GX+",
		"ids" => [
			"0x00000000,0x71a08086,0x00",
			"0x(00539004|080f9005|ffff9005),0x005f9005,0x00",
			"0x00bc1013,0x00bc1013,0x23",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel N440BX",
		"ids" => [
			"0x00000000,0x71928086,0x..",
			"0x........,0x000f1000,0x37",
			"0x00088086,0x12298086,0x05",
			"0x00000000,0x71118086,0x01",
			"0x........,0x00bc1013,0x23",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel S845WD1",
		"ids" => [
			"0x57448086,0x244b8086,0x05",
		],
		"sockets" => 1,
	},
	{
		"model" => "Intel SCB20-S",		# SCSI version
		"ids" => [
			"0x340f8086,0x12298086,0x0d",
			"0x340f8086,0x02121166,0x92",
		],
		"sockets" => 2,
	},
	{
		"model" => "Intel SCB20-A",
		"ids" => [
			"0x34108086,0x12298086,0x0d",
			"0x34108086,0x02121166,0x92",
		],
		"sockets" => 2,
	},
	{
		"model" => "Dell PE6650",
		"ids" => [
			"0x01091028,0x164414e4,0x14",		# network
		],
		"sockets" => 4,
	},
	{
		"model" => "Compaq 6400R",
		"ids" => [
			"0xb0dd0e11,0x12298086,0x05",		# network
		],
		"sockets" => 4,
	},

	# Laptops
	{
		"model" => "Dell Latitude D610",
		"ids" => [
			"0x01821028,0x26538086,0x03",		# SATA
			"0x01821028,0x167714e4,0x01",		# Network
			"0x27228086,0x42208086,0x05",		# Wireless
		],
		"sockets" => 1,
		"dimms" => 2,
		"memory-type" => "DDR2-533 non-ECC Unbuffered",
		"max-memory" => "2G",
	},
	{
		"model" => "HP NC6220",
		"ids" => [
			"0x0944103c,0x266f8086,0x03",		# ATA
			"0x0944103c,0x167d14e4,0x11",		# Network
			"0x12f6103c,0x42208086,0x05",		# Wireless
		],
		"sockets" => 1,
		"dimms" => 2,
		"memory-type" => "DDR2-533 non-ECC Unbuffered",
		"max-memory" => "2G",
	},
	{
		"model" => "HP NC6400",
		"ids" => [
			"0x30ad103c,0x27df8086,0x01",		# ATA
			"0x30ad103c,0x16fd14e4,0x21",		# Network
			"0x135[bc]103c,0x42228086,0x02",	# Wireless
		],
		"sockets" => 1,
		"dimms" => 2,
		"memory-type" => "DDR2-667 non-ECC Unbuffered",
		"max-memory" => "4G",
	},

);

my(%hostid_synonyms);
my(%hostid_to_pci);
my(%hostid_to_driver);

my(%linux_driver_map) = (
	"lsi" => "mpt",
	"3w" => "twe",
);

sub sig_warn {
        my($err) = @_;
	$err = "$zero: perl-warn: $err";
	$perl_warn .= $err;
	warn $err;
}

sub sig_die {
        my($err) = @_;
	$err = "$zero: perl-die: $err";
	$perl_warn .= $err;
	email('', '');
	die $err;
}

$SIG{'__WARN__'} = 'sig_warn';
$SIG{'__DIE__'} = 'sig_die';

sub watchdog {
	die "watchdog expired; something is taking too long";
}

sub usage {
	print "usage:\t$zero [-dhnv] [-o output-file]\n";
	print "\t-d  show debugging information\n";
	print "\t-h  show usage\n";
	print "\t-n  don't break output lines\n";
	print "\t-v  show version\n";
	print "\t-o  write output to a file\n";
}

sub round {
	my($number) = @_;
	return int($number + 0.5);
}

sub cmp_devs {
	my($aa, $bb) = ($a, $b);
	$aa = $hostid_to_pci{$aa} if ($hostid_to_pci{$aa});
	$bb = $hostid_to_pci{$bb} if ($hostid_to_pci{$bb});
	my($atype, $anum) = ($aa =~ /^(.*?)(\d*)$/);
	my($btype, $bnum) = ($bb =~ /^(.*?)(\d*)$/);
	return ($atype cmp $btype || $anum <=> $bnum);
}

sub parse_enable {
	my($string, $more_yes, $more_no) = @_;
	my($yes) = "yes|on|enabled|1";
	my($no) = "no|off|disabled|0";
	$yes .= "|$more_yes" if $more_yes;
	$no .= "|$more_no" if $more_no;
	if ($string =~ /^($yes)$/i) {
		return 1;
	} elsif ($string =~ /^($no)$/i) {
		return 0;
	} else {
		return;
	}
}

sub parse_value {
	my($string) = @_;
	if ($string =~ /^(\d+)/) {
		return $1;
	}
	return $string;
}

sub parse_bytes {
	my($string, $factor) = @_;
	my($result) = 0;
	my(%exp) = (
		"K" => 1,
		"KB" => 1,
		"M" => 2,
		"MB" => 2,
		"G" => 3,
		"GB" => 3,
		"T" => 4,
		"TB" => 4,
	);
	if ($string =~ /^\s*([\d\.]+)\s*(K|KB|M|MB|G|GB|T|TB)\s*$/i) {
		$result = $1 * (($factor || 1000) ** $exp{uc($2)});
	}
}

sub parse_rpm {
	my($string) = @_;
	my($result) = "";
	my(@rpms) = (4500, 5400, 7200, 10000, 15000);
	my($low) = 0;
	my($high);
	foreach $high (@rpms) {
		if ($string <= $high) {
			if ($high < 10000) {
				$result = sprintf("%3.1fK", $high / 1000);
			} else {
				$result = sprintf("%2dK", $high / 1000);
			}
			last;
		}
	}
	return $result;
}

sub parse_volume_status {
	my($string) = @_;
	my($result);
	if ($string =~ /^(OK|Optimal)$/i) {
		$result = "ok";
	} elsif ($string =~ /^(Degraded)$/i) {
		$result = "degraded";
	} elsif ($string =~ /^(Failed|INTERM RECOVERY|Interim Recovery Mode|BROKEN)$/i) {
		$result = "failed";
	} else {
		$result = $string;
	}
	return $result;
}

sub parse_drive_status {
	my($string) = @_;
	my($result);
	if ($string =~ /^(OK|Ready)/i) {
		$result = "ok";
	} elsif ($string =~ /^(Online)/i) {
		$result = "online";
	} elsif ($string =~ /^(Offline)/i) {
		$result = "offline";
	} elsif ($string =~ /^(Failed)/i) {
		$result = "failed";
	} elsif ($string =~ /^(Rebuild)/i) {
		$result = "rebuilding";
	} else {
		$result = $string;
	}
	return $result;
}

sub parse_drive_interface {
	my($string) = @_;
	my($result) = $string;
	if ($string =~ /Parallel SCSI/) {
		$result = "SCSI";
	} elsif ($string =~ /SAS/) {
		$result = "SAS";
	}
	return $result;
}

sub parse_drive_speed {
	my($string) = @_;
	my($result) = "";
	if ($string =~ /320/) {
		$result = "U320";
	} elsif ($string =~ /SATA 1\.5 Gb\/s/) {
		$result = "SATA-150";
	} elsif ($string =~ /SATA 3\.0 Gb\/s/) {
		$result = "SATA-300";
	}
	return $result;
}

sub canon_raid {
	my($raid) = @_;
	return unless (defined $raid);

	$raid =~ s/RAID 6 \(ADG\)/RAID ADG/;

	$raid =~ s/volume//i;
	$raid =~ s/[^a-z0-9]+$//i;	# nuke garbage from the end

	$raid =~ s/raid\s+raid/raid/ig;

	$raid =~ s/SINGLE/JBOD/i;
	$raid =~ s/RAID Simple/JBOD/i;

	$raid =~ s/^\s+//;
	$raid =~ s/\s+$//;

	$raid =~ s/\-/ /g;

	$raid =~ s/raid(.+)/RAID $1/i;

	# /proc/driver/cciss/cciss0
	$raid =~ s/1\(1\+0/10/;
	$raid =~ s/1\(0\+1/10/;

	# hpacucli
	$raid =~ s/1\+0/10/;
	$raid =~ s/0\+1/10/;

	# aac
	$raid =~ s/0\/1/10/;

	$raid =~ s/\s+/-/g;

	return $raid;
}

sub canon_cpu_vendor {
	my($vendor) = @_;
	if ($vendor =~ /Intel|Pentium|Xeon|Celeron/i) {
		$vendor = 'Intel';
	} elsif ($vendor =~ /AMD/i) {
		$vendor = 'AMD';
	} elsif ($vendor =~ /^I/i) {
		$vendor = 'Intel';
	}
	return $vendor;
}

sub canon_drive {
	my($model) = @_;
	$model =~ s/^\s+//;
	$model =~ s/\s+$//;
	$model =~ s/[\W_]+/-/ig;
	$model =~ s/^\-+//;
	$model =~ s/\-+$//;
	return $model;
}

sub canon_memory {
	my($model) = @_;
	if ($model =~ /^0x/ || (length($model) > 18 && $model =~ /^[0-9a-z]+$/i)) {
		$model =~ s/^0x0*//;
		$model =~ s/(..)/sprintf("%c", hex($1))/ge;
	}
	$model =~ s/\s+/ /g;
	$model =~ s/^\s+//;
	$model =~ s/\s+$//;
	return $model;
}

sub clean_white {
	my($string) = @_;
	$string =~ s/\s+/ /g;
	$string =~ s/^\s+//;
	$string =~ s/\s+$//;
	return $string;
}

sub clean_pci_model {
	my($model) = @_;
	$model =~ s/\(was:.*\)//i;		# freebsd
	$model =~ s/\(rev .*\)$//;		# linux
	$model =~ s/PCI Express/PCI-E/i;
	$model =~ s/(Gigabit) Ethernet( Controller( \(Copper\))?)?/$1/i;
	$model =~ s/(Gigabit) Network Connection/$1/i;
	$model =~ s/\b(Corporation|Corp\.?|Inc|Co\.|Ltd\.|Technologies Inc|Technology Group Ltd\.|Semiconductor Co\.\,)//ig;
	$model =~ s/\s+(Controller|Adapter)$//i;
	return clean_white($model);
}

sub print_bytes {
	my($number, $factor, $int, $round_limit) = @_;
	my(@suffix) = ('', 'K', 'M', 'G', 'T', 'P', 'E');
	my($i) = 0;
	$factor = 1000 unless $factor;
	$round_limit = 10 unless $round_limit;
	# keep going until we're below 1000, otherwise can end up with 1020MB
	# given that we round to one decimal, 1001/1024 will round to 1.0, i.e. we avoid 0.98 type results
	while ($number >= 1000) {
		$number = $number / $factor;
		$i++;
	}
	my($result);
	if ($number >= $round_limit) {
		$result = sprintf("%.0f%sB", $number, $suffix[$i]);
	} elsif ($number) {
		$result = sprintf("%3.1f%sB", $number, $suffix[$i]);
		$result =~ s/\.0// if ($int);
	} else {
		$result = sprintf("0B");
	}
	return $result;
}

sub cpuid {
	my($vendor, $cpuid) = @_;
	$vendor = canon_cpu_vendor($vendor);
	my($family, $model, $stepping, $ext_model, $ext_family);
	$stepping = $cpuid & 0xf;
	$model = ($cpuid >> 4) & 0xf;
	$family = ($cpuid >> 8) & 0xf;
	$ext_model = ($cpuid >> 16) & 0xf;
	$ext_family = ($cpuid >> 20) & 0xff;
	$family += $ext_family;
	$model += ($ext_model << 4);
	return "$vendor-$family,$model,$stepping";
}

sub init_dmesg {
	my($in) = 'DMESG';
	my($line);
	if (-r $dmesg_file) {
		open($in, "< $dmesg_file") || warn("open: $dmesg_file");
		while ($line = <$in>) {
			$dmesg_buf .= $line;
		}
		close($in);
	} else {
		$dmesg_buf = `$dmesg_prog`;
	}
}

my($pci_all) = "";
my($guess_system) = "Unknown";
my($system_model_index);

sub guess_system {
	my($init) = @_;
	my($system) = "";
	my($buf) = $dmesg_buf;
	my($i) = 0;

	my($model);
	foreach $model (@system_models) {
		my($guess) = $model->{'model'};
		my($id);
		my($match) = 1;
		foreach $id (@{$model->{'ids'}}) {
			if (!($pci_all =~ /<$id>/)) {
				$match = 0;
				last;
			}
		}
		if ($match && defined $system_models[$i]{'model-match'}) {
			$match = 0 unless ($init =~ /$system_models[$i]{'model-match'}/);
		}
		if ($match) {
			$system = $guess;
			$guess_system = $guess;
			$system_model_index = $i;
			last;
		}
		$i++;
	}

	if ($guess_system eq "Unknown" && $< == 0) {
		$debug .= "Debug-System:\t$guess_system\n";
	}

	return $system if ($system);

	if ($buf =~ /ServerWorks ROSB4 ATA33/ && $buf =~ /fxp1:/) {
		if ($buf =~ /CMD 649 ATA100/ && $buf =~ /Mach64-GR/) {
			$system = "Compaq ProLiant DL320?";
		} elsif (($buf =~ /sym1:/ || $buf =~ /ida0:/) && $buf =~ /Mach64-GV/) {
			$system = "Compaq ProLiant DL360?";
		} elsif ($buf =~ /Compaq Smart Array 5i/ && $buf =~ /Mach64-GR/) {
			$system = "Compaq ProLiant DL380 G2?";
		}

	} elsif ($buf =~ /ServerWorks CSB5 ATA100/ && $buf =~ /bge1:/ && $buf =~ /Mach64-GR/ && $buf =~ /Compaq Smart Array 5i/) {
		if ($buf =~ /Broadcom BCM5701/) {
			$system = "Compaq ProLiant DL360 G2?";
		} elsif ($buf =~ /Broadcom BCM5703/) {
			if ($buf =~ /pci6/ && $buf =~ /pci7/) {
				$system = "Compaq ProLiant DL380 G3?";
			} else {
				$system = "HP ProLiant DL360 G3?";
			}
		}

	} elsif ($buf =~ /Broadcom BCM5702/ && $buf =~ /bge1:/ && $buf =~ /Mach64-GR/) {
		if ($buf =~ /CMD 649 ATA100/) {
			$system = "HP ProLiant DL320 G2?";
		}

	} elsif ($buf =~ /HP Smart Array 6i/ && $buf =~ /bge1:/ && $buf =~ /Mach64-GR/) {
		if ($buf =~ /Intel 6300ESB ATA100/) {
			$system = "HP ProLiant DL360 G4?";
		} elsif ($buf =~ /Intel ICH5 ATA100/) {
			$system = "HP ProLiant DL380 G4?";
		}

	}
	return ($system || "Unknown");
}


my(%interfaces);
my(%pci_map);
my(%pci_to_driver);
my(%irq_to_pci);
my(%irq_to_driver);
my(%scsi_to_driver);

my($ndrives) = 0;
my($drive_id) = 1;
my(%volumes);
my(%drives);
my(%props);

my(%logical_to_os);
my(%controller_volumes);

sub grok_proc_scsi {
	my($file) = 'FILE';
	my($line);

	return unless ($os_type eq "linux");

	if (-e $proc_scsi) {
		my($letter) = 'a';
		my($dev, $channel, $id, $lun, $model, $vendor, $vendor_model);
		$need_arcconf++ if (-e "/proc/scsi/aacraid");
		open($file, "< $proc_scsi") || warn("open: $proc_scsi: $!");
		$debug_storage .= ">> $proc_scsi\n";
		while ($line = <$file>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^Host:\s+(\S+)\s+Channel:\s+(\S+)\s+Id:\s+(\S+)\s+Lun:\s+(\S+)/) {
				($dev, $channel, $id, $lun) = ($1, $2, $3, $4);
			} elsif ($line =~ /^Host: (\S+)/) {
				$dev = $1;
			} elsif ($line =~ /^\s*Vendor:\s*(.*)Model:\s*(.*\S)\s*Rev:/) {
				($vendor, $model) = ($1, $2);
				$vendor_model = "$vendor $model";
				$need_megacli++ if ($vendor_model =~ /$perc_names/);	# no /i so that PepperCo doesn't match
				$need_megarc++ if ($vendor_model =~ /MegaRAID/);
				$need_twcli++ if ($vendor_model =~ /(3ware|AMCC)/);
			} elsif ($line =~ /^\s*Type:\s*Direct-Access/) {
				my($vol) = "sd" . $letter++;
				my($device) = vol_to_hostid($vol) || $dev;
				$volumes{$vol}++;
				if ($model =~ /(raid\d+)/i) {
					$props{$vol}{raid} = $1;
				} else {
					$props{$vol}{raid} = "JBOD";
				}
				if ($vendor =~ /^(PepperC|SMSC)/) {
					$props{$vol}{hidden} = 1;
				}
				$props{$vol}{device} = $device if ($device);

				$drives{$vol}{$drive_id}++;
				$props{drives}{$drive_id}{model} = canon_drive($vendor_model);
				$props{drives}{$drive_id}{volume} = $vol;
				smartctl($vol, "/dev/$vol", $vendor, $drive_id);
				hdparm($vol, "/dev/$vol", $drive_id);
				$drive_id++;
				$ndrives++;

				if (defined($dev) && defined($channel)) {
					if ($vendor_model =~ /MegaRAID/) {
						my($logical) = sprintf("%s-%d-%d-%d", $dev, $channel, $id, $lun);
						$logical_to_os{'megaraid'}{$logical} = $vol;
					} elsif ($vendor_model =~ /$perc_names/) {
						my($logical) = sprintf("%s-%d-%d-%d", $dev, $channel, $id, $lun);
						$logical_to_os{'perc'}{$logical} = $vol;
					} elsif ($vendor_model =~ /(3ware|AMCC)/i) {
						my($logical) = sprintf("c%d-u%d", ($dev =~ /^scsi(\d+)/), $id);
						$logical_to_os{'tw'}{$logical} = $vol;
					} else {
						$logical_to_os{'other'}{$model} = $vol;
					}
				}
				undef $dev;
				undef $channel;
				undef $id;
				undef $lun;
			}
		}
		close($file);
	}
}

# need to parse /sys/bus/pci/drivers/cciss/ to get pci-id to match with controller

sub grok_proc_cciss {
	my($dir) = 'DIR';
	my($file) = 'FILE';
	my($line);

	return unless ($os_type eq "linux" && -d $proc_cciss);

	$need_hpacucli++;

	opendir($dir, $proc_cciss) || warn("opendir: $proc_cciss: $!");
	my(@dirs) = readdir($dir);
	closedir($dir);

	my(@dirs2);
	if (-d $sys_cciss) {
		opendir($dir, $sys_cciss) || warn("opendir: $sys_cciss: $!");
		@dirs2 = readdir($dir);
		closedir($dir);
	}

	$debug_storage .= ">> $proc_cciss\n";

	my($ent, $ent2);
	foreach $ent (@dirs) {
		if ($ent =~ /^cciss\d+$/) {
			open($file, "< $proc_cciss/$ent") || warn("open: $proc_cciss/$ent: $!");
			$debug_storage .= ">>> $proc_cciss/$ent\n";
			while ($line = <$file>) {
				$debug_storage .= $line;
				chomp($line);
				if ($line =~ /firmware version: (\S+)/i) {
					$props{$ent}{'firmware'} = $1;
				} elsif ($line =~ /^(cciss\/c\d+d\d+):\s+([\d\.]+)GB\s+(\S+.*)\s*$/) {
					my($vol) = $1;
					$volumes{$vol}++;
					$props{$vol}{raid} = $3;

					$props{$vol}{device} = $ent;
					$controller_volumes{$ent}{$vol}++;

					foreach $ent2 (@dirs2) {
						if ($ent2 =~ /^[0-9a-f]{4}:(.*)/i) {
# not sure when this was added and why.  it only seems to screw things up
# commenting out the next line on 20080329, the other two were commented out long ago
#							$props{$vol}{device} = $1;
#							$other_devices{'Disk-Control'}{$dev} = "$dev: $model";
#							$cleanup{'Disk-Control'}{$num}++;
						}
					}
					
				}
			}
			close($file);
		}
	}
}

sub ccdconfig {
	my($in) = 'CCD';
	my($line);
	my($result) = 0;

	system("which ccdconfig > /dev/null 2>&1");
	return $result if ($?);

	open($in, "ccdconfig -g 2> /dev/null |") || warn "exec ccdconfig: $!";
	$debug_storage .= ">> ccdconfig -g\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		$result = 1;
		my($vol, $ileave, $flags, @parts) = split(' ', $line);
		if ($flags =~ /MIRROR/) {
			$props{$vol}{raid} = "RAID-1";
		} else {
			$props{$vol}{raid} = "RAID-0";
			$props{$vol}{stripe} = $ileave * 512;
		}
		my($model) = "";
		my($sep) = "partitions: ";
		my($part);
		foreach $part (@parts) {
			$part =~ s,/dev/,,;
			$model .= "$sep$part";
			$sep = ", ";
		}
		$volumes{$vol}++;
		$drives{$vol}{$drive_id}++;
		$props{drives}{$drive_id}{model} = $model;
		$props{drives}{$drive_id}{volume} = $vol;
		$drive_id++;
		$ndrives++;
	}
	close($in);

	return $result;
}

sub grok_mdstat {
	my($file) = 'FILE';
	my($line);
	my($vol, $raid, $rest);

	return unless ($os_type eq "linux");

	if (-e $proc_mdstat) {
		open($file, "< $proc_mdstat") || warn("open: $proc_mdstat: $!");
		$debug_storage .= ">> $proc_mdstat\n";
		while ($line = <$file>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^(md\d+).*(raid\d+)\s+(.*)/) {
				($vol, $raid, $rest) = ($1, $2, $3);
				$props{$vol}{raid} = $raid;
				my($dev);
				my($model) = "";
				my($sep) = "partitions: ";
				my($part, %parts);
				foreach $dev (split(' ', $rest)) {
					if ($dev =~ /^(.*)\[(\d+)\]/) {
						$parts{$2} = $1;
					}
					$dev =~ s/\[(\d+)\]//;
					$dev =~ s/\d+$//;
					$props{$dev}{inmd}++;
				}
				foreach $part (sort keys %parts) {
					$model .= "$sep$parts{$part}";
					$sep = ", ";
				}
				$drives{$vol}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{volume} = $vol;
				$drive_id++;
				$ndrives++;
			} elsif ($line =~ /(\S+)\s+chunks/) {
				$props{$vol}{stripe} = parse_bytes($1, 1024);
			}
		}
	}
}

sub grok_proc_partitions {
	my($file) = 'FILE';
	my($line);

	return unless ($os_type eq "linux");

	if (-e $proc_partitions) {
		open($file, "< $proc_partitions") || warn("open: $proc_partitions: $!");
		$debug_storage .= ">> $proc_partitions\n";
		while ($line = <$file>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+([hs]d[a-z]+)(\s+|$)/) {
				my($blocks) = $3;
				my($vol) = $4;
				$volumes{$vol}++;
				$props{$vol}{bytes} = $blocks * 1024;
			} elsif ($line =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+((cciss|ida)\/c\d+d\d+)(\s+|$)/) {
				my($blocks) = $3;
				my($vol) = $4;
				$volumes{$vol}++;
				$props{$vol}{bytes} = $blocks * 1024;
				$need_hpacucli++;
			} elsif ($line =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(rd\/c\d+d\d+)(\s+|$)/) {
				my($blocks) = $3;
				my($vol) = $4;
				$volumes{$vol}++;
				$props{$vol}{bytes} = $blocks * 1024;
				$need_rd++;
			} elsif ($line =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(md\d+)(\s+|$)/) {
				my($blocks) = $3;
				my($vol) = $4;
				$volumes{$vol}++;
				$props{$vol}{bytes} = $blocks * 1024;
				$need_mdstat++;
			} elsif ($line =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(lvm\S+)(\s+|$)/) {
				$need_lvm++;
			} elsif ($line =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(dm-\d+)(\s+|$)/) {
				$need_lvm++;
			}
		}
		close($file);
	}
}

sub grok_proc_ide {
	my($dir) = 'DIR';

	return unless ($os_type eq "linux" && -d $proc_ide);

	opendir($dir, $proc_ide) || warn("opendir: $proc_ide: $!");
	my(@dirs1) = readdir($dir);
	closedir($dir);

	my($ent1);
	foreach $ent1 (@dirs1) {
		if ($ent1 =~ /^ide\d+$/ && -d "$proc_ide/$ent1") {
			opendir($dir, "$proc_ide/$ent1") || warn("opendir: $proc_ide/$ent1: $!");
			my(@dirs2) = readdir($dir);
			closedir($dir);

			my($ent2);
			foreach $ent2 (@dirs2) {
				if ($ent2 =~ /^hd([a-z])$/ && -d "$proc_ide/$ent1/$ent2") {
					my($letter) = $1;
					my($vol) = $ent2;
					my($media, $model, $blocks, $cache);
					chomp($media = `cat $proc_ide/$ent1/$vol/media 2> /dev/null`);
					next if ($media =~ /(cdrom|unknown)/i);
					chomp($model = `cat $proc_ide/$ent1/$vol/model 2> /dev/null`);
					chomp($blocks = `cat $proc_ide/$ent1/$vol/capacity 2> /dev/null`);
					chomp($cache = `cat $proc_ide/$ent1/$vol/cache 2> /dev/null`);
					$cache *= 1024 if ($cache);
					$model = canon_drive($model);
					$volumes{$vol}++;
					$props{$vol}{raid} = "JBOD";
					$props{$vol}{bytes} = $blocks * 512;
					$props{$vol}{device} = $ent1;
					$props{$vol}{ms} = ((ord($letter) - ord('a')) % 2) ? "slave" : "master";
					$drives{$vol}{$drive_id}++;
					$props{drives}{$drive_id}{model} = $model;
					$props{drives}{$drive_id}{size} = $blocks * 512;
					$props{drives}{$drive_id}{cache_size} = $cache;
					$props{drives}{$drive_id}{volume} = $vol;

					# override with hdparm info if we have it
					smartctl($vol, "/dev/$vol", "ATA", $drive_id);
					hdparm($vol, "/dev/$vol", $drive_id);

					$drive_id++;
					$ndrives++;
				}
			}
		}
	}
}

sub pci_to_driver {
	my($root) = "/sys/bus/pci/drivers";
	my($dir) = 'DIR';
	my($driver);

	if (-d $root) {
		opendir($dir, "$root") || warn("opendir: $root: $!");
		my(@dirs) = readdir($dir);
		closedir($dir);
		foreach $driver (sort @dirs) {
			next if ($driver =~ /^\.+$/);
			opendir($dir, "$root/$driver") || warn("opendir: $root/$driver: $!");
			my(@files) = readdir($dir);
			closedir($dir);
			my($file);
			my($count) = 0;
			foreach $file (sort @files) {
				if ($file =~ /^0000:(.*)/) {
					my($pci) = $1;
					$pci_to_driver{$pci} = lc($driver);
					$pci_to_driver{$pci} =~ s/\s+/_/g;
					$pci_to_driver{$pci} .= "-" if ($driver =~ /\d$/);
					$pci_to_driver{$pci} .= $count++;
				}
			}
		}
	}
}

sub pci_to_hostid {
	my($pci) = @_;
	my($root) = "/sys/bus/pci/devices";
	my($dir) = 'DIR';
	my($file) = 'FILE';
	my(@dirs1);
	my($hostid);
	my($full_pci) = $pci;

	$full_pci = "0000:$full_pci" unless (-d "$root/$full_pci");
	if (-d "$root/$full_pci") {
		opendir($dir, "$root/$full_pci") || warn("opendir: $root/$full_pci: $!");
		my(@dirs1) = readdir($dir);
		closedir($dir);

		my($ent1);
		foreach $ent1 (sort @dirs1) {
			if ($ent1 =~ /^host\d+$/) {
				if ($hostid) {
					$hostid_synonyms{$ent1} = $hostid;
				} else {
					$hostid = $ent1;
				}

				$hostid_to_pci{$ent1} = $pci;
				if ($pci_to_driver{$pci}) {
					$hostid_to_driver{$ent1} = $pci_to_driver{$pci};
				} elsif (-e "/sys/class/scsi_host/$ent1/proc_name") {
					my($driver) = `cat "/sys/class/scsi_host/$ent1/proc_name"`;
					chomp($driver);
					$driver = $linux_driver_map{$driver} || $driver;
					$driver .= $1 if ($ent1 =~ /(\d+)$/);
					$hostid_to_driver{$ent1} = $pci_to_driver{$pci} = $driver;
				}
			}
		}
	}
	return $hostid;
}

sub vol_to_hostid {
	my($vol) = @_;
	my($root) = "/sys/block";

	if (-l "$root/$vol/device") {
		my($link) = readlink("$root/$vol/device");
		if ($link =~ /\/(host\d+)\//) {
			return $hostid_synonyms{$1} || $1;
		}
	}
	return;
}

my(%ioports_map);
my(%iomem_map);
my(%interrupts_map);
my(%irq_count);

sub grok_proc_ioports {
	my($file) = 'FILE';
	my($line);

	return unless ($os_type eq "linux");

	if (-e $proc_interrupts) {
		my($class) = 0;
		my($in) = "LSPCI";
		open($in, "$lspci -vn 2> /dev/null |") || warn("exec: $lspci: $!");
		while ($line = <$in>) {
			if ($line =~ /^([0-9a-f\.\:]+)\s+(Class\s+)?([0-9a-f]+):/i) {
				$class = "0x$3";
			}
			if ($line =~ /irq\s+(\d+)/i) {
				$irq_count{$1}++;
				$irq_count{"$1,$class"}++;
			}
		}
		close($in);

		open($file, "< $proc_interrupts") || warn("open: $proc_interrupts: $!");
		while ($line = <$file>) {
			if ($line =~ /^\s*(\d+).*\s+(\S+)$/) {
				$interrupts_map{$1} = lc($2) if ($irq_count{$1} && $irq_count{$1} == 1);
			}
		}
		close($file);
	}
	if (-e $proc_ioports) {
		open($file, "< $proc_ioports") || warn("open: $proc_ioports: $!");
		while ($line = <$file>) {
			if ($line =~ /^\s*([0-9a-f]+)-([0-9a-f]+)\s+:\s+(\w+)/) {
				$ioports_map{$1} = lc($3);
			}
		}
		close($file);
	}
	if (-e $proc_iomem) {
		open($file, "< $proc_iomem") || warn("open: $proc_iomem: $!");
		while ($line = <$file>) {
			if ($line =~ /^\s*([0-9a-f]+)-([0-9a-f]+)\s+:\s+(\w+)/) {
				$iomem_map{$1} = lc($3);
			}
		}
		close($file);
	}
}

my(%iomem_to_pci);

sub grok_lspci {
	my($in) = "LSPCI";
	my($line);
	my(%driver_count);
	my(%model);

	if ($os_type eq "linux") {
		open($in, "$lspci 2> /dev/null |") || warn("exec: $lspci: $!");
		while ($line = <$in>) {
			chomp($line);
			if ($line =~ /^([0-9a-f:\.]+) ([^:]+):(.*)/i) {
				my($pci, $class, $model) = ($1, $2, $3);
				if ($model =~ s/: Unknown device [0-9a-f]+$//i) {
					$model .= " $class";
				}
				$model{$pci} = clean_pci_model($model);
			}
		}
		close($in);

		open($in, "$lspci -vn 2> /dev/null |") || warn("exec: $lspci: $!");
		$line = <$in>;
		my($first) = 1;
		my($pci, $class, $chip, $card, $rev, $irq, $irq_class);
		while (1) {
			$debug_lspci .= $line;
			if (eof($in) || $line =~ /^\S+/) {
				if (!$first) {
					my($model) = $model{$pci} || 'Unknown';
					if ($chipsets{"$chip,$rev"}) {
						$other_devices{'Chipset'}{"$chip,$rev"} = $chipsets{"$chip,$rev"};
					} elsif ($chipsets{$chip}) {
						$other_devices{'Chipset'}{$chip} = $chipsets{$chip};
					}
					if ($pci_devices{'storage'}{"$card,$chip,$rev"}) {
						my($dev) = pci_to_hostid($pci) || $pci;
						$other_devices{'Disk-Control'}{$dev} = "$dev: " . $pci_devices{'storage'}{"$card,$chip,$rev"};
					} elsif ($pci_devices{'storage'}{"$card,$chip"}) {
						my($dev) = pci_to_hostid($pci) || $pci;
						$other_devices{'Disk-Control'}{$dev} = "$dev: " . $pci_devices{'storage'}{"$card,$chip"};
					} elsif ($pci_devices{'storage'}{"0x????????,$chip"}) {
						my($dev) = pci_to_hostid($pci) || $pci;
						$debug .= "Debug-Disk-Control:\t$dev: $card,$chip,$rev - $model\n";
						$other_devices{'Disk-Control'}{$dev} = "$dev: " . $pci_devices{'storage'}{"0x????????,$chip"};
					} elsif ($pci_devices{'network'}{"$card,$chip,$rev"}) {
						$other_devices{'Network'}{$pci} = "$pci: " . $pci_devices{'network'}{"$card,$chip,$rev"};
					} elsif ($pci_devices{'network'}{"0x????????,$chip,$rev"}) {
						$debug .= "Debug-Network:\t$pci: $card,$chip,$rev - $model\n";
						$other_devices{'Network'}{$pci} = "$pci: " . $pci_devices{'network'}{"0x????????,$chip,$rev"};
					} elsif ($pci_devices{'network'}{"0x????????,$chip,0x??"}) {
						$debug .= "Debug-Network:\t$pci: $card,$chip,$rev - $model\n";
						$other_devices{'Network'}{$pci} = "$pci: " . $pci_devices{'network'}{"0x????????,$chip,0x??"};
					} elsif ($pci_devices{'other'}{"$card,$chip,$rev"}) {
						$other_devices{$pci_devices{'other'}{"$card,$chip,$rev"}{type}}{"$card,$chip"} = $pci_devices{'other'}{"$card,$chip,$rev"}{model};
					} elsif ($pci_classes{$class}) {
						my($dev) = pci_to_hostid($pci) || $pci;
						$debug .= "Debug-" . $pci_classes{$class} . ":\t$dev: $card,$chip,$rev - $model\n";
						$other_devices{$pci_classes{$class}}{$dev} = "$dev: $model";
					}
					$pci_map{$pci} = "$card,$chip,$rev";
					$pci_all .= "<$card,$chip,$rev>";
					$sig_pci .= "$card,$chip,$rev\t$model\n";
				}

				if ($line =~ /^([0-9a-f\.\:]+)\s+(Class\s+)?([0-9a-f]+):\s+([0-9a-f]+):([0-9a-f]+)( \(rev ([0-9a-f]+)\))?/i) {
					($pci, $class, $chip, $card, $rev, $irq, $irq_class) = ($1, "0x$3", "0x$5$4", "0x00000000", $7 ? "0x$7" : "0x00", 0, 0);
					$pci =~ s/^0000://;
					$first = 0;
				}
			} elsif ($line =~ /^\sFlags:.*IRQ\s+(\d+)/i) {
				if ($irq_count{$1} && $irq_count{$1} == 1) {
					$irq = $1;
					$irq_to_pci{$irq} = $pci;
				}
				if ($irq_count{"$1,$class"} && $irq_count{"$1,$class"} == 1) {
					$irq_class = "$1,$class";
					$irq_to_pci{$irq_class} = $pci;
				}
			} elsif ($line =~ /^\s*I\/O ports at ([0-9a-f]+)/i) {
				my($addr) = $1;
				unless ($pci_to_driver{$pci}) {
					my($networks) = "e100|e1000|eepro100|tg3|bnx2|bcm5700|forcedeth|pcnet32|sky2";
					my($match) = "^(lsi|aic79xx|qla2300|cciss|sata_nv|sata_sil|3w|3ware|3w-9xxx|libata|(ide|ioc)\\d+|$networks)\$";
					my($driver);
					if ($ioports_map{$addr} && $ioports_map{$addr} =~ /$match/) {
						$driver = $ioports_map{$addr};
					} elsif ($irq && $interrupts_map{$irq} && $interrupts_map{$irq} =~ /$match/) {
						# use only on last resort, shared interrupts makes this often wrong
						$driver = $interrupts_map{$irq};
					}
					if ($driver) {
						$driver = $linux_driver_map{$driver} || $driver;
						if ($driver =~ /^((ide|ioc)\d+|$networks)$/) {
							$pci_to_driver{$pci} = $driver;
						} else {
							$driver_count{$driver}++;
							$pci_to_driver{$pci} = $driver;
							$pci_to_driver{$pci} .= "-" if ($driver =~ /\d$/);
							$pci_to_driver{$pci} .= ($driver_count{$driver} - 1);
						}
					}
				}
				if ($pci_to_driver{$pci}) {
					$irq_to_driver{$irq_class} = $pci_to_driver{$pci} if ($irq_class);
					$irq_to_driver{$irq} = $pci_to_driver{$pci} if ($irq);
				}
			} elsif ($line =~ /^\s*Memory at ([0-9a-f]+)/i) {
				my($addr) = $1;
				unless ($pci_to_driver{$pci}) {
					my($networks) = "e100|e1000|eepro100|tg3|bnx2|bcm5700|forcedeth|pcnet32|sky2";
					my($match) = "^(megaraid|megasas|aic7xxx|$networks)\$";
					my($driver);
					$iomem_to_pci{$addr} = $pci;
					if ($iomem_map{$addr} && $iomem_map{$addr} =~ /$match/) {
						$driver = $iomem_map{$addr};
					} elsif ($irq && $interrupts_map{$irq} && $interrupts_map{$irq} =~ /$match/) {
						# use only on last resort, shared interrupts makes this often wrong
						$driver = $interrupts_map{$irq};
					}
					if ($driver) {
						$driver = $linux_driver_map{$driver} || $driver;
						if ($driver =~ /^($networks)$/) {
							$pci_to_driver{$pci} = $driver;
						} else {
							$driver_count{$driver}++;
							$pci_to_driver{$pci} = $driver;
							$pci_to_driver{$pci} .= "-" if ($driver =~ /\d$/);
							$pci_to_driver{$pci} .= ($driver_count{$driver} - 1);
						}
					}
				}
				if ($pci_to_driver{$pci}) {
					$irq_to_driver{$irq_class} = $pci_to_driver{$pci} if ($irq_class);
					$irq_to_driver{$irq} = $pci_to_driver{$pci} if ($irq);
				}
			} elsif ($line =~ /^\s*Subsystem: ([0-9a-f]+):([0-9a-f]+)/i) {
				$card = "0x$2$1";
			}
			last if eof($in);
			$line = <$in>;
		}
		close($in);
	}
}

sub grok_pciconf {
	my($in) = "PCICONF";
	my($line);
	my($dev, $class, $card, $chip, $rev);
	my($model);
	my(%vendor, %model);
	my(@lines, $lines);

	system("which $pciconf > /dev/null 2>&1");
	return if ($?);

	open($in, "$pciconf -lv 2> /dev/null |") || warn("exec: $pciconf: $!");
	if (eof($in)) {
		close($in);
		open($in, "$pciconf -l 2> /dev/null |") || warn("exec: $pciconf: $!");
	}
	while ($line = <$in>) {
		$debug_pciconf .= $line;
		push(@lines, $line);
		if ($line =~ /^(\S+)\@.*class=(\S+).*card=(\S+).*chip=(\S+).*rev=(\S+)/) {
			($dev, $class, $card, $chip, $rev) = ($1, $2, $3, $4, $5);
		} elsif ($line =~ /^\s+vendor\s+=\s+'(.*)'$/) {
			$vendor{$dev} = clean_pci_model($1);
		} elsif ($line =~ /^\s+device\s+=\s+'(.*)'$/) {
			$model{$dev} = clean_pci_model($1);
		}
	}
	close($in);

	foreach $line (@lines) {
		if ($line =~ /^(\S+)\@.*class=(\S+).*card=(\S+).*chip=(\S+).*rev=(\S+)/) {
			($dev, $class, $card, $chip, $rev) = ($1, $2, $3, $4, $5);
			$class =~ s/..$//;
			if ($sysctl_dev{$dev} && $sysctl_dev{$dev}{'xmodel'}) {
				$model = $sysctl_dev{$dev}{'xmodel'};
			} elsif ($vendor{$dev} && $model{$dev}) {
				$model = "$vendor{$dev} $model{$dev}";
			} else {
				$model = 'unknown';
			}
			$pci_all .= "<$card,$chip,$rev>";
			$sig_pci .= "$card,$chip,$rev\t$model\n";
			if ($chipsets{"$chip,$rev"}) {
				$other_devices{'Chipset'}{"$chip,$rev"} = $chipsets{"$chip,$rev"};
			} elsif ($chipsets{$chip}) {
				$other_devices{'Chipset'}{$chip} = $chipsets{$chip};
			}
			if ($dev =~ /^(atapci|ciss|amr|ahc|ahd|asr|aac|aacch|mpt|sym|twe|twa|asr|mfi|ida)\d+/ ) {
				$need_cissutil++ if ($1 eq "ciss");
				$need_megarc++ if ($1 eq "amr");
				$need_mfiutil++ if ($1 eq "mfi");
				$need_mptutil++ if ($1 eq "mpt");
				$need_arcconf++ if ($1 eq "aac");
				$need_twcli++ if ($1 eq "twe" || $1 eq "twa");
				if (!$pci_devices{'storage'}{"$card,$chip,$rev"} && !$pci_devices{'storage'}{"$card,$chip"}) {
					$debug .= "Debug-Disk-Control:\t$dev: $model, $card,$chip,$rev\n";
					$card = "0x????????";
				}
				if ($pci_devices{'storage'}{"$card,$chip,$rev"}) {
					$other_devices{'Disk-Control'}{$dev} = "$dev: " . $pci_devices{'storage'}{"$card,$chip,$rev"};
				} elsif ($pci_devices{'storage'}{"$card,$chip"}) {
					$other_devices{'Disk-Control'}{$dev} = "$dev: " . $pci_devices{'storage'}{"$card,$chip"};
				} else {
					$other_devices{'Disk-Control'}{$dev} = "$dev: $model";
				}
			} elsif ($dev =~ /^($freebsd_net_devs)/) {
				$interfaces{$dev}{'model'} = $model unless ($model eq 'unknown' && $interfaces{$dev}{'model'});
				if (!$pci_devices{'network'}{"$card,$chip,$rev"}) {
					$debug .= "Debug-Network:\t$dev: ";
					$debug .= "$interfaces{$dev}{'model'}, " if ($interfaces{$dev}{'model'});
					$debug .= "$card,$chip,$rev\n";
					$card = "0x????????";
				}
				if (!$pci_devices{'network'}{"$card,$chip,$rev"}) {
					$rev = "0x??";
				}
				if ($pci_devices{'network'}{"$card,$chip,$rev"}) {
					$interfaces{$dev}{'model'} = $pci_devices{'network'}{"$card,$chip,$rev"};
				}
			} elsif ($pci_devices{'storage'}{"$card,$chip,$rev"}) {
				$other_devices{'Disk-Control'}{$dev} = "$dev: " . $pci_devices{'storage'}{"$card,$chip,$rev"};
			} elsif ($pci_devices{'storage'}{"$card,$chip"}) {
				$other_devices{'Disk-Control'}{$dev} = "$dev: " . $pci_devices{'storage'}{"$card,$chip"};
			} elsif ($pci_devices{'network'}{"$card,$chip,$rev"}) {
				$other_devices{'Network'}{$dev} = "$dev: " . $pci_devices{'network'}{"$card,$chip,$rev"};
			} elsif ($pci_devices{'other'}{"$card,$chip,$rev"}) {
				$other_devices{$pci_devices{'other'}{"$card,$chip,$rev"}{type}}{"$card,$chip"} = $pci_devices{'other'}{"$card,$chip,$rev"}{model};
			} elsif ($pci_classes{$class}) {
				$other_devices{$pci_classes{$class}}{$dev} = "$dev: $model";
				$debug .= "Debug-" . $pci_classes{$class} . ":\t$dev: $model, $card,$chip,$rev\n";
			}
		}
	}
}

sub hdparm {
	my($vol, $device, $id) = @_;
	my($result) = 0;
	my($in) = 'HDPARM';
	my($line);

	system("which hdparm > /dev/null 2>&1");
	return $result if ($?);

	# only use hdparm for /dev/hdx and /dev/sdx devices
	return $result if ($device !~ /^\/dev\/(sd|hd)[a-z]+$/);

	open($in, "hdparm -iI $device 2> /dev/null |") || warn("hdparm: $!");
	$debug_storage .= ">> hdparm -iI $device\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		$line =~ s/\s*$//;
		if ($line =~ /Model=([^,]+)/i || $line =~ /Model Number:\s*(.*)/i) {
			$props{drives}{$id}{model} = canon_drive($1);
			$result = 1;
		}
		if ($line =~ /SerialNo=([^,]+)/i || $line =~ /Serial Number:\s*(.*)/i) {
			$props{drives}{$id}{serial} = $1;
		}
		if ($line =~ /FwRev=([^,]+)/i || $line =~ /Firmware Revision:\s*(.*)/i) {
			$props{drives}{$id}{firmware} = $1;
		}
		if ($line =~ /^\s*Used: (\S+)/) {
			$props{drives}{$id}{interface} = parse_protocol($1);
		}
		if ($line =~ /^\s*Likely used: (\d+)/) {
			$props{drives}{$id}{interface} = parse_protocol("ATA-$1");
		}
		if ($line =~ /LBAsects=(((\d+)))/i || $line =~ /LBA(48)?\s*user addressable sectors( =|:)\s*(\d+)/i) {
			$props{drives}{$id}{size} = $3 * 512;
		}
		if ($line =~ /BuffSize=([^,]+)/i)  {
			$props{drives}{$id}{cache_size} = parse_bytes($1, 1024);
		}
		if ($line =~ /WriteCache=([^,]+)/i) {
			$props{drives}{$id}{wcache_enable} = parse_enable($1);
		}
		if ($line =~ /^\s*(\*?)\s*Write cache\s*$/i) {
			$props{drives}{$id}{wcache_support} = 1;
			$props{drives}{$id}{wcache_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /^\s*(\*?)\s*Look-ahead\s*$/i) {
			$props{drives}{$id}{readahead_support} = 1;
			$props{drives}{$id}{readahead_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /^\s*(\*?)\s*Device-initiated interface power management\s*$/i) {
			$props{drives}{$id}{dipm_support} = 1;
			$props{drives}{$id}{dipm_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /^\s*(\*?)\s*Host-initiated interface power management\s*$/i) {
			$props{drives}{$id}{hipm_support} = 1;
			$props{drives}{$id}{hipm_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /^\s*(\*?)\s*Native Command Queueing \(NCQ\)\s*$/i) {
			$props{drives}{$id}{ncq_support} = 1;
			$props{drives}{$id}{ncq_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /Queue depth: (\d+)/i) {
			$props{drives}{$id}{ncq_depth} = $1;
		}
		if ($line =~ /^\s*(\*?)\s*SMART feature set\s*$/i) {
			$props{drives}{$id}{smart_support} = 1;
			$props{drives}{$id}{smart_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /^\s*(\*?)\s*Automatic Acoustic Management feature set\s*$/i) {
			$props{drives}{$id}{aam_support} = 1;
			$props{drives}{$id}{aam_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /Recommended acoustic management value: (\d+), current value: (\d+)/i) {
			$props{drives}{$id}{aam_value} = $2;
		}
		if ($line =~ /^\s*(\*?)\s*Power Management feature set\s*$/i) {
			$props{drives}{$id}{pm_support} = 1;
			$props{drives}{$id}{pm_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /^\s*(\*?)\s*Advanced Power Management feature set\s*$/i) {
			$props{drives}{$id}{apm_support} = 1;
			$props{drives}{$id}{apm_enable} = parse_enable($1, '\*', '');
		}
		if ($line =~ /Advanced power management level: (\d+)/i) {
			$props{drives}{$id}{apm_value} = $1;
		}
		if ($line =~ /^\s*(\*)\s*(SATA-I signaling)/i) {
			$props{drives}{$id}{interface} = parse_protocol($2);
		}
		if ($line =~ /^\s*(\*)\s*(SATA-II signaling)/i) {
			$props{drives}{$id}{interface} = parse_protocol($2);
		}
		if ($line =~ /Logical Unit WWN Device Identifier:\s*(\S+)/i) {
			$props{drives}{$id}{wwn} = "0x$1";
		}
	}
	close($in);
	return $result;
}

sub smartctl {
	my($vol, $device, $vendor, $id) = @_;
	my($result) = 0;
	my($line);
	my($in) = 'SMARTCTL';
	my($dev_opt) = ($vendor =~ /^ATA\s*$/) ? '-d ata' : '';

	system("which smartctl > /dev/null 2>&1");
	return $result if ($?);

	# orginally used "smartctl -a" here.  this causes some PE2950 RHEL boxes to hang/crash
	# e.g. test164.data.corp.sp1.yahoo.com, kr2-ymon-001.ysm.kr2.yahoo.com, s200.del.re1.yahoo.net
	# need to be very careful here.  "smartctl -i" does appear to be safe for all types, but
	# for now we'll be conservative and just use this for ATA drives, which is why we
	# starting using this in the first place.  before we use for anything else, need to
	# be careful.
	#
	# 20080824 - enabling for all types now.  as stated earlier does appear safe

	open($in, "smartctl -i $dev_opt $device 2> /dev/null |") || warn "exec smartctl: $!";
	$debug_storage .= ">> smartctl -i $dev_opt $device\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /^Device Model:\s*(.*)/) {
			$props{drives}{$id}{model} = canon_drive($1);
		} elsif ($line =~ /^Serial Number:\s*(.*)/) {
			$props{drives}{$id}{serial} = $1;
		} elsif ($line =~ /^Firmware Version:\s*(.*)/) {
			$props{drives}{$id}{firmware} = $1;
		} elsif ($line =~ /^User Capacity:\s*([\d,]+)\s+bytes/) {
			my($size) = $1;
			$size =~ s/,//g;
			$props{drives}{$id}{size} = int($size);
		} elsif ($line =~ /^SMART support is: Available/) {
			$props{drives}{$id}{smart_support} = 1;
		} elsif ($line =~ /^SMART support is: Enabled/) {
			$props{drives}{$id}{smart_enable} = 1;
		}
		$result = 1;
	}
	close($in);
	return $result;
}

# new lshal - 0.5.11rc2 on fedora9
#	- info.bus seems to have gone away, only used now for usb?
#	- linux.sysfs_path_device is gone

sub grok_lshal {
	my($in) = "LSHAL";
	my($line);
	my(%hal, $udi);
	my(%cleanup);

	open($in, "$lshal 2> /dev/null |") || warn("exec: $lshal: $!");
	while ($line = <$in>) {
		$debug_lshal .= $line;
		chomp($line);
		if ($line =~ /^udi = \'([^\']*)\'/) {
			$udi = $1;
		} elsif ($line =~ /^\s+(\S+)\s+=\s+\'([^\']*)\'/) {
			$hal{$udi}{$1} = $2;
		} elsif ($line =~ /^\s+(\S+)\s+=\s+(\S+)/) {
			$hal{$udi}{$1} = $2;
		}
	}
	close($in);
	foreach $udi (keys %hal) {
		if ($hal{$udi}{'pci.product_id'}) {
			my($num) = substr($hal{$udi}{'linux.sysfs_path'}, -7);
			my($id) = $pci_map{$num};
			if ($hal{$udi}{'net.interface'}) {
				my($model) = $other_devices{'Network'}{$num};
				$model =~ s/^$num: //;
				$interfaces{$hal{$udi}{'net.interface'}}{'model'} = $model;
				$cleanup{'Network'}{$num}++;
				$success{'grok_lshal_network'}++;
			}
			$need_arcconf++ if ($hal{$udi}{'pci.vendor'} && $hal{$udi}{'pci.vendor'} eq 'Adaptec' && $hal{$udi}{'pci.product'} =~ /AAC/);
		} elsif (($hal{$udi}{'info.subsystem'} && $hal{$udi}{'info.subsystem'} eq 'ide') || ($hal{$udi}{'info.bus'} && $hal{$udi}{'info.bus'} eq 'ide_host')) {
			my($num, $dev);
			if ($hal{$udi}{'info.subsystem'} && $hal{$udi}{'info.subsystem'} eq 'ide') {
				# new lshal - 0.5.11rc2
				($num, $dev) = ($hal{$udi}{'linux.sysfs_path'} =~ /(.{7})\/(ide\d+)\//);
			} else {
				# legacy lshal
				($num, $dev) = ($hal{$udi}{'ide_host.linux.sysfs_path'} =~ /(.{7})\/([^\/]+)$/);
			}
			if ($num =~ /^[0-9a-f:\.]+$/) {
				# guard against case - linux.sysfs_path = '/sys/devices/ide0/0.0'
				# which happens with lshal 0.5.11rc2 and ide == compatible in bios
				my($model) = $other_devices{'Disk-Control'}{$num};
				$dev =~ s/^.*\///;
				$model =~ s/^$num: //;
				$other_devices{'Disk-Control'}{$dev} = "$dev: $model";
				$cleanup{'Disk-Control'}{$num}++;
			}
		} elsif (($hal{$udi}{'info.subsystem'} && $hal{$udi}{'info.subsystem'} eq 'scsi_host') || ($hal{$udi}{'info.bus'} && $hal{$udi}{'info.bus'} eq 'scsi_host')) {
			my($num, $dev);
			if ($hal{$udi}{'info.subsystem'} && $hal{$udi}{'info.subsystem'} eq 'scsi_host') {
				# new lshal - 0.5.11rc2
				($num, $dev) = ($hal{$udi}{'linux.sysfs_path'} =~ /(.{7})\/([^\/]+)$/);
			} else {
				# legacy lshal
				($num, $dev) = ($hal{$udi}{'linux.sysfs_path_device'} =~ /(.{7})\/([^\/]+)$/);
			}
			unless ($hal{$udi}{'info.parent'} =~ /usb/) {
				# check for synonyms for this hostid
				$dev = pci_to_hostid($num) || $dev;
				unless (defined $other_devices{'Disk-Control'}{$dev}) {
					my($model) = $other_devices{'Disk-Control'}{$num};
					$dev =~ s/^.*\///;
					$model =~ s/^$num: //;
					$other_devices{'Disk-Control'}{$dev} = "$dev: $model";
					$cleanup{'Disk-Control'}{$num}++;
				}
			}
		} elsif ($hal{$udi}{'storage.drive_type'} && $hal{$udi}{'storage.drive_type'} eq 'disk') {
			my($devpath) = $hal{$udi}{'block.device'};
			my($vol) = $devpath;
			$vol =~ s/.*\///;
			$volumes{$vol}++;

			my($firmware, $serial, $model);
			$firmware = $hal{$udi}{'storage.firmware_version'};
			$serial = $hal{$udi}{'storage.serial'};
			$model = canon_drive($hal{$udi}{'storage.model'});
			if ($model =~ /(raid\d+)/i) {
				$props{$vol}{raid} = $1;
			} else {
				$props{$vol}{raid} = "JBOD";
			}

			if ($hal{$udi}{'storage.bus'} eq 'ide') {
				my($node) = $udi;
				my($ms) = 'unknown';
				while ($hal{$node}{'info.parent'}) {
					$node = $hal{$node}{'info.parent'};
					if ($hal{$node}{'info.product'}) {
						if ($hal{$node}{'info.product'} =~ /IDE device \((slave|master)\)/) {
							$ms = $1;
						}
					}
					if ($hal{$node}{'linux.subsystem'} && $hal{$node}{'linux.subsystem'} eq 'ide') {
						# new lshal - 0.5.11rc2 on fedora9
						my($dev) = ($hal{$node}{'linux.sysfs_path'} =~ /\/(ide\d+)\//);
						$props{$vol}{'device'} = $dev;
						$props{$vol}{'ms'} = $ms if ($ms);
						$success{'grok_lshal_ide'}++;
						$drives{$vol}{$drive_id}++;
						$props{drives}{$drive_id}{model} = $model;
						$props{drives}{$drive_id}{volume} = $vol;
						$props{drives}{$drive_id}{firmware} = $firmware;
						$props{drives}{$drive_id}{serial} = $serial;
						smartctl($vol, $devpath, $hal{$udi}{'storage.vendor'}, $drive_id);
						hdparm($vol, $devpath, $drive_id);
						$drive_id++;
						$ndrives++;
						last;
					} elsif ($hal{$node}{'info.bus'} eq 'ide_host') {
						# legacy lshal
						my($dev) = ($hal{$node}{'ide_host.linux.sysfs_path'} =~ /\/([^\/]+)$/);
						$props{$vol}{'device'} = $dev;
						$props{$vol}{'ms'} = $ms if ($ms);
						$success{'grok_lshal_ide'}++;
						$drives{$vol}{$drive_id}++;
						$props{drives}{$drive_id}{model} = $model;
						$props{drives}{$drive_id}{volume} = $vol;
						$props{drives}{$drive_id}{firmware} = $firmware;
						$props{drives}{$drive_id}{serial} = $serial;
						smartctl($vol, $devpath, $hal{$udi}{'storage.vendor'}, $drive_id);
						hdparm($vol, $devpath, $drive_id);
						$drive_id++;
						$ndrives++;
						last;
					}
				}
			} elsif ($hal{$udi}{'storage.bus'} eq 'scsi') {
				# good test case - feedb-beta.local.mud

				my($node) = $udi;
				my($extmodel) = $hal{$node}{'storage.vendor'} . ' ' . $hal{$node}{'storage.model'};
				my($scsi_id, $channel, $id, $lun) = (0, 0, 0, 0);

				$need_megacli++ if ($extmodel =~ /$perc_names/);
				$need_megarc++ if ($extmodel =~ /MegaRAID/);
				$need_twcli++ if ($extmodel =~ /(3ware|AMCC)/i);

				if ($hal{$node}{'info.parent'} =~ /scsi_(\d+)_(\d+)_(\d+)_(\d+)/) {
					($scsi_id, $channel, $id, $lun) = ($1, $2, $3, $4);
				} elsif ($hal{$node}{'info.parent'} =~ /scsi_host_scsi_device/) {
					my($parent) = $hal{$node}{'info.parent'};
					($scsi_id, $channel, $id, $lun) = ($hal{$parent}{'scsi.host'}, $hal{$parent}{'scsi.bus'}, $hal{$parent}{'scsi.target'}, $hal{$parent}{'scsi.lun'});
				}

				if ($extmodel =~ /MegaRAID/) {
					my($logical) = sprintf("scsi%d-%d-%d-%d", $scsi_id, 0, $id, 0);
					$logical_to_os{'megaraid'}{$logical} = $vol;
				} elsif ($extmodel =~ /$perc_names/) {
					my($logical) = sprintf("scsi%d-%d-%d-%d", $scsi_id, 0, $id, 0);
					$logical_to_os{'perc'}{$logical} = $vol;
				} elsif ($extmodel =~ /(3ware|AMCC)/i) {
					my($logical) = sprintf("c%d-u%d", $scsi_id, $id);
					$logical_to_os{'tw'}{$logical} = $vol;
				} else {
					$logical_to_os{'other'}{$hal{$node}{'storage.model'}} = $vol;
				}

				while ($hal{$node}{'info.parent'}) {
					if (($hal{$node}{'info.bus'} && $hal{$node}{'info.bus'} eq 'scsi_host') ||
					    ($hal{$node}{'info.category'} && $hal{$node}{'info.category'} eq 'scsi_host')) {
						my($dev) = ($hal{$node}{'linux.sysfs_path'} =~ /\/([^\/]+)$/);
						$props{$vol}{'device'} = $dev;
						$success{'grok_lshal_scsi'}++;
						$drives{$vol}{$drive_id}++;
						$props{drives}{$drive_id}{model} = $model;
						$props{drives}{$drive_id}{volume} = $vol;
						smartctl($vol, $devpath, $hal{$udi}{'storage.vendor'}, $drive_id);
						hdparm($vol, $devpath, $drive_id);
						$drive_id++;
						$ndrives++;
						last;
					}
					$node = $hal{$node}{'info.parent'};
				}
			}
		}
	}

	my($type, $num);
	foreach $type (keys %cleanup) {
		foreach $num (keys %{$cleanup{$type}}) {
			delete $other_devices{$type}{$num};
		}
	}
}

my(%lvm_logical_to_physical);

sub lvdisplay {
	my($in) = 'LVM';
	my($line);
	my($result) = 0;
	my($lv) = "unknown";
	my($lv_count) = 0;
	my($lv_size) = 0;
	my($vol) = "unknown";
	my($model) = "unknown";
	my($sep) = '';
	my($looking) = 0;

	system("which lvdisplay > /dev/null 2>&1");
	return $result if ($?);
	
	$debug_storage .= ">> lvm\n";

	open($in, "lvdisplay -m 2> /dev/null |") || warn "exec lvdisplay: $!";
	$debug_storage .= ">>> lvdisply -m\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		$result = 1;
		if ($line =~ /^Synopsis:/) {
			$result = 0;
			last;
		}
		if ($line =~ /LV Name\s+(\S+)/) {
			$lv = $1;
			$lv =~ s,^/dev/,,;
			$lv =~ s,/,-,g;
			$vol = "dm$lv_count";
			$lv_count++;
		} elsif ($line =~ /LV Size\s+(.*)/) {
			$lv_size = parse_bytes($1, 1000);
		} elsif ($line =~ /Type\s+striped/) {
			$ndrives++;
			$volumes{$vol}++;
			$props{$vol}{bytes} = $lv_size;
			$props{$vol}{raid} = "RAID 1";
			$sep = "partitions: ";
			$model = '';
			$looking++;
		} elsif ($line =~ /Stripe size\s+(.*)/) {
			$props{$vol}{stripe} = parse_bytes($1, 1024);
		} elsif ($line =~ /Physical volume\s+(\S+)/) {
			my($dev) = $1;
			$lvm_logical_to_physical{$lv} = $dev;
			$dev =~ s,^/dev/,,;
			$model .= "$sep$dev";
			$sep = ", ";
		} elsif ($line =~ /^\s*$/) {
			if ($looking) {
				$drives{$vol}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{volume} = $vol;
				$drive_id++;
				$lvm_logical_to_physical{$lv} = $vol;
			}
			$looking = 0;
		}
	}	
	close($in);

	if (!$result) {
		my($volumes);
		open($in, "lvscan 2> /dev/null |") || warn "exec lvscan: $!";
		$debug_storage .= ">>> lvscan\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /ACTIVE\s*\"(\S+)\"/) {
				$volumes .= " $1";
			}
		}
		close($in);

		if ($volumes) {
			my($next) = 0;
			open($in, "lvdisplay -v $volumes 2> /dev/null |") || warn "exec lvdisplay: $!";
			$debug_storage .= ">>> lvdisplay -v $volumes\n";
			while ($line = <$in>) {
				$debug_storage .= $line;
				chomp($line);
				if ($line =~ /LV Name\s+(\S+)/) {
					$lv = $1;
				}
				if ($next) {
					if ($line =~ /^\s+(\S+)/) {
						$lvm_logical_to_physical{$lv} = $1;
					}
					$next = 0;
				}
				if ($line =~ /^\s*PV Name/) {
					$next = 1;
				}
			}
			close($in);
		}
	}

	return $result;
}

sub df {
	my($in) = 'DF';
	my($line);
	my($result) = 0;

	system("which df > /dev/null 2>&1");
	return $result if ($?);

	if ($os_type eq "linux") {
		open($in, "df -P -k -l 2> /dev/null |") || warn "exec df: $!";
		$debug_storage .= ">> df -P -k -l\n";
	} else {
		open($in, "df -k -t ufs 2> /dev/null |") || warn "exec df: $!";
		$debug_storage .= ">> df -k -t ufs\n";
	}

	$line = <$in>;
	$debug_storage .= $line;
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		$result++;
		my($vol, $size, $used, $avail, @rest) = split(' ', $line);
		next unless ($vol =~ m,^/dev/,);
		if ($vol =~ m,^/dev/mapper/(.*),) {
			$vol = $lvm_logical_to_physical{$1} || $vol;
		}
		$vol = $lvm_logical_to_physical{$vol} || $vol;
		$vol =~ s,^/dev/,,;
		if ($os_type eq "linux") {
			if ($vol =~ m,^[hs]d,) {
				$vol =~ s,\d+$,,;
			} else {
				$vol =~ s,p\d+$,,;
			}
		} else {
			$vol =~ s,s\d+[a-e]$,,;		# e.g. /dev/ad0s1e
			$vol =~ s,(\d+)[a-e]$,$1,;	# e.g. /dev/ad0e
			$vol =~ s,p\d+$,,;		# e.g. /dev/mfid0p4
			$need_ccdconfig++ if ($vol =~ /^ccd/);
		}
		$props{$vol}{df}{size} += $size * 1024;
		$props{$vol}{df}{used} += $used;
		$props{$vol}{df}{avail} += $avail;
	}
	close($in);

	return $result;
}

sub other_devices {
	my($line);
	my($type, $device);
	my(@chipsets);
	my($out, @out);
	foreach $type (sort keys %other_devices) {
		foreach $device (sort cmp_devs keys %{$other_devices{$type}}) {
			if ($type eq 'Chipset') {
				push(@chipsets, $other_devices{$type}{$device});
				next;
			}

			$out = '';
			$out .= "$type:\t";
			$out .= "\t" if (length($type) < 7);

			my($description) = $other_devices{$type}{$device};
			my($dev) = $device;
			if ($hostid_to_pci{$dev}) {
				$dev = $hostid_to_pci{$dev};
			}
			if ($dev =~ /^[0-9a-f:\.]+$/) {
				my($new) = $pci_to_driver{$dev} || pci_to_hostid($dev);
				if ($new) {
					if ($type eq 'Network') {
						$description =~ s/^$device/$device ($new)/;
					} else {
						$description =~ s/^$device/$new/;
						$dev = $new;
					}
				}
			}
			$out .= $description;

			if ($type eq 'Disk-Control') {
				if ($props{$dev}{'package'}) {
					if ($props{$dev}{'firmware'}) {
						$out .= ", Package $props{$dev}{'package'}";
					} else {
						$out .= ", FW $props{$dev}{'package'}";
					}
				}
				if ($props{$dev}{'firmware'}) {
					$out .= ", FW $props{$dev}{'firmware'}";
				}
				if ($props{$dev}{'bios'}) {
					$out .= ", BIOS $props{$dev}{'bios'}";
				}
				if ($props{$dev}{'cache-status'} || $props{$dev}{'cache-size'}) {
					$out .= ", Cache";
					$out .= " $props{$dev}{'cache-status'}" if $props{$dev}{'cache-status'};
					if ($props{$dev}{'cache-size'}) {
						$out .= ' ' . print_bytes($props{$dev}{'cache-size'}, 1024, 1);
					} elsif (defined $props{$dev}{'cache-read'}) {
						$out .= ' ' . print_bytes($props{$dev}{'cache-read'}, 1024, 1);
						$out .= '/' . print_bytes($props{$dev}{'cache-write'}, 1024, 1);
						$out .= ' (R/W)';
					}
				}
				if ($props{$dev}{'bbu'}) {
					$out .= ", BBU";
				}
			}
			push(@out, $out);
		}
	}
	foreach $out (sort cmp_other_devices @out) {
		$output .= $out . "\n";
	}
	if (@chipsets) {
		my($chipset, $pretty, $vendor, $last);
		foreach $chipset (sort @chipsets) {
			$chipset =~ s/^(NB|SB|IB): //;
			($vendor) = ($chipset =~ /^(\S+)\s+/);
			if ($last && $vendor eq $last) {
				$chipset =~ s/^$vendor\s+//;
				$pretty .= ", " if $pretty;
			} else {
				$pretty .= "; " if $pretty;
			}
			$pretty .= $chipset;
			$last = $vendor;
		}
		$output .= "Chipset:\t$pretty\n";
	} elsif ($system_model_index && defined $system_models[$system_model_index]{chipset}) {
		$output .= "Chipset:\t$system_models[$system_model_index]{chipset}\n";
	} else {
		$debug .= "Debug-Chipset:\tUnknown\n";
	}
}

sub cmp_other_devices {
	my($atype, $anum) = ($a =~ /^[^:]*:\s*([^:]*?)(\d*)/);
	my($btype, $bnum) = ($b =~ /^[^:]*:\s*([^:]*?)(\d*)/);
	if ($anum && $bnum) {
		return ($atype cmp $btype || $anum <=> $bnum);
	} else {
		return ($atype cmp $btype)
	}
}

sub dmesg_disks {
	my($line);
	my(%disks, $disk);
	my($result, $sep);
	foreach $line (split(/\n/, $dmesg_buf)) {
		if ($line =~ /^((mfid|twed|idad|da|ad|wd|sd)\d+):/) {
			$disks{$1}++;
		}
	}
	$sep = "";
	$result = "";
	foreach $disk (sort { $a cmp $b } keys %disks) {
		$result .= "$sep$disk";
		$sep = " ";
	}
	return $result;
}

my(%dmesg);

sub grok_dmesg {
	my($line);
	foreach $line (split(/\n/, $dmesg_buf)) {
		chomp($line);
		$line =~ s/\(R\)//g;
		if ($line =~ /^CPU:\s+([^\(]*).*\(([\d\.]+-MHz)/i) {
			my($family, $speed) = ($1, $2);
			$family =~ s/\bCPU.*//;
			$family =~ s/\s+$//;
			$speed =~ s/-//g;
			$dmesg{proc}{family} = $family;
			$dmesg{proc}{speed} = $speed;
			if ($family =~ /amd/i) {
				$dmesg{proc}{vendor} = "AMD";
			} elsif ($family =~ /intel/i || $family =~ /xeon/i || $family =~ /pentium/i) {
				$dmesg{proc}{vendor} = "Intel";
			}
		} elsif ($line =~ /^\s*Features\s*=\s*0x([0-9a-f]+)/i) {
			$dmesg{proc}{features} = $1;
		} elsif ($line =~ /Origin\s*=\s*"(\S+)".*Id\s*=\s*0x([0-9a-f]+).*Stepping\s*=\s*(\S+)/i) {
			$dmesg{proc}{origin} = $1;
			$dmesg{proc}{cpuid} = $2;
			$dmesg{proc}{stepping} = $3;
		} elsif ($line =~ /^((atapci|mfi|mpt|ahc|ahd|asr|aac|aacch|sym|ida|twe|twa)\d+): <([^>]*)>.*device/) {
			$need_mfiutil++ if ($2 eq "mfi");
			$need_mptutil++ if ($2 eq "mpt");
			$need_twcli++ if ($2 eq "twe" || $2 eq "twa");
			$other_devices{"Disk-Control"}{$1} = "$1: $3";
			$other_devices{"Disk-Control"}{$1} =~ s/\s+controller\b.*//i;
		} elsif ($line =~ /^(($freebsd_net_devs)\d+): <([^>]*)>.*device/) {
			$interfaces{$1}{'model'} = $3;
			$interfaces{$1}{'model'} =~ s/\s*(Ethernet|Network|,).*//;
		} elsif ($line =~ /^ciss\d+/) {
			$need_cissutil++;
			if ($line =~ /^(ciss\d+): <([^>]*)>.*port.*device/) {
				$other_devices{"Disk-Control"}{$1} = "$1: $2";
			} elsif ($line =~ /^(ciss\d+):\s+firmware\s+([\d\.]+)/) {
				$props{$1}{'firmware'} = $2;
			}
		} elsif ($line =~ /^amr\d+/) {
			$need_megarc++;
			if ($line =~ /^(amr\d+): <([^>]*)>.*Firmware\s+([^,]+),.*BIOS\s+([^,]+)/) {
				$other_devices{"Disk-Control"}{$1} = "$1: $2";
				$props{$1}{'firmware'} = $3;
				$props{$1}{'bios'} = $4;
			}
		}
	}
}

my(%procs);

sub parse_proc {
	my($in, $section, $handle) = @_;
	my($line);
	my($socket) = ++$procs{sockets};
	my($ignore) = 1;
	my($key);

	while ($line = <$in>) {
		$debug_dmidecode .= $line;
		chomp($line);
		$line =~ s/\s+$//;
		last if ($line =~ /^(handle.*)?$/i);
		$line =~ s/$clean_smbios_regex//i;
		if ($handle) {
			if ($line =~ /^\t(\S[^:]+):\s*(.*)/) {
				$key = $1;
				$dmi{$handle}{'key'}{$1} = $2;
			} elsif ($line =~ /^\t\t(.*)/) {
				$dmi{$handle}{'key'}{$key} .= "\t$1";
			}
		}
		if ($line =~ /^\s*Type:\s+(CPU|Central Processor)/i) {
			$ignore = 0;
		} elsif ($line =~ /^\s*Socket Designation:\s+(Slot-2)/i) {
			$ignore = 0 
		} elsif ($line =~ /^\s*current (cpu )?speed:\s+(.*)$/i) {
			my($speed) = $2;
			$speed =~ s/\s+//g;
			$procs{$socket}{speed} = ($speed eq "0MHz") ? 0 : $speed;
		} elsif ($line =~ /^\s*(external )?clock:\s+(.*)$/i) {
			my($clock) = $2;
			$clock =~ s/\s+//g;
			$procs{$socket}{clock} = ($clock eq "0MHz") ? 0 : $clock;
		} elsif ($line =~ /^\s*family:\s+(.*)$/i) {
			$procs{$socket}{family} = $1;
		} elsif ($line =~ /^\s*manufacturer:\s+(.*)$/i) {
			$procs{$socket}{vendor} = $1;
		} elsif ($line =~ /^\s*cpu present:\s+(.*)$/i) {
			$procs{$socket}{status} = ($1 =~ /yes/i);
		} elsif ($line =~ /^\s*id:\s+(.*)$/i) {
			my($id) = lc($1);
			if ($id =~ / /) {
				# from dmidecode
				$id = join('', reverse(split(' ', $id))) if ($id =~ / /);
			}
			$id =~ s/^0x//;
			$procs{$socket}{cpuid} = substr($id, -8);
			$procs{$socket}{features} = substr($id, 0 ,8);
		} elsif ($line =~ /^\s*status:\s+(.*)$/i) {
			$procs{$socket}{status} = ($1 =~ /\bpopulated\b/i);
		} elsif ($line =~ /^\s*(L\d+ Cache Handle):\s+(.*)$/) {
			$procs{$socket}{$1} = $2;
		}
	}
	if ($ignore) {
		$procs{sockets}--;
		delete $procs{$socket};
	}
}

$smbios{'Memory'}{'Count'} = 0;

sub parse_memory {
	my($in, $section, $sub, $handle) = @_;
	my($count) = $smbios{$section}{'Count'} + 1;
	my($line);
	my($ignore) = 0;
	my($speed) = 0;
	my($size) = 0;
	my($manufacturer) = 0;
	my($part) = 0;
	my($type) = 0;
	my($key);

	if ($sub eq "Device") {
		# override previous Module entries
		if (!$smbios{$section}{$sub} && $count > 1) {
			while ($count) {
				delete $smbios{$section}{$count};
				$count--;
			}
			$count = 1;
		}
		$smbios{$section}{$sub}++;
	} elsif ($smbios{$section}{'Device'}) {
		# ignore if we've already parsed Device entries
		$ignore = 1;
	}

	while ($line = <$in>) {
		$debug_dmidecode .= $line;
		chomp($line);
		$line =~ s/\s+$//;
		last if ($line =~ /^(handle.*)?$/i);
		$line =~ s/$clean_smbios_regex//i;
		if ($handle) {
			if ($line =~ /^\t(\S[^:]+):\s*(.*)/) {
				$key = $1;
				$dmi{$handle}{'key'}{$1} = $2;
			} elsif ($line =~ /^\t\t(.*)/) {
				$dmi{$handle}{'key'}{$key} .= "\t$1";
			}
		}
		if ($line =~ /^\s*(current )?speed:\s+((\d+).*)/i) {
			$speed = ($3 == 0 || $3 > 10000) ? 0 : $2;
			$speed =~ s/\([\d\.]+ ns\)//;
			$speed =~ s/\s+//g;
		} elsif ($line =~ /^\s*(installed )?size:\s+([^\(]*)/i) {
			$size = $2;
		} elsif ($line =~ /^\s*(memory\s+)?type:\s+(.*)$/i) {
			$type = $2;
			$ignore = 1 if ($type eq "Flash");
		} elsif ($line =~ /^\s*manufacturer:\s+(.*)$/i) {
			$manufacturer = $1;
		} elsif ($line =~ /^\s*part\s+number:\s+(.*)$/i) {
			$part = canon_memory($1);
		} elsif ($line =~ /^\s*form factor: (chip|tsop)/i) {
			$ignore = 1;
		}
	}
	if (!$ignore) {
		$smbios{$section}{'Count'} = $count;
		$smbios{$section}{$count}{'Speed'} = $speed;
		$smbios{$section}{$count}{'Size'} = $size;
		$smbios{$section}{$count}{'Type'} = $type;
		$smbios{$section}{$count}{'Manufacturer'} = $manufacturer;
		$smbios{$section}{$count}{'Part Number'} = $part;
	}
}

sub parse_bios {
	my($in, $section, $handle) = @_;
	my($count) = ++$smbios{$section}{'Count'};
	my($line);
	my($key);

	while ($line = <$in>) {
		$debug_dmidecode .= $line;
		chomp($line);
		$line =~ s/\s+$//;
		last if ($line =~ /^(handle.*)?$/i);
		$line =~ s/$clean_smbios_regex//i;
		if ($handle) {
			if ($line =~ /^\t(\S[^:]+):\s*(.*)/) {
				$key = $1;
				$dmi{$handle}{'key'}{$1} = $2;
			} elsif ($line =~ /^\t\t(.*)/) {
				$dmi{$handle}{'key'}{$key} .= "\t$1";
			}
		}
		if ($line =~ /^\s*vendor:\s+(\S+.*)$/i) {
			$smbios{$section}{$count}{'Vendor'} = $1;
		} elsif ($line =~ /^\s*version:\s+(\S+.*)$/i) {
			my($version) = $1;
			$version =~ s/BIOS Version(:)?\s+//i;
			$version =~ s/\-\[(.*)\]\-/$1/;
			$version =~ s/^'(.*)'$/$1/;
			$version = clean_white($version);
			if ($version =~ s/\s+(BIOS )?Date(:)?\s+(\S+.*)//i) {
				$smbios{$section}{$count}{'Release Date'} = $3;
			} 
			$smbios{$section}{$count}{'Version'} = $version;
		} elsif ($line =~ /^\s*release date:\s+(\S+.*)$/i) {
			$smbios{$section}{$count}{'Release Date'} = $1;
		}
	}
}


sub parse_system {
	my($in, $section, $handle) = @_;
	my($count) = ++$smbios{$section}{'Count'};
	my($line);
	my($key);

	while ($line = <$in>) {
		$debug_dmidecode .= $line;
		chomp($line);
		$line =~ s/\s+$//;
		last if ($line =~ /^(handle.*)?$/i);
		$line =~ s/$clean_smbios_regex//i;
		if ($handle) {
			if ($line =~ /^\t(\S[^:]+):\s*(.*)/) {
				$key = $1;
				$dmi{$handle}{'key'}{$1} = $2;
			} elsif ($line =~ /^\t\t(.*)/) {
				$dmi{$handle}{'key'}{$key} .= "\t$1";
			}
		}
		if ($line =~ /^\s*product( name)?:\s+(.*)$/i) {
			$smbios{$section}{$count}{'Product Name'} = $2;
		} elsif ($line =~ /^\s*manufacturer:\s+(.*)$/i) {
			$smbios{$section}{$count}{'Manufacturer'} = $1;
		}
	}
}

my(%interface_to_driver);

sub grok_network {
	my($in) = "IFCONFIG";
	my($line);
	my($int);

	open($in, "ifconfig -a |") || warn("exec: ifconfig: $!");
	$debug_network .= ">> ifconfig -a\n";

	if ($os_type eq "freebsd") {
		while ($line = <$in>) {
			$debug_network .= $line;
			chomp($line);
			if ($line =~ /^(\w+):/) {
				$int = $1;
			}
			if ($line =~ /^\s*media:\s*(Ethernet)?\s*(.*)/) {
				my($media) = $2;
				$media =~ s/status.*//;
				$media =~ s/autoselect//;
				$media =~ s/[\(\)]//g;
				$media =~ s/\s+/ /g;
				$media =~ s/^\s+//;
				$media =~ s/\s+$//;
				$interfaces{$int}{media} = $media;
			} elsif ($line =~ /^\s*ether\s*(\S*)/) {
				$interfaces{$int}{mac} = $1;
			}
			if ($line =~ /\bstatus:\s*(.*)/) {
				$interfaces{$int}{status} = $1;
			}
		}
	} elsif ($os_type eq "linux") {
		my($pci) = 0;
		my($debug_network_2) = '';
		while ($line = <$in>) {
			$debug_network .= $line;
			chomp($line);
			if ($line =~ /^(\w+)\s+Link.*HWaddr\s+(\S+)/) {
				$int = $1;
				$interfaces{$int}{mac} = lc($2);
				my($eth) = "ETHTOOL";
				my($eline);
				my($speed, $duplex) = ("", "");
				open($eth, "$ethtool $int 2> /dev/null |") || warn("exec: $ethtool: $!");
				$debug_network_2 .= ">> $ethtool $int\n";
				while ($eline = <$eth>) {
					$debug_network_2 .= $eline;
					chomp($eline);
					if ($eline =~ /Link detected:\s*(\S+)/) {
						# 2.6 kernels only?
# comment out for now 20080201, found a case (mc1020.mail.re1, op951002.inktomisearch.com) running rhel4u6 that
# shows link detected when there is no link.  ifconfig doesn't show RUNNING in this
# case, so maybe ifconfig is more accurate.  if this is still commented out some
# months from now, this should all be nuked.
#						$interfaces{$int}{status} = ($1 eq "yes") ? "active" : "no carrier";
# in order to keep output the same (i.e. no carrier when no link as root), set
# to no carrier as default and let ifconfig decide
						$interfaces{$int}{status} = "no carrier" unless ($interfaces{$int}{status} && $interfaces{$int}{status} eq "active");
					} elsif ($eline =~ /Speed:\s*(\S+)/) {
						$speed = $1;
					} elsif ($eline =~ /Duplex:\s*(\S+)/) {
						$duplex = lc($1);
					}
				}
				$interfaces{$int}{media} = $speed;
				$interfaces{$int}{media} .= " <$duplex-duplex>" if ($duplex);
				close($eth);

				my($driver) = $int;
				open($eth, "$ethtool -i $int 2> /dev/null |") || warn("exec: $ethtool: $!");
				$debug_network_2 .= ">> $ethtool -i $int\n";
				while ($eline = <$eth>) {
					$debug_network_2 .= $eline;
					chomp($eline);
					if ($eline =~ /driver:\s+(\S+)/) {
						$interface_to_driver{$int} = $1;
					} elsif ($eline =~ /bus-info:\s+(\S+)/) {
						$pci = $1;
					}
				}
				close($eth);

				if (!$pci) {
					if (-l "/sys/class/net/$int/driver") {
						$interface_to_driver{$int} = readlink("/sys/class/net/$int/driver");
						$interface_to_driver{$int} =~ s/.*\///;
					}
					if (-l "/sys/class/net/$int/device") {
						$pci = readlink("/sys/class/net/$int/device");
						$pci =~ s/.*\///;
					}
				}
			}
			if (defined $int && $line =~ /RUNNING/) {
				# needed for 2.4 kernels which don't have "Link detected" above
				$interfaces{$int}{status} = "active";
			}
			if (!$pci && defined $int && $line =~ /Interrupt:(\d+)/i) {
				$pci = $irq_to_pci{"$1,0x0200"} if ($irq_to_pci{"$1,0x0200"});
			}
			if (!$pci && defined $int && $line =~ /Memory:([0-9a-f]+)/i) {
				$pci = $iomem_to_pci{$1} if ($iomem_to_pci{$1});
			} 
			if ($line =~ /^\s*$/) {
				if ($int) {
					if (!$pci) {
						if ($dmesg_buf =~ /\b$int:.*probe: addr 0x([0-9a-f]+)*/im) {
							$pci = $iomem_to_pci{$1} if ($iomem_to_pci{$1});
						}
					}
					if (!$interface_to_driver{$int}) {
						if ($pci && $pci_to_driver{$pci}) {
							$interface_to_driver{$int} = $pci_to_driver{$pci};
						} elsif ($dmesg_buf =~ /^(\S+):.*\b$int:.*link is up/im) {
							$interface_to_driver{$int} = $1;
						}
					}
					if ($pci) {
						$pci =~ s/^0000://;
						if ($other_devices{'Network'}{$pci}) {
							my($model) = $other_devices{'Network'}{$pci};
							$model =~ s/^$pci: //;
							$interfaces{$int}{'model'} = $model;
							delete $other_devices{'Network'}{$pci};
						}
					}
				}
				undef $int;
				$pci = 0;
			}
		}
		$debug_network .= $debug_network_2;
	}

	close($in);
}

sub cpuinfo {
	my($in) = 'CPUINFO';
	my($line);

	# prefer data from smbinfo if available
	return unless (-r $cpuinfo);

	my($family, $model, $stepping, $vendor_id, $model_name, $flags, $speed, $cores, $threads);
	my(%physical_id, %core_id);
	my($n) = 0;

	open($in, "< $cpuinfo") || warn "open: $cpuinfo: $!";
	while ($line = <$in>) {
		$debug_cpuinfo .= $line;
		chomp($line);
		if ($line =~ /^cpu family\s*:\s*(\d+)/i) {
			$family = $1;
		} elsif ($line =~ /^model\s*:\s*(\d+)/i) {
			$model = $1;
		} elsif ($line =~ /^stepping\s*:\s*(\d+)/i) {
			$stepping = $1;
		} elsif ($line =~ /^vendor_id\s*:\s*(.*)/i) {
			$vendor_id = $1;
		} elsif ($line =~ /^model name\s*:\s*(.*)/i) {
			$model_name = $1;
		} elsif ($line =~ /^cpu MHz\s*:\s*(.*)/i) {
			$speed = $1;
		} elsif ($line =~ /^physical id\s*:\s*(.*)/i) {
			$physical_id{$1}++;
		} elsif ($line =~ /^core id\s*:\s*(.*)/i) {
			$core_id{$1}++;
		} elsif ($line =~ /^flags\s*:\s*(.*)/i) {
			$flags = $1;
		} elsif ($line =~ /^cpu cores\s*:\s*(.*)/i) {
			$cores = $1;
		} elsif ($line =~ /^\s*$/) {
			if ($family && $model) {
				$n++;
				unless ($procs{$n}{cpuid}) {
					if ($vendor_id =~ /AMD/i && $family >= 15) {
						$procs{$n}{cpuid} = sprintf("%x", (($family - 15) << 20) | (($model >> 4) << 16) | (15 << 8) | (($model & 0xf) << 4) | $stepping);
					} else {
						$procs{$n}{cpuid} = sprintf("%x", ($family << 8) | ($model << 4) | $stepping);
					}
				}
				$procs{$n}{vendor} = $vendor_id;
				$procs{$n}{family} = $model_name;
				$procs{$n}{speed} = $speed;
				$procs{$n}{flags} = $flags;
				# don't set {status} since we don't know if this is a socket
			}
		}
	}

	# $cpu_cores			how many physical cores per socket for this particular cpuid, lookup table
	# $cpu_threads			how many logical cores per socket for this particular cpuid, lookup table

	# $cores			how many cores per chip does cpuinfo think

	# $procs{threads}		how many logical cores for overall system, kernel sees but may be disabled
	# $procs{active_threads}	how many logical cores for overall system that are enabled
	# $procs{cores}			how many physical cores for overall system, some may be disabled in bios/kernel
	# $procs{active_cores}		how many physical cores for overall system that are enabled
	# $procs{sockets}		how many sockets for overall system, might not be populated with chip

	my($id) = cpuid($procs{1}{vendor}, hex($procs{1}{cpuid}));

	my($cpu_info, $cpu_cores, $cpu_threads, $cpu_gen) = ('', 0, 0, 0);
	if ($cpu_models{$id}) {
		$model_name = canon_cpu_model($model_name);
		if (ref($cpu_models{$id}[0]) eq "ARRAY") {
			my($try);
			foreach $try (@{$cpu_models{$id}}) {
				($cpu_info, $cpu_cores, $cpu_threads, $cpu_gen) = @{$try->[1]};
				last if ($model_name =~ /$try->[0]/i);
			}
		} else {
			($cpu_info, $cpu_cores, $cpu_threads, $cpu_gen) = @{$cpu_models{$id}};
		}
	}

	if ($cpu_cores) {
		if ($cores && $cores != $cpu_cores) {
			$debug .= sprintf("Debug-Cores:\t%d (cpuinfo) != %d (lookup)\n", $cores, $cpu_cores);
		}
		$cores = $cpu_cores;
	}

	if (!$cores) {
		if ($model_name =~ /opteron/i && $flags =~ /\bht\b/i) {
			# older kernels don't have the "cpu cores" field, so guess that ht == 2
			$cores = 2;
		}
	}

	my($htt) = 0;
	if (defined $procs{1}{features}) {
		my($features) = $procs{1}{features};
		$htt = (hex($features) >> $feature_htt_bit) & 0x0001;
	} else {
		$htt = ($flags =~ /\bht\b/i);
	}

	if ($cpu_threads) {
		# don't know of a way to actually test to see if hyperthreads are enabled.
		# so this will be wrong if hyperthreading is disabled in bios, in  particular
		# this is a problem with old dl320 g2's that probably don't have bios support,
		# so cpu is HT capable but no way for system to run in that mode.  this one is
		# easy to fix because cpu count is one.  need to use dmidecode for this.

		# well, at least on old kernels, some seem not to show HT in flags, which i
		# guess means kernel doesn't support it.  so let's at least get that case right.

		# actually absense of HT does not imply kernel doesn't support it.  e.g.
		# tp-ms-db1.search.sp1 is running 2.4.21-32.ELsmp, supports HT but doesn't show in cpuinfo
		if ($htt) {
			$threads = $cpu_threads;
		}
	}

	# $n is now number of threads

	# this we're sure about, cpuinfo always gets this right
	$procs{threads} = $n;
	$procs{active_threads} = $n;

	my(@keys) = keys %core_id;
	if ($#keys > -1) {
		$n = $#keys + 1;
	} else {
		if ($n > 1 && $threads) {
			$n = $n / $threads;
		}
	}

	# $n is now number of cores

	$procs{cores} = $n;
	$procs{active_cores} = $n;

	if ($n > 1 && $cores) {
		$n = $n / $cores;
	}

	# $n is now number of chips

	@keys = keys %physical_id;
	$procs{chips} = $#keys + 1;
}

sub mptutil {
	my($result) = 0;
	my($in) = 'MPT';
	my($line);
	my($dev);
	my($adapter, @adapters);
	my($vol);


	system("which $mptutil > /dev/null 2>&1");
	return $result if ($?);
	$have_mptutil = 1;
	$debug_storage .= ">> $mptutil\n";

	open($in, "$mptutil show adapter 2> /dev/null |") || warn "exec $mptutil: $!";
	$debug_storage .= ">>> $mptutil show adapter\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /^(mpt(\d+))\s+Adapter/) {
			$adapter = $2;
			$dev = $1;
			push(@adapters, $adapter);
			$got_mptutil = 1;
			$result = 1;
		} elsif ($line =~ /^\s*battery backup: (.*)/i) {
			$props{$dev}{'bbu'} = ($1 eq "present");
		} elsif ($line =~ /^\s*onboard memory: (.*)/i) {
			$props{$dev}{'cache-size'} = parse_bytes($1, 1024);
		}
	}
	close($in);

	open($in, "$mptutil show drives 2> /dev/null |") || warn "exec $mptutil: $!";
	$debug_storage .= ">>> $mptutil show drives\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /^(mpt\d+) physical drives:/i) {
			$vol = "$1-spares";
		} elsif ($line =~ /\b(SPARE|UNCONFIGURED)\b.*<([^>]+)>/) {
			my($model) = canon_drive($2);
			$props{$vol}{raid} = "JBOD";
			$drives{$vol}{$drive_id}++;
			$props{drives}{$drive_id}{model} = $model;
			$props{drives}{$drive_id}{volume} = $vol;
			$drive_id++;
			$volumes{$vol}++;
			$ndrives++;
		}
	}
	close($in);

	foreach $adapter (@adapters) {
		my($array);
		my(%array_to_vol);
		my($dev) = "mpt$adapter";

		open($in, "$mptutil -u $adapter show firmware 2> /dev/null |") || warn "exec $mptutil: $!";
		$debug_storage .= ">>> $mptutil -u $adapter show firmware\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^BIOS\s+(\S+)/) {
				$props{$dev}{'bios'} = $1;
			} elsif ($line =~ /^APP\s+(\S+)/) {
				$props{$dev}{'firmware'} = $1;
			}
		}
		close($in);

		open($in, "$mptutil -u $adapter show config 2> /dev/null |") || warn "exec $mptutil: $!";
		$debug_storage .= ">>> $mptutil -u $adapter show config\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^\s*volume (da)?(\d+) \(([^\)]*)\) (\S+) (\S+) (\S+)/) {
				$vol = "da$2";
				delete $drives{$vol};
				$props{$vol}{bytes} = parse_bytes($3, 1024);
				$props{$vol}{raid} = $4;
				my($stripe, $status) = ($5, $6);
				if ($stripe =~ /^\d+/) {
					$props{$vol}{stripe} = parse_bytes($stripe, 1024);
					$props{$vol}{status} = parse_volume_status($status);
				} else {
					$props{$vol}{status} = parse_volume_status($stripe);
				}
			} elsif ($line =~ /^\s*dedicated spare/) {
				$vol = '';
			} elsif ($line =~ /^\s*array (\d+)\s*$/) {
				if ($vol) {
					$array_to_vol{$1} = $vol;
				}
			}
		}
		close($in);

		open($in, "$mptutil -u $adapter show config 2> /dev/null |") || warn "exec $mptutil: $!";
		while ($line = <$in>) {
			chomp($line);
			if ($line =~ /^\s*volume (da)?(\d+)/) {
				$vol = "da$2";
			} elsif ($line =~ /^\s*spare (\d+) \([^\)]*\) ([^<]+) <([^>]+)> (\S+)/) {
				my($status) = $2;
				my($model) = canon_drive($3) || "Unknown";
				my($spares) = "$dev-spares";
				$drives{$spares}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{interface} = $4;
				$props{drives}{$drive_id}{volume} = $spares;
				$drive_id++;
				$volumes{$spares}++;
				$props{$spares}{raid} = "JBOD";
				$ndrives++;
				if ($status ne "ONLINE") {
					$props{$spares}{failed}++;
				}
			} elsif ($line =~ /^\s*drive (da)?(\d+) \([^\)]*\) ([^<]+) <([^>]+)> (\S+)/) {
				my($status) = $3;
				my($model) = canon_drive($4) || "Unknown";
				if ($1) {
					$vol = "da$2";
					delete $drives{$vol};	# nuke camcontrol info
				}
				$drives{$vol}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{interface} = $5;
				$props{drives}{$drive_id}{status} = parse_drive_status($status);
				$props{drives}{$drive_id}{volume} = $vol;
				$drive_id++;
				$volumes{$vol}++;
				$ndrives++;
				if ($status eq "REBUILD") {
					$props{$vol}{rebuild}++;
				} elsif ($status eq "MISSING") {
					$props{$vol}{missing}++;
				} elsif ($status ne "ONLINE") {
					# e.g. OFFLINE, FAILED
					$props{$vol}{failed}++;
				}
			}
		}
		close($in);
	}
}

sub mfiutil {
	my($result) = 0;
	my($in) = 'MFI';
	my($line);
	my($dev);
	my($adapter, @adapters);
	my($vol);
	my($try);

	# MegaCli docs at:
	# http://www.lsilogic.com/files/docs/techdocs/storage_stand_prod/sas/mr_sas_sw_ug.pdf

	system("which $mfiutil > /dev/null 2>&1");
	return $result if ($?);
	$have_mfiutil = 1;
	$debug_storage .= ">> $mfiutil\n";

	$try = 0;
	while (1) {
		open($in, "$mfiutil -u $try show adapter 2> /dev/null |") || warn "exec $mfiutil: $!";
		$debug_storage .= ">>> $mfiutil -u $try show adapter\n";
		my($found) = 0;
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^(mfi(\d+))\s+Adapter/) {
				$adapter = $2;
				$dev = $1;
				push(@adapters, $adapter);
				$got_mfiutil = 1;
				$result = 1;
				$found = 1;
			} elsif ($line =~ /^\s*firmware: (.*)/i) {
				$props{$dev}{'package'} = $1;
			} elsif ($line =~ /^\s*battery backup: (.*)/i) {
				$props{$dev}{'bbu'} = ($1 eq "present");
			} elsif ($line =~ /^\s*onboard memory: (.*)/i) {
				$props{$dev}{'cache-size'} = parse_bytes($1, 1024);
			}
		}
		close($in);
		last if !$found;
		$try++;
	}

	foreach $adapter (@adapters) {
		my($array);
		my(%array_to_vol);
		my($dev) = "mfi$adapter";

		open($in, "$mfiutil -u $adapter show drives 2> /dev/null |") || warn "exec $mfiutil: $!";
		$debug_storage .= ">>> $mfiutil -u $adapter show drives\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^(mfi\d+) physical drives:/i) {
				$vol = "$1-spares";
			} elsif ($line =~ /\b(SPARE|UNCONFIGURED)\b.*<([^>]+)>/) {
				my($model) = canon_drive($2) || "Unknown";
				$props{$vol}{raid} = "JBOD";
				$drives{$vol}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{volume} = $vol;
				$drive_id++;
				$volumes{$vol}++;
				$ndrives++;
			}
		}
		close($in);

		open($in, "$mfiutil -u $adapter show firmware 2> /dev/null |") || warn "exec $mfiutil: $!";
		$debug_storage .= ">>> $mfiutil -u $adapter show firmware\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^BIOS\s+(\S+)/) {
				$props{$dev}{'bios'} = $1;
			} elsif ($line =~ /^APP\s+(\S+)/) {
				$props{$dev}{'firmware'} = $1;
			}
		}
		close($in);

		open($in, "$mfiutil -u $adapter show config 2> /dev/null |") || warn "exec $mfiutil: $!";
		$debug_storage .= ">>> $mfiutil -u $adapter show config\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^\s*volume (mfid)?(\d+) \(([^\)]*)\) (\S+) (\S+) (\S+)/) {
				$vol = "mfid$2";
				delete $drives{$vol};
				$props{$vol}{bytes} = parse_bytes($3, 1024);
				$props{$vol}{raid} = $4;
				my($stripe, $status) = ($5, $6);
				if ($stripe =~ /^\d+/) {
					$props{$vol}{stripe} = parse_bytes($stripe, 1024);
					$props{$vol}{status} = parse_volume_status($status);
				} else {
					$props{$vol}{status} = parse_volume_status($stripe);
				}
			} elsif ($line =~ /^\s*dedicated spare/) {
				$vol = '';
			} elsif ($line =~ /^\s*array (\d+)\s*$/) {
				if ($vol) {
					$array_to_vol{$1} = $vol;
				}
			}
		}
		close($in);

		open($in, "$mfiutil -u $adapter show config 2> /dev/null |") || warn "exec $mfiutil: $!";
		while ($line = <$in>) {
			chomp($line);
			if ($line =~ /^\s*array (\d+) of (\d+) drives/) {
				$array = $1;
				$vol = $array_to_vol{$array};
			} elsif ($line =~ /^\s*drive (\d+) \([^\)]*\) ([^<]+) <([^>]+)> (\S+)/) {
				my($status) = $2;
				my($model) = canon_drive($3) || "Unknown";
				$drives{$vol}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{interface} = $4;
				$props{drives}{$drive_id}{status} = parse_drive_status($status);
				$props{drives}{$drive_id}{volume} = $vol;
				$drive_id++;
				$volumes{$vol}++;
				$ndrives++;
				if ($status eq "REBUILD") {
					$props{$vol}{rebuild}++;
				} elsif ($status eq "MISSING") {
					$props{$vol}{missing}++;
				} elsif ($status ne "ONLINE") {
					# e.g. OFFLINE, FAILED
					$props{$vol}{failed}++;
				}
			} elsif ($line =~ /^\s*volume/) {
				last;
			}
		}
		close($in);
	}
}

sub omreport {
	my($omreport) = 'omreport';
	my($result) = 0;
	my($line);
	my($controller, %controllers);
	my($device);
	my(%seen);
	my(%count);

	return 1 if ($got_omreport);

	system("which $omreport > /dev/null 2>&1");
	return $result if ($?);
	$have_omreport = 1;
	$debug_storage .= ">> $omreport\n";

	$count{'megaraid'} = 0;
	$count{'megaraid_sas'} = 0;

	open($in, "$omreport storage controller 2> /dev/null |") || warn "exec $omreport: $!";
	$debug_storage .= ">>> $omreport storage controller\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /^\s*$/) {
			$device = "unknown";
		} elsif ($line =~ /^ID\s*:\s*(\d+)\s*$/) {
			$controller = $1;
			$got_omreport = 1;
			$result = 1;
		} elsif ($line =~ /^Name\s*:\s*(.*)/) {
			my($name) = $1;
			if ($name =~ /PERC [34]/) {
				$device = 'megaraid' . $count{'megaraid'}++;
				$controllers{$controller} = $device;
			} elsif ($name =~ /PERC/) {
				$device = 'megaraid_sas' . $count{'megaraid_sas'}++;
				$controllers{$controller} = $device;
			}
		} elsif ($line =~ /^Firmware Version\s*:\s*(.*)/) {
			# don't use this if megarc already set firmware version
			$props{$device}{'package'} = $1 unless ($props{$device}{'firmware'});
		} elsif ($line =~ /^Cache Memory Size\s*:\s*(.*)/) {
			$props{$device}{'cache-size'} = parse_bytes($1, 1024);
		}
	}
	close($in);

	foreach $controller (keys %controllers) {
		$device = $controllers{$controller};
		open($in, "$omreport storage battery controller=$controller 2> /dev/null |") || warn "exec $omreport: $!";
		$debug_storage .= ">>> $omreport storage battery controller=$controller\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^Status\s*:\s*(\S+)/) {
				$props{$device}{'bbu'} = ($1 eq "Ok");
			}
		}
		close($in);

		my($volume, $name, $raid, $bytes, $stripe, $virtual) = (0, 0, 0, 0, 0, 0);
		my(%virtuals);

		open($in, "$omreport storage vdisk controller=$controller 2> /dev/null |") || warn "exec $omreport: $!";
		$debug_storage .= ">>> $omreport storage vdisk controller=$controller\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^ID\s*:\s*(\d+)/) {
				$virtual = $1;
			} elsif ($line =~ /^Layout\s*:\s*(\S+)/) {
				$raid = canon_raid($1);
			} elsif ($line =~ /^Name\s*:\s*(.*)/) {
				$name = $1;
			} elsif ($line =~ /^Size\s*:\s*.*\((\d+) bytes\)/) {
				$bytes = $1;
			} elsif ($line =~ /^Device Name\s*:\s*(\S+)/) {
				$volume = $1;
				$volume =~ s/^\/dev\///;
			} elsif ($line =~ /^Stripe Element Size\s*:\s*(.*)/) {
				$stripe = parse_bytes($1, 1024);
			} elsif ($line =~ /^\s*$/) {
				if ($name) {
					$volume = "$device-l$virtual" unless $volume;
					$virtuals{$virtual} = $volume;
					$volumes{$volume}++;
					$props{$volume}{bytes} = $bytes;
					$props{$volume}{raid} = $raid;
					$props{$volume}{stripe} = $stripe;
					# remove info we got from /proc/scsi/scsi
					delete $drives{$volume} unless $seen{$volume}++;
					($volume, $name, $raid, $bytes, $stripe, $virtual) = (0, 0, 0, 0, 0, 0);
				}
			}
		}
		close($in);

		foreach $virtual (keys %virtuals) {
			$volume = $virtuals{$virtual};
			my($have_size) = $props{$volume}{bytes};
			my($state);
			my($vendor, $model, $serial, $firmware, $year, $week, $day, $wwn) = ('', '', '', '', '', '', '', '');

			open($in, "$omreport storage adisk controller=$controller vdisk=$virtual 2> /dev/null |") || warn "exec $omreport: $!";
			$debug_storage .= ">>> $omreport storage adisk controller=$controller vdisk=$virtual\n";
			while ($line = <$in>) {
				$debug_storage .= $line;
				chomp($line);
				if ($line =~ /^State\s*:\s*(.*)/) {
					$state = $1;
					$props{$volume}{failed}++ if ($1 ne 'Online');
				} elsif ($line =~ /^Hot Spare\s*:\s*(.*)/) {
					$props{$volume}{spare}++ if ($1 eq 'Yes');
				} elsif ($line =~ /^Product ID\s*:\s*(.*)/) {
					$model = $1;
				} elsif ($line =~ /^Serial No\.\s*:\s*(.*)/) {
					$serial = $1;
				} elsif ($line =~ /^Revision\s*:\s*(.*)/) {
					$firmware = $1;
				} elsif ($line =~ /^Vendor ID\s*:\s*(.*)/) {
					$vendor = $1;
				} elsif ($line =~ /^Manufacture Day\s*:\s*(.*)/) {
					$day = $1;
				} elsif ($line =~ /^Manufacture Week\s*:\s*(.*)/) {
					$week = $1;
				} elsif ($line =~ /^Manufacture Year\s*:\s*(.*)/) {
					$year = $1;
				} elsif ($line =~ /^SAS Address\s*:\s*(.*)/) {
					$wwn = $1;
				} elsif ($line =~ /^Capacity\s*:\s*.*\((\d+) bytes\)/) {
					$bytes = $1;
					$props{$volume}{bytes} += $bytes unless ($have_size);
				} elsif ($line =~ /^\s*$/) {
					if ($model) {
						$model = canon_drive("$vendor-$model");
						$ndrives++;
						$drives{$volume}{$drive_id}++;
						$props{drives}{$drive_id}{model} = $model;
						$props{drives}{$drive_id}{volume} = $volume;
						$props{drives}{$drive_id}{serial} = $serial;
						$props{drives}{$drive_id}{firmware} = $firmware;
						$props{drives}{$drive_id}{wwn} = $wwn;
						$props{drives}{$drive_id}{size} = $bytes;
						$props{drives}{$drive_id}{date} = sprintf("%04d%02d%02d", $year, $week, $day) if ($year && $week && $day);
						$drive_id++;
					}
					($model, $vendor) = ('', '');
				}
			}
			close($in);
		}
	}

	return $result;
}

sub arcconf {
	my($result) = 0;
	my($line);
	my($controller, @controllers);
	my($volume);
	my(%mapping);

	system("which $arcconf > /dev/null 2>&1");
	return $result if ($?);
	$have_arcconf = 1;
	$debug_storage .= ">> $arcconf\n";

	open($in, "$arcconf getversion 2> /dev/null |") || warn "exec $arcconf: $!";
	$debug_storage .= ">>> $arcconf getversion\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /^Controller #(\d+)/) {
			push(@controllers, int($1));
			$got_arcconf++;
			$result++;
		}
	}
	close($in);

	foreach $controller (@controllers) {
		open($in, "$arcconf getconfig $controller 2> /dev/null |") || warn "exec $arcconf: $!";
		$debug_storage .= ">>> $arcconf getconfig $controller\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			last if ($line =~ /^Logical device information/);
		}
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			last if ($line =~ /^Physical Device information/);
			if ($line =~ /Logical device number (\d+)/) {
				# hopefully gets overwritten by "logical device name"
				$volume = "aac-c$controller-l$1";
			} elsif ($line =~ /Logical device name.*: (.*)/) {
				my($logical) = $1;
				# if only one vol, map it
				my(@keys) = keys %{$logical_to_os{'other'}};
				if ($#keys == 0) {
					$logical = $keys[0];
				}
				if ($logical_to_os{'other'}{$logical}) {
					$volume = $logical_to_os{'other'}{$logical};
					# remove info we got from /proc/scsi/scsi
					delete $drives{$volume};
				}
			} elsif ($line =~ /RAID level.*: (.*)/) {
				$props{$volume}{raid} = "RAID $1";
			} elsif ($line =~ /Status of logical device.*: (.*)/) {
				$props{$volume}{status} = parse_volume_status($1);
			} elsif ($line =~ /Size.*: (.*)/) {
				$props{$volume}{bytes} = parse_bytes($1, 1024);
			} elsif ($line =~ /Stripe-unit size.*: (.*)/) {
				$props{$volume}{stripe} = parse_bytes($1, 1024);
			} elsif ($line =~ /Read-cache mode.*: (\S+)/) {
				$props{$volume}{rcache_enable} = parse_enable($1);
			} elsif ($line =~ /Write-cache mode.*: (\S+)/) {
				$props{$volume}{wcache_enable} = parse_enable($1);
			} elsif ($line =~ /Segment \d+.*: .*\((\d+),(\d+)\)/) {
				my($channel, $device) = ($1, $2);
				$mapping{"$channel,$device"} = $volume;
			}
		}
		my($vendor, $model, $hide, $state, $channel);
		my($id) = 0;
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^\s*Device is (.*)/) {
				my($type) = $1;
				if ($type =~ /Hard drive/) {
					$hide = 0;
					$id = $drive_id;
				} else {
					$hide = 1;
					$id = 0;
				}
			} elsif ($line =~ /Channel #(\d+)/) {
				$channel = $1;
			} elsif ($line =~ /^\s*State\s+: (.*)/) {
				$state = $1;
				$props{drives}{$id}{status} = parse_drive_status($1);
			} elsif ($line =~ /^\s*Reported Channel,Device\s+: (.*)/) {
				if ($mapping{$1}) {
					$volume = $mapping{$1};
				} else {
					$volume = "aac-c$controller-spares";
					$props{$volume}{raid} = "JBOD";
				}
			} elsif ($line =~ /^\s*Firmware\s+: (.*)/) {
				$props{drives}{$id}{firmware} = $1;
			} elsif ($line =~ /^\s*Serial number\s+: (.*)/) {
				$props{drives}{$id}{serial} = $1;
			} elsif ($line =~ /^\s*Size\s+: (.*)/) {
				$props{drives}{$id}{size} = parse_bytes($1, 1024);
			} elsif ($line =~ /^\s*Write Cache\s+: (.*)/) {
				$props{drives}{$id}{wcache_enable} = parse_enable($1);
			} elsif ($line =~ /^\s*S\.M\.A\.R\.T\.\s+: (.*)/) {
				$props{drives}{$id}{smart_enable} = parse_enable($1);
			} elsif ($line =~ /^\s*Transfer Speed\s+: (.*)/) {
				$props{drives}{$id}{speed} = parse_drive_speed($1);
			} elsif ($line =~ /^\s*Vendor\s+: (.*)/) {
				$vendor = $1;
			} elsif ($line =~ /^\s*Model\s+: (.*)/) {
				$model = canon_drive("$vendor$1");
				if (!$hide) {
					$ndrives++;
					$drives{$volume}{$drive_id}++;
					$props{drives}{$drive_id}{model} = $model;
					$props{drives}{$drive_id}{volume} = $volume;
					$drive_id++;
					$volumes{$volume}++;
					if ($state eq "Rebuilding") {
						$props{$volume}{rebuild}++;
					} elsif ($state ne "Online" && $state ne "Ready") {
						$props{$volume}{failed}++;
					}
				}
			}
		}
		close($in);
	}

	return $result;
}

sub megacli {
	my($adapter, @adapters);
	my($result) = 0;
	my($line);
	my($volume) = "NA";
	my(%seen);
	my($driver) = 'megaraid_sas';

	# MegaCli docs at:
	# http://www.lsilogic.com/files/docs/techdocs/storage_stand_prod/sas/mr_sas_sw_ug.pdf

	my($megacli) = 'MegaCli';
	system("which $megacli > /dev/null 2>&1");
	if ($?) {
		$megacli = 'MegaCli64';
		system("which $megacli > /dev/null 2>&1");
		if ($?) {
			$megacli = 'megacli';
			system("which $megacli > /dev/null 2>&1");
			return $result if ($?);
		}
	}
	$have_megacli = 1;
	$debug_storage .= ">> $megacli\n";

	open($in, "$megacli -AdpAllInfo -aAll 2> /dev/null |") || warn "exec $megacli: $!";
	$debug_storage .= ">>> $megacli -AdpAllInfo -aAll\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /^Adapter\s+\#(\d+)/) {
			$adapter = int($1);
			push(@adapters, $adapter);
			$got_megacli = 1;
			$result = 1;
		} elsif ($line =~ /^FW Package Build\s*:\s+(\S+)/i) {
			$props{"$driver$adapter"}{'package'} = $1;
		} elsif ($line =~ /^FW Version\s*:\s+(\S+)/i) {
			$props{"$driver$adapter"}{'firmware'} = $1;
		} elsif ($line =~ /^BIOS Version\s*:\s+(\S+)/i) {
			$props{"$driver$adapter"}{'bios'} = $1;
		} elsif ($line =~ /^Serial No\s*:\s+(\S+)/i) {
			$props{"$driver$adapter"}{'serial'} = $1;
		}
	}
	close($in);

	foreach $adapter (@adapters) {
		# if only one volume, wiring is easy
		my($default_volume) = '';
		if (defined $controller_volumes{"$driver$adapter"}) {
			my(@keys) = keys %{$controller_volumes{"$driver$adapter"}};
			if ($#keys == 0) {
				$default_volume = $keys[0];
				delete $drives{$default_volume};	# nuke camcontrol info
			}
		}

		my($state, $wwn, $raw) = ('', '');

		open($in, "$megacli -cfgDsply -a$adapter 2> /dev/null |") || warn "exec $megacli: $!";
		$debug_storage .= ">>> $megacli -cfgDsply -a$adapter\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^BBU: Present/i) {
				$props{"$driver$adapter"}{'bbu'} = 1;
			} elsif ($line =~ /^Memory: (.*)/i) {
				$props{"$driver$adapter"}{'cache-size'} = parse_bytes($1, 1024);
			} elsif ($line =~ /^DISK GROUPS: (\d+)/) {
				$volume = "$driver$adapter-spares";
				$props{$volume}{raid} = "JBOD";
				$volume = ($default_volume || "$driver$adapter-vol$1");

				# make some guesses here for linux on adapter, channel, lun
				my($logical) = sprintf("scsi%d-%d-%d-%d", $adapter, 0, $1, 0);
				if (defined $logical_to_os{'perc'}{$logical}) {
					$volume = $logical_to_os{'perc'}{$logical};
					# remove info we got from /proc/scsi/scsi
					delete $drives{$volume} unless $seen{$volume}++;
				} else {
					# if only one vol, map it
					my(@keys) = keys %{$logical_to_os{'perc'}};
					if ($#keys == 0) {
						$volume = $logical_to_os{'perc'}{$keys[0]};
						# remove info we got from /proc/scsi/scsi
						delete $drives{$volume} unless $seen{$volume}++;
					}
				}

				$volumes{$volume}++;
			} elsif ($line =~ /^RAID Level: Primary-(\d+)/) {
				$props{$volume}{raid} = "RAID $1";
			} elsif ($line =~ /^State:\s*(\S+)/) {
				$props{$volume}{status} = parse_volume_status($1);
			} elsif ($line =~ /^Size:\s*(\S+)/) {
				$props{$volume}{bytes} = parse_bytes($1, 1024);
			} elsif ($line =~ /^Raw Size:\s*(\S+)/) {
				$raw = parse_bytes($1, 1024);
			} elsif ($line =~ /^Stripe Size:\s*(\S+)/) {
				$props{$volume}{stripe} = parse_bytes($1, 1024);
			} elsif ($line =~ /^Firmware state:\s+(\S+)/) {
				$state = $1;
			} elsif ($line =~ /^SAS Address\(0\):\s*(\S+)/) {
				$wwn = $1;
			} elsif ($line =~ /^Inquiry Data:\s+(\S+)\s+(\S+)\s+(\S+)(\s+(\S+))?/) {
				my($model, $serial);
				if ($4) {
					$model = canon_drive("$1-$2-$3");
					$serial = $5;
				} else {
					$model = canon_drive("$1-$2");
					$serial = $3;
				}
				$ndrives++;
				$drives{$volume}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{volume} = $volume;
				$props{drives}{$drive_id}{serial} = $serial;
				$props{drives}{$drive_id}{wwn} = $wwn;
				$props{drives}{$drive_id}{size} = $raw;
				$props{drives}{$drive_id}{status} = parse_drive_status($state);
				$drive_id++;
				$volumes{$volume}++;
				if ($state eq "FAILED") {
					$props{$volume}{failed}++;
				}
			}
		}
		close($in);
	}
	return $result;
}

sub megarc {
	my(%megadrives);
	my($adapter, @adapters);
	my($result) = 0;
	my($line);
	my($volume) = "NA";
	my(%seen);
	my($driver) = ($os_type eq 'linux') ? 'megaraid' : 'amr';

	system("which $megarc > /dev/null 2>&1");
	return $result if ($?);
	$have_megarc = 1;
	$debug_storage .= ">> $megarc\n";

	open($in, "$megarc -AllAdpInfo -noLog 2> /dev/null |") || warn "exec $megarc: $!";
	$debug_storage .= ">>> $megarc -AllAdpInfo\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		last if ($line =~ /^\s*AdapterNo/);
	}
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		$got_megarc = 1;
		$result = 1;
		if ($line =~ /^\s*(\d+)/) {
			push(@adapters, int($1));
		} else {
			last;
		}
	}
	close($in);

	foreach $adapter (@adapters) {
		unless ($os_type eq "freebsd" && $os_date < 20070130) {
			# armd bug in early freebsd kernels causes data corruption for this query, so don't do it.
			open($in, "$megarc -ctlrInfo -a$adapter -noLog 2> /dev/null |") || warn "exec $megarc: $!";
			$debug_storage .= ">>> $megarc -ctlrInfo -a$adapter\n";
			while ($line = <$in>) {
				$debug_storage .= $line;
				chomp($line);
				if ($line =~ /Firmware Version : (\S+)/i) {
					$props{"$driver$adapter"}{'firmware'} = $1;
				}
				if ($line =~ /BIOS Version : (\S+)/i) {
					$props{"$driver$adapter"}{'bios'} = $1;
				}
				if ($line =~ /DRAM : (\S+)/i) {
					$props{"$driver$adapter"}{'cache-size'} = parse_bytes($1, 1024);
				}
			}
			close($in);
		}

		# if only one volume, wiring is easy
		my($default_volume) = '';
		if (defined $controller_volumes{"$driver$adapter"}) {
			my(@keys) = keys %{$controller_volumes{"$driver$adapter"}};
			if ($#keys == 0) {
				$default_volume = $keys[0];
				delete $drives{$default_volume};	# nuke camcontrol info
			}
		}

		open($in, "$megarc -ldInfo -Lall -a$adapter -noLog 2> /dev/null |") || warn "exec $megarc: $!";
		$debug_storage .= ">>> $megarc -ldInfo -Lall -a$adapter\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /Logical Drive\s*:\s*(\d+)\s*\(\s*Adapter:\s*(\d+)\s*\)/) {
				$volume = "$driver$adapter-spares";
				$props{$volume}{raid} = "JBOD";
				$volume = ($default_volume || "$driver$adapter-vol$1");

				# make some guesses here for linux on adapter, channel, lun
				my($logical) = sprintf("scsi%d-%d-%d-%d", $adapter, 0, $1, 0);
				if (defined $logical_to_os{'megaraid'}{$logical}) {
					$volume = $logical_to_os{'megaraid'}{$logical};
					# remove info we got from /proc/scsi/scsi
					delete $drives{$volume} unless $seen{$volume}++;
				} else {
					# if only one vol, map it
					my(@keys) = keys %{$logical_to_os{'megaraid'}};
					if ($#keys == 0) {
						$volume = $logical_to_os{'megaraid'}{$keys[0]};
						# remove info we got from /proc/scsi/scsi
						delete $drives{$volume} unless $seen{$volume}++;
					}
				}

				$volumes{$volume}++;
			}
			if ($line =~ /RaidLevel\s*:\s*(\S+)/) {
				$props{$volume}{raid} = "RAID $1";
			}
			if ($line =~ /StripSz\s*:\s*(\S+)/) {
				$props{$volume}{stripe} = parse_bytes($1, 1024);
			}
			if ($line =~ /Cache\s*:\s*(\S+)/) {
				$props{$volume}{cachepolicy} = $1;	# CachedIO, DirectIO
			}
			if ($line =~ /WrPolicy\s*:\s*(\S+)/) {
				$props{$volume}{wrpolicy} = $1;		# WriteBack, ?
			}
			if ($line =~ /RdAhead\s*:\s*(\S+)/) {
				$props{$volume}{rdahead} = $1;		# Yes, No
			}
			if ($line =~ /^\s*(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s*$/) {
				my($status) = $5;
				my($drive) = sprintf("%d-%d-%d", $adapter, int($1), int($2));
				$megadrives{$drive}{vol} = $volume;
				if ($status eq "FAILED") {
					$props{$volume}{failed}++;
				} elsif ($status eq "RBLD") {
					$props{$volume}{rebuild}++;
				}
			}
		}
		close($in);

		open($in, "$megarc -phys -idAll -chAll -a$adapter -noLog 2> /dev/null |") || warn "exec $megarc: $!";
		$debug_storage .= ">>> $megarc -phys -idAll -chAll -a$adapter\n";
		my($type, $rev);
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^\s*Adapter\s+(\d+),\s+Channel\s+(\d+),\s+Target ID\s+(\d+)/) {
				my($drive) = sprintf("%d-%d-%d", int($1), int($2), int($3));
				$volume = $megadrives{$drive}{vol} || "$driver$adapter-spares";
			}
			if ($line =~ /Type\s*:\s+(\S+)/) {
				$type = lc($1);
			}
			if ($line =~ /Revision\s*:\s*(\S+)/) {
				$rev = $1;
			}
			if ($line =~ /Product\s*:\s+(\S+)/ && $type eq "disk") {
				my($model) = canon_drive($1);
				$ndrives++;
				$drives{$volume}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{volume} = $volume;
				$props{drives}{$drive_id}{firmware} = $rev;
				$drive_id++;
				$volumes{$volume}++;
			}
		}
		close($in);
	}
	return $result;
}

sub mptable {
	my($in) = 'MPTABLE';
	my($line);
	my($result) = 0;

	system("which $mptable > /dev/null 2>&1");
	return $result if ($?);

	open($in, "$mptable 2> /dev/null |") || warn "exec $mptable: $!";
	while ($line = <$in>) {
		last if ($line =~ /^Processors:/);
	}
	while ($line = <$in>) {
		last if ($line =~ /^[-= ]*$/);
		$result = 1;
		$procs{mptable}++;
		if (!$procs{$procs{mptable}}{cpuid}) {
			my(@cols) = split(' ', $line);
			my($family, $model, $step, $flags) = @cols[$#cols-3 .. $#cols];
			if ($family == 6 && $model == 2 && $step == 1) {
				# appears to be the only bug with mptable, otherwise seems reliable
				# e.g. uds1.ads.cnb.yahoo.com, p24.chat.dcn.yahoo.com, mailsrv.bangalore.corp.yahoo.com
				# and it's not just 6b1 that it gets wrong
				# so ignore it here, hopefully get it from dmesg
			} else {
				$procs{$procs{mptable}}{cpuid} = sprintf("0x%x%x%x", $family, $model, $step);
			}
			$flags =~ s/^0x//;
			$procs{$procs{mptable}}{features} = $flags;
		}
	}
	close($in);
	return $result;
}

sub twcli {
	my($in) = 'TWCLI';
	my($line);
	my($controller, %controllers);
	my(%units, %ports);
	my($version) = 0;
	my($result) = 0;
	my(%seen);

	system("which $twcli > /dev/null 2>&1");
	return $result if ($?);
	$have_twcli = 1;
	$debug_storage .= ">> $twcli\n";

	open($in, "$twcli info 2> /dev/null |") || warn "exec $twcli: $!";
	$debug_storage .= ">>> $twcli info\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		last if ($line =~ /^---------/);
	}
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
	 	last if ($line =~ /^\s*$/);
		if ($line =~ /^Controller\s+(\d+):\s+(\S+)/) {
			$controllers{"c$1"} = $2;
			$version = 1;
		} elsif ($line =~ /^(c\d+)\s+(\S+)/) {
			$controllers{$1} = $2;
			$version = 2;
		}
	}
	close($in);

	foreach $controller (keys %controllers) {
		my(%header);
		$got_twcli++;
		$result++;
		open($in, "$twcli info $controller |") || warn "exec $twcli: $!";
		$debug_storage .= ">>> $twcli info $controller\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			my(@cols) = split(' ', $line);
			if ($line =~ /^Unit/) {
				my($i);
				for ($i=0; $i <= $#cols; $i++) {
					$header{$cols[$i]} = $i;
				}
			} elsif ($line =~ /^u(\d+)/ || $line =~ /^\s*unit\s+(\d+)/i) {
				my($unit) = "u$1";
				my($volume);
				my($type) = ($version == 1) ? $cols[2] : $cols[1];
				my($size) = ($version == 1) ? "$cols[5] $cols[6]" : "$cols[$header{'Size(GB)'}] GB";
				my($stripe) = parse_bytes(($version == 1) ? $cols[4] : $cols[$header{'Stripe'}], 1024);
				if ($type eq "SPARE") {
					$volume = "tw-$controller-spares";
					$props{$volume}{raid} = "JBOD";
				} else {
					$volume = "$controller-$unit";
					if (defined $logical_to_os{'tw'}{$volume}) {
						$volume = $logical_to_os{'tw'}{$volume};
						# remove info we got from /proc/scsi/scsi
						delete $drives{$volume} unless $seen{$volume}++;
					} else {
						# if only one vol, map it
						my(@keys) = keys %{$logical_to_os{'tw'}};
						if ($#keys == 0) {
							$volume = $logical_to_os{'tw'}{$keys[0]};
							# remove info we got from /proc/scsi/scsi
							delete $drives{$volume} unless $seen{$volume}++;
						}
					}
					$props{$volume}{raid} = $type;
					$props{$volume}{bytes} = parse_bytes($size, 1024);
				}
				$props{$volume}{stripe} = $stripe;
				$volumes{$volume}++;
				$units{$unit}{volume} = $volume;

				# update 3ware controller model if we have enough data
				my($device) = $props{$volume}{device};
				if ($controllers{$controller} && $device) {
					if (defined $other_devices{'Disk-Control'}{$device}) {
						$other_devices{'Disk-Control'}{$device} =~ s/(7xxx\/8xxx|9xxx)/$controllers{$controller}/;
					}
				}
			} elsif ($line =~ /^p(\d+)/ || $line =~ /^\s*port\s+(\d+)/i) {
				my($port) = "p$1";
				my($unit) = "unknown";
				next if ($line =~ /NOT.PRESENT/);
				if ($version == 1) {
					if ($line =~ /unit\s+(\d+)/i) {
						$unit = "u$1";
					} elsif ($line =~ /\(NO UNIT\)/) {
						$unit = "free";
					}
				} else {
					$unit = $cols[2];
				}
				my($volume);
				if (defined($units{$unit}) && $units{$unit}{volume}) {
					$volume = $units{$unit}{volume};
				} else {
					if ($unit eq "-") {
						$volume = "tw-$controller-free";
					} else {
						$volume = "tw-$controller-$unit";
					}
					$volumes{$volume}++;
					$props{$volume}{raid} = "JBOD";
				}
				$props{$volume}{failed}++ if ($line =~ /DEGRADED/);
				$ports{$controller}{$port} = $volume;
			}
		}
		close($in);

		if ($version == 1) {
			my($unit);
			foreach $unit (keys %units) {
				my($volume) = $units{$unit}{volume};
				open($in, "$twcli info $controller $unit |") || warn "exec $twcli: $!";
				$debug_storage .= ">>> $twcli info $controller $unit\n";
				while ($line = <$in>) {
					$debug_storage .= $line;
					chomp($line);
					if ($line =~ /^Unit Type:\s+(.*)/) {
						$props{$volume}{raid} = $1;
					} elsif ($line =~ /^Size:.*\((\d+)\s+blocks\)/) {
						$props{$volume}{bytes} = $1 * 512;
					} elsif ($line =~ /^Stripe Size:\s*(\S+)/) {
						$props{$volume}{stripe} = parse_bytes($1, 1024);
					}
				}
				close($in);
			}
		}

		my($port);
		foreach $port (keys %{$ports{$controller}}) {
			open($in, "$twcli info $controller $port model |") || warn "exec $twcli: $!";
			$debug_storage .= ">>> $twcli info $controller $port model\n";
			$line = <$in>;
			$debug_storage .= $line;
			chomp($line);
			$line =~ s/^.*model\s*=\s*//i;
			my($model) = canon_drive($line);
			my($volume) = $ports{$controller}{$port};
			$drives{$volume}{$drive_id}++;
			$props{drives}{$drive_id}{model} = $model;
			$props{drives}{$drive_id}{volume} = $volume;
			$drive_id++;
			close($in);
		}
	}
	return $result;
}

sub smbinfo {
	my($in) = "IN";
	my($line);
	my($result) = 0;

	# smbinfo obsoletes smbiosinfo
	system("which $smbinfo > /dev/null 2>&1");
	if ($?) {
		system("which $smbiosinfo > /dev/null 2>&1");
		return $result if ($?);
		open($in, "$smbiosinfo -l 2> /dev/null |") || warn "exec $smbiosinfo: $!";
	} elsif (-t STDIN) {
		open($in, "$smbinfo -l 2> /dev/null |") || warn "exec $smbinfo: $!";
	} else {
		# older versions of smbinfo don't work when stdin is not a tty, e.g. from cron
		# closing stdin works for some older versions, however ancient versions will stil break
		open($in, "$smbinfo -l <&- 2> /dev/null |") || warn "exec $smbinfo: $!";
	}

	if (!eof($in)) {
		$line = <$in>;
		$result = 1;
		my($comp);
		if ($line =~ /^SMBIOS version/) {
			$line = <$in>;
			while (!eof($in)) {
				$debug_dmidecode .= $line;
				if ($line =~ /^(\S.*):/) {
					$comp = $1;
					parse_bios($in, 'BIOS', 0) if ($comp eq "BIOS Information");
					parse_system($in, 'Base Board', 0) if ($comp eq "Base Board Information");
					parse_system($in, 'System', 0) if ($comp eq "System Information");
					parse_proc($in, 'Processor', 0) if ($comp eq "Processor Information");
					parse_memory($in, 'Memory', 'Device', 0) if ($comp eq "Memory Device");
				}
				$line = <$in>;
			}
		} else {
			while (!eof($in)) {
				$debug_dmidecode .= $line;
				if ($line =~ /^\[\S+\s+([^\]]+)\]/) {
					$comp = $1;
					parse_bios($in, 'BIOS', 0) if ($comp eq "BIOS");
					parse_system($in, 'Base Board', 0) if ($comp eq "Base Board");
					parse_system($in, 'System', 0) if ($comp eq "System");
					parse_proc($in, 'Processor', 0) if ($comp eq "Processor");
					parse_memory($in, 'Memory', 'Device', 0) if ($comp eq "Memory Device");
				}
				$line = <$in>;
			}
		}
	}
	return $result;
}

sub dmidecode {
	my($in) = 'DMIDECODE';
	my($line);
	my($result) = 0;
	my($version) = 0;
	my($section, $count, $attribute, $value, $sep);
	my($handle) = 0;
	my($type) = 0;
	my($key);

	system("which $dmidecode > /dev/null 2>&1");
	return $result if ($?);

	open($in, "$dmidecode 2> /dev/null |") || warn("exec $dmidecode: $!");
	while ($line = <$in>) {
		$debug_dmidecode .= $line;
		$line =~ s/\s+$//;

		if ($handle) {
			if ($line =~ /^(\S+.*)/) {
				$dmi{$handle}{'name'} = $1;
			} elsif ($line =~ /^\t(\S[^:]+):\s*(.*)/) {
				$key = $1;
				$dmi{$handle}{'key'}{$1} = $2;
			} elsif ($line =~ /^\t\t(.*)/) {
				$dmi{$handle}{'key'}{$key} .= "\t$1";
			}
		}

		if ($line =~ /^Handle (\S+), DMI type (\d+)/) {
			$handle = $1;
			$type = $2;
			$dmi{$handle}{'type'} = $type;
			$version = 1;
		} elsif ($line =~ /^\t?(System|Base Board)( Information)?( Block)?$/) {
			$result = 1;
			parse_system($in, $1, $handle);
			$handle = 0;
		} elsif ($line =~ /^\t?(BIOS)( Information)( Block)?$/) {
			parse_bios($in, $1, $handle);
			$handle = 0;
		} elsif ($line =~ /^\t?(Memory) (Device|Module Information)$/) {
			parse_memory($in, $1, $2, $handle);
			$handle = 0;
		} elsif ($line =~ /^\t?(Processor)( Information)?$/) {
			parse_proc($in, $1, $handle);
			$handle = 0;
		} elsif ($line =~ /^checksum failed/) {
			last;
		} elsif ($line =~ /^\s*$/) {
			$handle = 0;
		}
	}
	close($in);
	return $result;
}

sub hpacucli {
	my($in) = 'HPACUCLI';
	my($line);
	my($slot, %slots, %logicals, %physicals, %arrays);

	system("which $hpacucli > /dev/null 2>&1");
	return if ($?);
	$have_hpacucli = 1;

	return if system("$hpacucli version > /dev/null 2>&1");
	$debug_storage .= ">> $hpacucli\n";

	open($in, "$hpacucli controller all show 2> /dev/null |") || warn "exec $hpacucli: $!";
	$debug_storage .= ">>> $hpacucli controller all show\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /in\s+slot\s+(\d+)/i) {
			$slots{$1}++;
			$got_hpacucli = 1;
		}
	}
	close($in);

	my($count) = 0;
	foreach $slot (keys %slots) {
		my($dev) = "cciss$count";
		my($total_cache_size) = 0;
		my($ratio) = 0;

		open($in, "$hpacucli controller slot=$slot show |") || warn "exec $hpacucli: $!";
		$debug_storage .= ">>> $hpacucli controller slot=$slot show\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^\s*Read Cache Size: (.*)/i) {
				$props{$dev}{'cache-read'} = parse_bytes($1, 1024);
			} elsif ($line =~ /^\s*Write Cache Size: (.*)/i) {
				$props{$dev}{'cache-write'} = parse_bytes($1, 1024);
			} elsif ($line =~ /^\s*Total Cache Size: (.*)/i) {
				$total_cache_size = parse_bytes($1, 1024);
			} elsif ($line =~ /^\s*Accelerator Ratio: (\d+)/i) {
				$ratio = $1;
			} elsif ($line =~ /^\s*Battery Pack Count: (\d+)/i) {
				$props{$dev}{'BBU'} = 1 if ($1);
			} elsif ($line =~ /^\s*Firmware Version: (.*)/i) {
				$props{$dev}{'firmware'} = $1;
			} elsif ($line =~ /^\s*Hardware Revision: (.*)/i) {
				$props{$dev}{'revision'} = $1;
			} elsif ($line =~ /^\s*Serial Number: (.*)/i) {
				$props{$dev}{'serial'} = $1;
			} elsif ($line =~ /^\s*Cache Status: (.*)/i) {
				$props{$dev}{'cache-status'} = ($1 eq 'OK') ? 'on' : 'off';
			}
		}
		close($in);

		if ($total_cache_size) {
			$props{$dev}{'cache-read'} = ($ratio / 100) * $total_cache_size;
			$props{$dev}{'cache-write'} = ((100 - $ratio) / 100) * $total_cache_size;
		}

		my(%ids, $id);
		my($array) = "NA";

		open($in, "$hpacucli controller slot=$slot logicaldrive all show |") || warn "exec $hpacucli: $!";
		$debug_storage .= ">>> $hpacucli controller slot=$slot logicaldrive all show\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /array\s+(\S+)/i) {
				$array = $1;
			} elsif ($line =~ /logicaldrive\s+(\d+)\s+\(([^,]*),([^,]*)/) {
				$ids{$1} = $array;
			}
		}
		close($in);

		foreach $id (keys %ids) {
			my($volume, $array, $raid, $size, $stripe, $cache_enable, $status, $uid) = ("ciss-s$slot-$ids{$id}-l$id", $ids{$id}, 0, 0, 0, 'NA', '');

			open($in, "$hpacucli controller slot=$slot logicaldrive $id show |") || warn "exec $hpacucli: $!";
			$debug_storage .= ">>> $hpacucli controller slot=$slot logicaldrive $id show\n";
			while ($line = <$in>) {
				$debug_storage .= $line;
				chomp($line);
				if ($line =~ /^\s*array\s+(\S+)/) {
					$array = $1;
				} elsif ($line =~ /^\s*Size:\s+(.*)/) {
					$size = parse_bytes($1, 1024);
				} elsif ($line =~ /^\s*Fault Tolerance:\s+(.*)/) {
					$raid = canon_raid("RAID $1");
				} elsif ($line =~ /^\s*Array Accelerator:\s+(.*)/) {
					$cache_enable = parse_enable($1);
				} elsif ($line =~ /^\s*Stripe Size:\s+(.*)/) {
					$stripe = parse_bytes($1, 1024);
				} elsif ($line =~ /^\s*Status:\s+(.*)/) {
					$status = parse_volume_status($1);
				} elsif ($line =~ /^\s*UID:\s+(.*)/) {
					$uid = $1;
				} elsif ($line =~ /^\s*Disk Name:\s+(\S+)/) {
					if ($1 ne 'Unknown') {
						$volume = $1;
						$volume =~ s/^\/dev\///;
					}
				}
			}
			close($in);

			$volumes{$volume}++;
			$logicals{$slot}{$array}{$id} = $volume;
			$props{$volume}{bytes} = $size if ($size);
			$props{$volume}{raid} = $raid if ($raid);
			$props{$volume}{stripe} = $stripe if ($stripe);
			$props{$volume}{status} = $status if ($status);
			$props{$volume}{cache_enable} = $cache_enable;
			$props{$volume}{uid} = $uid;
			$arrays{$slot}{$array}++;
		}

		$array = "NA";

		open($in, "$hpacucli controller slot=$slot physicaldrive all show |") || warn "exec $hpacucli: $!";
		$debug_storage .= ">>> $hpacucli controller slot=$slot physicaldrive all show\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /array\s+(\S+)/i) {
				$array = $1;
			} elsif ($line =~ /physicaldrive\s+(\S+)/) {
				$physicals{$slot}{$array}{$1}++;
			}
		}
		close($in);

		foreach $array (keys %{$arrays{$slot}}) {
			my($volume);
			my(@keys) = keys %{$logicals{$slot}{$array}};
			if ($#keys == 0) {
				# if only one logical in the array, then logical == array
				# so set the volume to the logical, otherwise we'll have an anonymous array volume
				$volume = $logicals{$slot}{$array}{$keys[0]};
			} else {
				# create anonymoums array volume
				$volume = "ciss-s$slot-$array";
				$volumes{$volume}++;
				$props{$volume}{raid} = "JBOD";
				$props{$volume}{bytes} = 0;
			}
			my($have_size) = $props{$volume}{bytes};
			my($drive);
			foreach $drive (keys %{$physicals{$slot}{$array}}) {
				my($size, $rpm, $speed, $interface, $status, $firmware, $serial) = (0, "", "", "", "", "", "");
				my($model) = "unknown";

				open($in, "$hpacucli controller slot=$slot physicaldrive $drive show |") || warn "exec $hpacucli: $!";
				$debug_storage .= ">>> $hpacucli controller slot=$slot physicaldrive $drive show\n";
				while ($line = <$in>) {
					$debug_storage .= $line;
					chomp($line);
					if ($line =~ /Size:\s+(.*)/i) {
						$size = parse_bytes($1, 1000);
					} elsif ($line =~ /Rotational Speed:\s+(.*)/i) {
						$rpm = parse_rpm($1);
					} elsif ($line =~ /Transfer Speed:\s+(.*)/i) {
						$speed = parse_drive_speed($1);
					} elsif ($line =~ /Status:\s+(.*)/i) {
						$status = parse_drive_status($1);
					} elsif ($line =~ /Interface Type:\s+(.*)/i) {
						$interface = parse_drive_interface($1);
					} elsif ($line =~ /Serial Number:\s+(.*)/i) {
						$serial = $1;
					} elsif ($line =~ /Firmware Revision:\s+(.*)/i) {
						$firmware = $1;
					} elsif ($line =~ /Model:\s+(.*)/i) {
						$model = canon_drive($1);
					}
				}
				close($in);

				if ($status eq "Failed") {
					$props{$volume}{failed}++;
				}

				if ($model eq "unknown") {
					$model = "Unknown";
					$model .= " " . print_bytes($size, 1000, 0) if ($size);
					$model .= " $rpm" if ($rpm);
					$model .= " $interface" if ($interface);
					$model .= " $speed" if ($speed);
				}
				$drives{$volume}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{volume} = $volume;
				$props{drives}{$drive_id}{firmware} = $firmware if ($firmware);
				$props{drives}{$drive_id}{serial} = $serial if ($serial);
				$props{drives}{$drive_id}{rpm} = $rpm if ($rpm);
				$props{drives}{$drive_id}{size} = $size if ($size);
				$props{drives}{$drive_id}{speed} = $speed if ($speed);
				$props{drives}{$drive_id}{interface} = $interface if ($interface);
				$props{drives}{$drive_id}{status} = $status if ($status);
				$drive_id++;
				$props{$volume}{bytes} += $size unless ($have_size);
			}
		}
		$count++;
	}
}

sub cissutil {
	my(%cissdrives);
	my($device, $dev);
	my(%failed);

	system("which $cissutil > /dev/null 2>&1");
	return if ($?);
	$have_cissutil = 1;
	$debug_storage .= ">> $cissutil\n";

	foreach $device (split(' ', `/bin/ls /dev/ciss[0-9]* 2> /dev/null`)) {
		$dev = $device;
		$dev =~ s,^/dev/ciss,,;
		my($volume) = "NA";
		my(@controllers);
		my($controller) = 0;

		# if only one volume, wiring is easy
		my($default_volume) = '';
		if (defined $controller_volumes{"ciss$dev"}) {
			my(@keys) = keys %{$controller_volumes{"ciss$dev"}};
			if ($#keys == 0) {
				$default_volume = $keys[0];
				delete $drives{$default_volume};	# nuke camcontrol info
			}
		}

		open($in, "$cissutil -c $device -d 2> /dev/null |") || warn "exec on \"$cissutil -c $device -d\" failed";
		$debug_storage .= ">>> $cissutil -c $device -d\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /^controller\s+(\d+)\s*(\((.*)\))?/) {
				$controller = $1;
				my($type) = $3; 
				if ($type && !$other_devices{"Disk-Control"}{"ciss$dev"}) {
					$type =~ s/\s+/ /g;
					$type =~ s/^\s*//;
					$type =~ s/\s*$//;
					$other_devices{"Disk-Control"}{"ciss$dev"} = "ciss$dev: $type";
				}
			} elsif ($line =~ /^running firmware\s+([\d\.]+)/i) {
				$props{"ciss$dev"}{"firmware"} = $1;
			} elsif ($line =~ /^cache is:\s+(.*)/i) {
				$props{"ciss$dev"}{'cache-status'} = ($1 =~ /enable/i) ? 'on' : 'off';
			} elsif ($line =~ /^write cache size = (.*)/i) {
				$props{"ciss$dev"}{'cache-write'} = parse_bytes($1, 1024);
			} elsif ($line =~ /^read cache size = (.*)/i) {
				$props{"ciss$dev"}{'cache-read'} = parse_bytes($1, 1024);
			} elsif ($line =~ /^logical drives:\s+(\d+)/) {
				push(@controllers, $controller);
			} elsif ($line =~ /^(da\d+)\s*$/) {
				$volume = $1;
				delete $drives{$volume};	# nuke camcontrol info
				$volumes{$volume}++;
			} elsif ($line =~ /^vol(\d+)\s*$/) {
				$volume = ($default_volume || "ciss$dev-c$controller-l$1");
				$volumes{$volume}++;
			} elsif ($line =~ /^(vol(\d+))?\s+blocks\s+available\s+(\S+)/) {
				$volume = ($default_volume || "ciss$dev-c$controller-l$2") if $1;
				$volumes{$volume}++;
				$props{$volume}{bytes} = $3 * 512;
			} elsif ($line =~ /^(vol(\d+))?\s+fault\s+tolerance\s+(\S+)/) {
				$volume = ($default_volume || "ciss$dev-c$controller-l$2") if $1;
				$props{$volume}{raid} = $3;
			} elsif ($line =~ /^(vol(\d+))?\s+status\s+(.*)/) {
				$volume = ($default_volume || "ciss$dev-c$controller-l$2") if $1;
				$props{$volume}{status} = parse_volume_status($3);
			} elsif ($line =~ /^(vol(\d+))?\s+stripe\s+size\s+(\S+)/) {
				$volume = ($default_volume || "ciss$dev-c$controller-l$2") if $1;
				$props{$volume}{stripe} = parse_bytes($3, 1024);
			} elsif ($line =~ /^(vol(\d+))?\s+failed\s+drives\s+\(([^\)]+)\)/) {
				$volume = ($default_volume || "ciss$dev-c$controller-l$2") if $1;
				my($failed) = $3;
				$failed =~ s/\bnone\b//g;
				my(@failed) = split(/,/, $failed);
				$props{$volume}{failed} = $#failed + 1;
				%failed = map { $_ => 1 } @failed;
			} elsif ($line =~ /^vol(\d+)\s+.*\s+(drives|spares)\s+\(([^\)]+)\)/) {
				$volume = ($default_volume || "ciss$dev-c$controller-l$1");
				my($drive);
				foreach $drive (split(/,/, $3)) {
					$cissdrives{"$dev-$controller-$drive"} = $volume;
				}
			}
		}
		close($in);
	
		foreach $controller (@controllers) {
			my($id) = 0;
			$volume = "ciss$dev-c$controller-NA";

			open($in, "$cissutil -c $device --controller $controller --drives 2> /dev/null |") || warn "exec on $cissutil failed";
			$debug_storage .= ">>> $cissutil -c $device --controller $controller --drives\n";
			while ($line = <$in>) {
				$debug_storage .= $line;
				chomp($line);
				$got_cissutil = 1;
				if ($line =~ /^\((b\d+t\d+) -> (da\d+)/) {
					$volume = $2;
					$ndrives++;
					$id = $drive_id++;
					$props{drives}{$id}{status} = parse_drive_status("failed") if ($failed{$1});
				} elsif ($line =~ /^\(b\d+t\d+ -> free/) {
					$volume = "ciss$dev-c$controller-NA";
					$volumes{$volume}++;
					$ndrives++;
					$id = $drive_id++;
				} elsif ($line =~ /^\((b(\d+)t(\d+))\)/) {
					my($full, $bus, $target) = ($1, $2, $3);
					if ($cissdrives{"$dev-$controller-$full"}) {
						$volume = $cissdrives{"$dev-$controller-$full"};
					} else {
						$volume = "ciss$dev-c$controller-NA";
						$volumes{$volume}++;
					}
					$ndrives++;
					$id = $drive_id++;
				} elsif ($line =~ /^\(b\d+t\d+ -> vol(\d+)/) {
					$volume = ($default_volume || "ciss$dev-c$controller-l$1");
					$ndrives++;
					$id = $drive_id++;
				} elsif ($line =~ /^\s+model\s+(.*)/) {
					my(@model) = split(' ', $1);
					my($model);
					if ($model[0] eq "Unknown" || $model[0] eq "ATA") {
						$model = canon_drive("$model[1],$model[2]");
						$props{drives}{$id}{serial} = $model[3];
						$props{drives}{$id}{firmware} = $model[4];
					} else {
						$model = canon_drive("$model[0],$model[1]");
						$props{drives}{$id}{serial} = $model[2];
						$props{drives}{$id}{firmware} = $model[3];
					}
					$drives{$volume}{$id}++;
					$props{drives}{$id}{model} = $model;
					$props{drives}{$id}{volume} = $volume;
				} elsif ($line =~ /^\s+configured:\s+(\d+)/) {
					$props{$volume}{free}++ unless ($1);
				} elsif ($line =~ /^\s+configured_spare:\s+(\d+)/) {
					$props{$volume}{spare}++ if ($1);
				} elsif ($line =~ /^\s+cache_currently_enabled:\s+(\d+)/) {
					$props{drives}{$id}{wcache_enable} = $1;
				} elsif ($line =~ /^\s+rpm:\s+(\d+)/) {
					$props{drives}{$id}{rpm} = $1;
				} elsif ($line =~ /^\s+size\s+(.*)/) {
					$props{drives}{$id}{size} = parse_bytes($1, 1024);
				} elsif ($line =~ /^\s+SMART:\s+(.*)/) {
					$props{drives}{$id}{smart_support} = parse_enable($1);
				} elsif ($line =~ /^\s+SMART_errors_enabled:\s+(.*)/) {
					$props{drives}{$id}{smart_enable} = parse_enable($1);
				} elsif ($line =~ /^\s+drive_present:\s+(.*)/) {
					if ($1) {
						$props{drives}{$id}{status} = parse_drive_status("online") unless (defined($props{drives}{$id}{status}));
					} else {
						$props{drives}{$id}{status} = parse_drive_status("offline");
					}
				}
			}
			close($in);
		}
	}
}

sub camcontrol {
	my($dev);

	system("which $camcontrol > /dev/null 2>&1");
	return if ($?);

	open($in, "$camcontrol devlist -v 2> /dev/null |") || warn "exec $camcontrol: $!";
	$debug_storage .= ">> $camcontrol devlist -v\n";
	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);
		if ($line =~ /^\<(\S+)\s+(\S+)/) {
			if ($1 eq "COMPAQ") {
				$want_cissutil++;
			} elsif ($1 eq "PE/PV") {
				$want_megarc++;
			}
		}
		if ($line =~ /^scbus[-\d]+\s+on\s+(\S+)\s+bus\s+\d+/) {
			$dev = $1;
		}
		if ($line =~ /\b(da\d+)\b/) {
			my($volume) = $1;
			if ($line =~ /^\<([^>]*)/) {
				my($id) = canon_drive($1);
				$ndrives++;
				$volumes{$volume}++;
				$drives{$volume}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $id;
				$props{drives}{$drive_id}{volume} = $volume;
				$drive_id++;
				if ($dev) {
					$props{$volume}{device} = $dev;
					$controller_volumes{$dev}{$volume}++;
				}
				if ($line =~ /(raid\s*\S+)/i) {
					$props{$volume}{raid} = $1;
				} else {
					$props{$volume}{raid} = "JBOD";
				}
				if ($dmesg_buf =~ /^$volume:.*?(\d+) (512 byte )?sectors/m) {
					$props{$volume}{bytes} = $1 * 512;
				}
			}
		}
	}
	close($in);
}

sub grok_sysctl_dev {
	my($in) = 'IN';
	my($line);
	my($dev, $key, $val);

	open($in, "sysctl dev |") || warn("sysctl: $!");
	while ($line = <$in>) {
		$debug_sysctl_dev .= $line;
		chomp($line);
		if ($line =~ /^dev\.([^\.]+)\.(\d+)\.\%?([^\:]+)\:\s+(.*)/) {
			($dev, $key, $val) = ($1 . $2, $3, $4);
			$sysctl_dev{$dev}{$key} = $val;
		}
	}
	foreach $dev (keys %sysctl_dev) {
		my($model) = $sysctl_dev{$dev}{desc};
		$model =~ s/\(R\)//g if $model;
		if ($dev =~ /^(atapci|mfi|mpt|ahc|ahd|asr|aac|aacch|sym|ida|ciss|amr)\d+$/) {
			$model =~ s/\s+controller\b.*//i;
			$other_devices{'Disk-Control'}{$dev} = "$dev: $model";
			$sysctl_dev{$dev}{'xmodel'} = $model;
		} elsif ($dev =~ /^(ata|amrd|mfid)\d+$/) {
			$props{$dev}{device} = $sysctl_dev{$dev}{parent};
		} elsif ($dev =~ /^($freebsd_net_devs)\d+$/) {
			if ($1 eq "msk") {
				$interface_to_driver{$dev} = $sysctl_dev{$dev}{parent};
			}
			$model =~ s/\s*(Ethernet|Network|,).*//;
			$interfaces{$dev}{'model'} = $model;
			$sysctl_dev{$dev}{'xmodel'} = $model;
		} else {
			$sysctl_dev{$dev}{'xmodel'} = $model;
		}
	}
}

sub parse_protocol {
	my($proto) = @_;
	my($result);
	if ($proto eq "Serial ATA v1.0" || $proto eq "SATA-I signaling") {
		$result = "SATA-1.0";
	} elsif ($proto eq "Serial ATA II" || $proto eq "SATA-II signaling") {
		$result = "SATA-2.0";
	} elsif ($proto =~ /ATA\/ATAPI revision\s+(\d+)/i) {
		$result = "ATA-$1";
	} else {
		$result = $proto;
		$result =~ s/ATA\/ATAPI/ATA/;
	}
	return $result;
}

sub atacontrol {
	my($in, $id, $disk) = @_;
	my($line);

	while ($line = <$in>) {
		$debug_storage .= $line;
		chomp($line);

		if ($line =~ /device (ad\d+):/) {
			$disk = $1;
		} elsif ($line =~ /^device model\s+(.*)/) {
			my($model) = canon_drive($1);
			$ndrives++;
			$volumes{$disk}++;
			$drives{$disk}{$id}++;
			$props{drives}{$id}{model} = $model;
			$props{drives}{$id}{volume} = $disk;
			$drive_id++;
			$props{$disk}{raid} = "JBOD";
		} elsif ($line =~ /^lba(48)? (not )?supported\s+(\d+) sectors/) {
			my($size) = $3 * 512;
			$props{drives}{$id}{size} = $size;
			if (!$props{$disk}{bytes} || $size > $props{$disk}{bytes}) {
				$props{$disk}{bytes} = $size;
			}
		} elsif ($line =~ /^Protocol\s+(.*)/) {
			$props{drives}{$id}{interface} = parse_protocol($1);
		} elsif ($line =~ /^(ATA\/ATAPI revision\s*\d+)/) {
			$props{drives}{$id}{interface} = parse_protocol($1);
		} elsif ($line =~ /^serial number\s+(.*)/) {
			$props{drives}{$id}{serial} = $1;
		} elsif ($line =~ /^firmware revision\s+(.*)/) {
			$props{drives}{$id}{firmware} = $1;
		} elsif ($line =~ /^read ahead\s+(\S+)\s+(\S+)/) {
			$props{drives}{$id}{readahead_support} = parse_enable($1);
			$props{drives}{$id}{readahead_enable} = parse_enable($2);
		} elsif ($line =~ /^write cache\s+(\S+)\s+(\S+)/) {
			$props{drives}{$id}{wcache_support} = parse_enable($1);
			$props{drives}{$id}{wcache_enable} = parse_enable($2);
		} elsif ($line =~ /^SMART\s+(\S+)\s+(\S+)/) {
			$props{drives}{$id}{smart_support} = parse_enable($1);
			$props{drives}{$id}{smart_enable} = parse_enable($2);
		} elsif ($line =~ /^power management\s+(\S+)\s+(\S+)/) {
			$props{drives}{$id}{pm_support} = parse_enable($1);
			$props{drives}{$id}{pm_enable} = parse_enable($2);
		} elsif ($line =~ /^advanced power management\s+(\S+)\s+(\S+)(\s+(\S+))?/) {
			$props{drives}{$id}{apm_support} = parse_enable($1);
			$props{drives}{$id}{apm_enable} = parse_enable($2);
			$props{drives}{$id}{apm_value} = parse_value($4) if ($3);
		} elsif ($line =~ /^automatic acoustic management\s+(\S+)\s+(\S+)(\s+(\S+))?/) {
			$props{drives}{$id}{aam_support} = parse_enable($1);
			$props{drives}{$id}{aam_enable} = parse_enable($2);
			$props{drives}{$id}{aam_value} = parse_value($4) if ($3);
		} elsif ($line =~ /^Native Command Queuing \(NCQ\)\s+(\S+)\s+(\S+)(\s+(\S+))?/) {
			$props{drives}{$id}{ncq_support} = parse_enable($1);
			$props{drives}{$id}{ncq_enable} = parse_enable($2, '', '-');
			$props{drives}{$id}{ncq_depth} = parse_value($4) if ($3);
		}
	}
}

sub freebsd_disks_unknown {
	my($sysctl) = `sysctl -n kern.disks 2> /dev/null` || dmesg_disks();
	chomp($sysctl);
	my($disk);
	foreach $disk (split(' ', $sysctl)) {
		next if ($disk =~ /^(md|cd)\d+$/);
		next if ($drives{$disk});

		$ndrives++;
		$volumes{$disk}++;
		$drives{$disk}{$drive_id}++;
		$props{drives}{$drive_id}{model} = "Unknown";
		$props{drives}{$drive_id}{volume} = $disk;
		$drive_id++;
	}
}

sub freebsd_disks {
	my($ac_works) = 1;

	# do this before things like cissutil, megarc (let them override this with more detailed info)
	camcontrol();

	my($which);
	chomp($which = `which $atacontrol` || 0);
	if ($?) {
		$ac_works = 0;
	} else {
		if (($yroot || $jailed) && $os_version < 500000000 && $parent_os !~ /^4/) {
			# 4.x atacontrol hangs on a 6.x kernel
			if (`file $which` =~ /FreeBSD 4/) {
				$ac_works = 0;
			}
		}
	}

	$debug_storage .= ">> $atacontrol\n";

	if ($ac_works) {
		# this works with freebsd 4
		my($i, $j);
		for ($i=0; $i<4; $i++) {
			for ($j=0; $j<2; $j++) {
				open($in, "$atacontrol cap $i $j 2> /dev/null |") || warn "exec on $atacontrol failed";
				$debug_storage .= ">>> $atacontrol cap $i $j\n";
				atacontrol($in, $drive_id, "ata-$i,$j");
				close($in);
			}
		}
	}

	my($grep) = "GREP";
	my($gline);

	my($sysctl) = `sysctl -n kern.disks 2> /dev/null` || dmesg_disks();
	chomp($sysctl);

	my($disk);
	foreach $disk (split(' ', $sysctl)) {
		next if ($disk =~ /^(md|cd)\d+$/);
		next if ($drives{$disk});

		if ($ac_works) {
			# this works with freebsd 6+
			open($in, "$atacontrol cap $disk 2> /dev/null |") || warn "exec $atacontrol: $!";
			$debug_storage .= ">>> $atacontrol cap $disk\n";
			atacontrol($in, $drive_id, $disk);
			close($in);

			next if ($drives{$disk});

			if ($disk =~ /^ar\d+$/) {
				my($model) = "";
				my($sep) = "partitions: ";
				open($in, "$atacontrol status $disk 2> /dev/null |") || warn "exec $atacontrol: $!";
				$debug_storage .= ">>> $atacontrol status $disk\n";
				while ($line = <$in>) {
					$debug_storage .= $line;
					chomp($line);
					if ($line =~ /(RAID\d+).*subdisks: (.*) status:/) {
						my($raid, $subs) = ($1, $2);
						my($sub);
						foreach $sub (split(' ', $subs)) {
							$model .= "$sep$sub";
							$sep = ", ";
						}
						$props{$disk}{raid} = $raid;
					} elsif ($line =~ /(RAID\d+)( stripesize=(\d+))? status:\s*(\S+)/) {
						my($raid, $stripe, $status) = ($1, $3, $4);
						$props{$disk}{raid} = $raid;
						$props{$disk}{stripe} = $stripe * 512 if $stripe;
						$props{$disk}{status} = parse_volume_status($status);
					} elsif ($line =~ /^\s*(\d+)\s+(ad\d+)\s+ONLINE/) {
						$model .= "$sep$2";
						$sep = ", ";
					} elsif ($line =~ /^\s*(\d+)\s+\-+\s+MISSING/) {
						$model .= "${sep}down";
						$sep = ", ";
					}
				}
				if ($model) {
					$ndrives++;
					$volumes{$disk}++;
					$drives{$disk}{$drive_id}++;
					$props{drives}{$drive_id}{model} = $model;
					$props{drives}{$drive_id}{volume} = $disk;
					$drive_id++;
					if ($dmesg_buf =~ /^$disk: (\d+)MB/m) {
						$props{$disk}{bytes} = $1 * 1024 * 1024;
					}
				}
			}
			next if ($drives{$disk});
		}
	
		foreach $gline (split(/\n/, $dmesg_buf)) {
			if ($gline =~ /(\s*\d+\s+READY\s+)?\(?\b$disk\)?: ((\d+)MB\s*)?\<([^\>]*)\>(\s+on\s+(\S+))?/i && !$volumes{$disk}) {
				my($size) = $3;
				my($model) = canon_drive($4);
				my($dev) = $6;
				if (defined($sysctl_dev{$disk})) {
					# on freebsd 6.x model/dev should be more accurate from sysctl
					$model = canon_drive($sysctl_dev{$disk}{desc});
					$dev = $sysctl_dev{$disk}{parent};
				}
				$ndrives++;
				$volumes{$disk}++;
				$drives{$disk}{$drive_id}++;
				$props{drives}{$drive_id}{model} = $model;
				$props{drives}{$drive_id}{volume} = $disk;
				$drive_id++;
				if ($size) {
					$props{$disk}{bytes} = $size * 1024 * 1024;
				}
				if ($gline =~ /\b(raid\s*\S+)/i) {
					$props{$disk}{raid} = $1;
				} else {
					$props{$disk}{raid} = "JBOD";
				}
				if ($dev) {
					$props{$disk}{device} = $dev;
					$controller_volumes{$dev}{$disk}++;
					if ($dev =~ /^twe(\d+)/) {
						my($ctrl) = $1;
						if ($gline =~ /\b$disk: <Unit (\d+)/) {
							$logical_to_os{'tw'}{"c$ctrl-u$1"} = $disk;
						}
					}
				}
			} elsif ($gline =~ /^$disk:.*?(\d+) (512 byte )?sectors/) {
				$props{$disk}{bytes} = $1 * 512;
				if ($gline =~ /(raid\s*\S+)/i) {
					$props{$disk}{raid} = $1;
				}
			}
		}
		next if ($drives{$disk});
	
		# on freebsd 6.x the following should work.  do this after dmesg check as it should have more info.
		if (defined($sysctl_dev{$disk})) {
			my($model) = canon_drive($sysctl_dev{$disk}{desc});
			my($dev) = $sysctl_dev{$disk}{parent};
			$ndrives++;
			$volumes{$disk}++;
			$drives{$disk}{$drive_id}++;
			$props{drives}{$drive_id}{model} = $model;
			$props{drives}{$drive_id}{volume} = $disk;
			$drive_id++;
			$props{$disk}{device} = $dev;
			$controller_volumes{$dev}{$disk}++;
		}
	}
	
	if ($ac_works) {
		# wire ATA drives
		my($channel);
		open($in, "$atacontrol list 2> /dev/null |") || warn "exec on $atacontrol failed";
		$debug_storage .= ">>> $atacontrol list\n";
		while ($line = <$in>) {
			$debug_storage .= $line;
			chomp($line);
			if ($line =~ /ATA\s+channel\s+(\d+)/i) {
				$channel = $1;
			} elsif ($line =~ /^\s*(master|slave):\s*(ad\d+)/i) {
				$props{$2}{channel} = "ata$channel";
				$props{$2}{ms} = lc($1);
			}
		}
		close($in);
	}

	# wire from dmesg if we haven't done it yet
	foreach $gline (split(/\n/, $dmesg_buf)) {
		if ($gline =~ /^(ad\d+):.*\s+at\s+(\S+)-(slave|master)/i) {
			if (!defined($props{$1}{channel})) {
				$props{$1}{channel} = $2;
				$props{$1}{ms} = $3;
			}
		} elsif ($gline =~ /^(ata\d+):.*\s+on\s+(ata\S+)/i) {
			$props{$1}{device} = $2 unless $props{$1}{device};
		}
	}
}

# this should probably be elsewhere, but do it last for now so that we always use this if we have it.
# should be very accurate.  certainly more than dmesg output which can get garbled.
sub freebsd_disk_ioctls {
	my($disk);
	my($sysctl) = `sysctl -n kern.disks 2> /dev/null` || dmesg_disks();
	chomp($sysctl);

	foreach $disk (split(' ', $sysctl)) {
		my($dev) = "/dev/$disk";
		if (-e $dev && open($in, $dev)) {
			my($status);
			my($bytes);
			if ($os_version >= 600000000) {
				$bytes = pack("I2", 0, 0);
				$status = ioctl($in, 0x40086481, $bytes);
				if ($status) {
					my(@bytes) = unpack("I2", $bytes);
					$props{$disk}{bytes} = $bytes[0] + $bytes[1] * 4*1024*1024*1024;
				}
			} elsif ($os_version < 500000000) {
				my($rdev) = (stat($dev))[6];
				my($offset) = round(148 + (16 * ($rdev & 0x07)));
				$bytes = pack("C276", 0);
				$status = ioctl($in, 0x41146465, $bytes);
				if ($status) {
					my(@bytes) = unpack("C${offset}I", $bytes);
					$props{$disk}{bytes} = $bytes[-1] * 512;
				}
			}
			close($in);
		}
	}
}

my(%grub);

sub grok_grub {
	my($conf);
	if (-e $grub) {
		$conf = `cat $grub 2> /dev/null`;
		if ($conf =~ /vmlinuz-$os_version\s.*maxcpus=(\d+)/m) {
			$grub{'maxcpus'} = $1;
		}
	}
}

sub grok_os {
	$os = "";
	$os_up = 0;
	chomp($os = `uname -s`);
	$os_type = lc($os);
	chomp($os .= " " . `uname -r`);
	chomp($os .= " " . `uname -m`);
	if ($os_type eq "freebsd") {
		my($config);
		chomp($config = `uname -v`);
		$config =~ s/.*\///;
		$config =~ s/\s+$//;
		$os .= " " . $config;
		chomp($os_version = `uname -r`);
		if ($os_version =~ /^(\d+)\.\d+-.*(-(\d+))?$/) {
			# $2 is null if stock FreeBSD
			$os_date = $2 ? $3 : "00000000";
			$os_version = "$1$os_date";
		}
		chomp($jailed = `sysctl -n security.jail.jailed 2> /dev/null` || -e "/var/run/yroot-jail.pid" || 0);
		chomp($parent_os = `sysctl -n kern.osrelease 2> /dev/null` || 0);
		if ($jailed) {
			my($host, $key);
			chomp($host = `cat /.yroot_host 2> /dev/null` || 0);
			chomp($key = `cat /etc/ssh/ssh_host_key.pub 2> /dev/null` || 0);
			if ($host) {
				$jailer = $host;
			} elsif ($key =~ /\@(\S+)/) {
				$jailer = $1;
			}
		}
		my($smp);
		# check to see if the sysctl name exists in 4.x or check value in 6.x
		chomp($smp = `sysctl -N machdep.smp_active 2> /dev/null` || `sysctl -n kern.smp.active 2> /dev/null` || 0);
		$os_up = 1 unless ($smp);
	} elsif ($os_type eq "linux") {
		chomp($os_version = `uname -r`);
		if (-e "/etc/redhat-release") {
			# http://kbase.redhat.com/faq/FAQ_43_4422.shtm
			chomp($os_distro = `cat /etc/redhat-release`);
			$os_distro =~ s/Red Hat Enterprise Linux/RHEL/;
			$os_distro =~ s/\s+release\s+/ /;
			$os_distro =~ s/\(.*Update (\d+)\)/U$1/;
		} elsif (-e "/etc/debian_version") {
			chomp($os_distro = `cat /etc/debian_version`);
			$os_distro = "Debian $os_distro";
		} elsif (-e "/etc/gentoo-release") {
			chomp($os_distro = `cat /etc/gentoo-release`);
			$os_distro =~ s/\s+Base System version\s+/ /;
		}
		if ($os_distro) {
			$os = "$os_distro, $os";
		}
	}
	if (-l "/.yroot") {
		my($link) = readlink("/.yroot");
		$link =~ s,^.*/var/yroots/,,;
		$yroot = $link;
	}
}

sub scsi_to_device {
	my($dev) = @_;
	if ($dev =~ /^scsi(\d+)$/) {
		my($id) = $1;
		if ($dmesg_buf =~ /\bscsi\(?$id\)?\s*:.*IRQ[:= ]\s*(\d+)/im) {
			$dev = $irq_to_driver{$1} if ($irq_to_driver{$1});
		}
	}
	return $dev;
}

sub cmp_disks {
	my($atype, $anum) = ($a =~ /^(.*?)(\d*)$/);
	my($btype, $bnum) = ($b =~ /^(.*?)(\d*)$/);
	if ($os_type eq "linux" && $atype =~ /^sd/ && $btype =~ /^sd/) {
		return (length($atype) cmp length($btype) || $atype cmp $btype);
	} else {
		return ($atype cmp $btype || $anum <=> $bnum);
	}
}

sub print_disks {
	my($volume);
	foreach $volume (sort cmp_disks keys %volumes) {
		next if ($volume =~ /^ata-/);
		next unless ($drives{$volume} || $props{$volume}{bytes});
		next if ($props{$volume}{hidden});
		next if ($volume =~ /^ar\d+$/ && !defined($props{$volume}{df}));

		my($out) = "$volume";
		my($out_dev) = "";
		if ($props{$volume}{channel}) {
			my($channel) = $props{$volume}{channel};
			my($dev) = $props{$channel}{device};
			if ($dev) {
				$dev = $pci_to_driver{$dev} || $hostid_to_driver{$dev} || $dev;
				$dev = scsi_to_device($dev);
				$out_dev .= "$dev:";
			}
			$out_dev .= "$props{$volume}{channel}-" . substr($props{$volume}{ms}, 0, 1);
		} elsif ($props{$volume}{device}) {
			my($dev) = $props{$volume}{device};
			$dev = $pci_to_driver{$dev} || $hostid_to_driver{$dev} || $dev;
			$dev = scsi_to_device($dev);
			$out_dev .= $dev;
			$out_dev .= "-" . substr($props{$volume}{'ms'}, 0, 1) if $props{$volume}{'ms'};
		} elsif ($dmesg_buf =~ /^$volume at (\S+)/m) {
			$out_dev .= $1;
		} elsif ($dmesg_buf =~ /^$volume: <.*> on (\S+)/m) {
			$out_dev .= $1;
		}

		next if ($out_dev =~ /^umass/);

		$out .= " ($out_dev)" if ($out_dev);
		$out .= ":";

		if ($props{$volume}{bytes}) {
			$out .= " " . print_bytes($props{$volume}{bytes}, 1000, 0);
		} elsif ($props{$volume}{df}{size}) {
			$out .= " " . print_bytes($props{$volume}{df}{size}, 1000, 0);
		}
		$out .= sprintf(" (%d%%)", $props{$volume}{df}{used} * 100 / ($props{$volume}{df}{used} + $props{$volume}{df}{avail})) if (($props{$volume}{df}{used} || $props{$volume}{df}{avail}) && !defined($props{$volume}{inmd}));
		my($raid) = canon_raid($props{$volume}{raid});
		$out .= " $raid" if ($raid);

# this output is meant for debugger, will hopefully use it later in real output
#		$out .= sprintf(" (stripe %s)", print_bytes($props{$volume}{stripe}, 1024, 1)) if ($props{$volume}{stripe});

		if ($props{$volume}{spare} || $props{$volume}{free} || $props{$volume}{failed} || $props{$volume}{rebuild} || $props{$volume}{missing}) {
			$out .= " (";
			my($sep) = "";
			if ($props{$volume}{spare}) {
				$out .= "$sep$props{$volume}{spare} spare";
				$sep .= ", ";
			}
			if ($props{$volume}{free}) {
				$out .= "$sep$props{$volume}{free} free";
				$sep .= ", ";
			}
			if ($props{$volume}{failed}) {
				$out .= "$sep$props{$volume}{failed} failed";
				$sep .= ", ";
			}
			if ($props{$volume}{rebuild}) {
				$out .= "$sep$props{$volume}{rebuild} rebuild";
				$sep .= ", ";
			}
			if ($props{$volume}{missing}) {
				$out .= "$sep$props{$volume}{rebuild} missing";
				$sep .= ", ";
			}
			$out .= ")";
		}
		$output .= "Disk:\t\t";
		$output .= $out;
		my($key);
		my(%pretty);
		foreach $key (keys %{$drives{$volume}}) {
			my($try);
			my($found) = 0;
			my($model) = $props{drives}{$key}{model};
			# sort by length to find longest match, e.g. some drives just differ in suffix
			foreach $try (sort { length($b) <=> length($a) } keys %drive_models) {
				if ($model =~ /$try/i) {
					$pretty{$drive_models{$try}}++;
					$found++;
					last;
				}
			}
			if (!$found) {
				unless ($model =~ /^($ignore_disk_models)/i) {
					$debug .= "Debug-Disk:\t$model\n";
				}
				if ($model =~ /^((COMPAQ|HP)-)?(BD|BF|DD|DF|DG|DH|FB|FJ)([0-9]{3})([0-9A-Z]{5})/ || $model =~ /^((ATA)-)?(GB)([0-9]{4})([0-9A-Z]{5})/) {
					my($type, $size, $seq) = ($3, $4, $5);
					my($str);
					$size =~ s/^0+//;
					$str = $size . "GB ";
					if ($type eq "BD") {
						$str .= "10K ";
						$str .= "U320 " if (hex($seq) >= 0x85A24);
					} elsif ($type eq "BF") {
						$str .= "15K ";
						$str .= "U320 " if (hex($seq) >= 0x87B54);
					} elsif ($type eq "DD") {
						$str .= "10K SAS ";
					} elsif ($type eq "DF") {
						$str .= "15K SAS ";
					} elsif ($type eq "DG") {
						$str .= "10K SAS 2.5\" ";
					} elsif ($type eq "DH") {
						$str .= "15K SAS 2.5\" ";
					} elsif ($type eq "FB") {
						$str .= "7.2K SATA/150 ";
					} elsif ($type eq "FJ") {
						$str .= "5.4K SATA/150 ";
					} elsif ($type eq "GB") {
						$str .= "7.2K SATA/300 ";
					}
					$str .= "HP";
					$pretty{$str}++;
					$found++;
				}
			}
			if (!$found) {
				$pretty{$model}++;
			}
		}
		my($sep) = " == ";
		foreach $key (sort { $pretty{$b} <=> $pretty{$a} || length($b) <=> length($a) } keys %pretty) {
			if ($volume =~ /^(md|ccd|ar|dm)\d+/) {
				$output .= "$sep$key";
			} else {
				$output .= "$sep$pretty{$key} x $key";
			}
			if ($opts{'n'}) {
				$sep = ", ";
			} else {
				$sep = sprintf("\n\t\t%*s", length($out) + 4, " ");
			}
		}
		$output .= "\n";
	}
}

my($sent_email) = 0;

sub email {
	my($old, $new) = @_;
	my($out) = 'OUT';

	return if ($sent_email);
	open($out, "| sendmail $email") || warn("exec sendmail: $!");
	print $out "To: $email\n";
	print $out "From: hwconfig\@yahoo-inc.com\n";
	print $out "Subject: hwconfig";
	print $out " diff" if ($old ne $new);
	print $out " for $hostname\n";
	print $out "\n";
	print $out $output;
	print $out "Version:\t$version\n";
	print $out "\n$debug" unless ($opts{'d'});
	print $out "Debug-System:\t$guess_system\n" unless ($debug =~ /^Debug-System/m);
	print $out "\n", $stderr, "\n" if ($stderr);
	print $out "\n", $old, "\n" if ($old);
	print $out "\n> perl warnings\n", $perl_warn if ($perl_warn);
	print $out "\n> pciconf\n", $sig_pci, "\n", $debug_pciconf if ($debug_pciconf);
	print $out "\n> lspci\n", $sig_pci, "\n", $debug_lspci if ($debug_lspci);
	print $out "\n> lshal\n", $debug_lshal if ($debug_lshal);
	print $out "\n> sysctl dev\n", $debug_sysctl_dev if ($debug_sysctl_dev);
	print $out "\n> dmidecode\n", $debug_dmidecode if ($debug_dmidecode);
	print $out "\n> cpuinfo\n", $debug_cpuinfo if ($debug_cpuinfo);
	print $out "\n> meminfo\n", $debug_meminfo if ($debug_meminfo);
	print $out "\n> storage\n", $debug_storage if ($debug_storage);
	print $out "\n> network\n", $debug_network if ($debug_network);
	print $out "\n.\n";
	close($out);
	$sent_email++;
}

# Main

$zero = $0;
$zero =~ s,.*/,,;

getopts('h?dmnvo:', \%opts);

if ($opts{'v'}) {
	print "$version\n";
	exit(0);
}
if ($opts{'h'} || $opts{'?'}) {
	usage();
	exit(0);
}

if ($<) {
	$stderr .= "$zero: warning: run as root for more detailed and accurate data\n";
}

$SIG{'ALRM'} = 'watchdog';
alarm($timeout);

if ($opts{'o'}) {
	# avoid infinite reboot by bailing if it looks like last run at reboot didn't complete
	if (-e $lock) {
		# avoid case where stray lock file permanently disables boot run
		# worst case we end up with a single crash after a reboot
		unlink($lock);
		die "$zero: lock file '$lock' already exists, bailing out";
	}
	system("touch $lock");
}

grok_os();
grok_grub();

init_dmesg();

grok_dmesg();

if ($os_type eq 'freebsd') {
	grok_sysctl_dev() if ($os_version >= 600000000);
	grok_pciconf();
} elsif ($os_type eq "linux") {
	pci_to_driver();
	grok_proc_ioports();
	grok_lspci();
	grok_lshal();
}

# YYYY - need to nuke all dmi work if it fails, i.e. clear %smbios and %procs
dmidecode() || smbinfo();

mptable();

my($bios);
if ($smbios{'BIOS'}{1}{'Vendor'}) {
	my($vendor, $version, $date) = ($smbios{'BIOS'}{1}{'Vendor'}, $smbios{'BIOS'}{1}{'Version'}, $smbios{'BIOS'}{1}{'Release Date'});
	$bios = $vendor;
	$bios =~ s/(Award|Dell|Intel|Phoenix).*/$1/;
	$bios =~ s/American Megatrends.*/AMI/;
	$bios =~ s/Hewlett.*/HP/;
	$bios =~ s/\s+/_/g;
	if ($version) {
		$version =~ s/\s+/_/g;
		$bios .= " $version";
	}
	if ($date) {
		$date =~ s/\s+/_/g;
		$bios .= " $date";
	}
}

sub clean_system {
	my($str) = @_;
	$str =~ s/[^ -~]//g;
	$str =~ s/[\.\,]/ /gi;
	$str =~ s/\b(Server|Board|Mainboard|Microtower|Microsystems|Inc|Computer|Corp|Corporation|Technology|Co|Ltd|Manufacturer)\b/ /gi;
	$str =~ s/Small Form Factor//i;
	$str =~ s/\bInc\b/ /gi;
	$str =~ s/Rackable Systems/Rackable/ig;
	$str =~ s/Hewlett-Packard/HP/i;
	$str =~ s/Enterprise Server Division//;
	$str =~ s/TYAN High-End Dual AMD Opteron//;
	$str =~ s/IBM System x iDataPlex/IBM iDataPlex/;
	$str =~ s/Dual AMD Opteron//;
	$str =~ s/TYAN/Tyan/g;
	$str =~ s/GIGABYTE/Gigabyte/g;
	$str =~ s/DELL/Dell/g;
	$str =~ s/MICRO-STAR INTERNATIONAL/MSI/ig;
	$str =~ s/ASUSTeK/Asus/ig;
	$str =~ s/DELUXE/Deluxe/g;
	$str =~ s/RIOWORKS/Arima/g;
	$str =~ s/A Sanmina-SCI Company//g;
	$str =~ s/NEWISYS/Newisys/g;
	$str =~ s/InventecESC/Inventec/g;
	$str =~ s/((HP|Sun|VMware|IBM|Tyan|Dell)\s+)+/$2 /i;
	$str =~ s/HP Compaq/HP/i;
	$str =~ s/\b(uT|CMT|SFF|MT)(\([^\)]+\))?/$1/;
	$str =~ s/\((TM|R)\)//g;
	$str =~ s/DL140 00h/DL140 G1/i;
	$str =~ s/\-?\[[^\]]+\]\-?/ /;
	$str =~ s/\([^\)]+\)/ /;
	$str = clean_white($str);
	return $str;
}

my($system) = 'Unknown';
my($base_board);
my($sys);

if ($smbios{'Base Board'}{1}{'Manufacturer'} && $smbios{'Base Board'}{1}{'Product Name'}) {
	$base_board = clean_system("$smbios{'Base Board'}{1}{'Manufacturer'} $smbios{'Base Board'}{1}{'Product Name'}");
}
if ($smbios{'System'}{1}{'Manufacturer'} && $smbios{'System'}{1}{'Product Name'}) {
	$system = "$smbios{'System'}{1}{'Manufacturer'} $smbios{'System'}{1}{'Product Name'}";
} elsif ($base_board) {
	$system = $base_board;
}

my($guess) = guess_system($system);
$system = $guess if ($system eq 'Unknown');

if ($system eq "Unknown") {
	$system = $smbios{'System'}{1}{'Product Name'} || $smbios{'System'}{1}{'Manufacturer'} || $smbios{'Base Board'}{1}{'Product Name'} || $smbios{'Base Board'}{1}{'Manufacturer'} || $system;
}

$system = clean_system($system);

if (defined($system_model_index)) {
	my($ugly) = $system_models[$system_model_index]{'ugly-model'};
	if ($ugly && $system =~ /^$ugly$/) {
		$system = $system_models[$system_model_index]{'model'};
	}
}

$sys = $system;
$sys =~ s/poweredge\s+(\d+)/PE$1/i;
$sys =~ s/poweredge\s*//i;
$sys =~ s/proliant\s*//i;
$sys = clean_white($sys);

my($procs, $processors);

sub canon_cpu_model {
	my($model) = @_;

	$model =~ s/\@.*//;
	$model =~ s/[\d\.]+(MHz|GHz)//i;
	$model =~ s/\((TM|R|quarter-micron|coppermine|katmai)\)/ /ig;
	$model =~ s/\b(cpu(\s+family|\s+-\s+s)?|processor|genuine|intel|amd)\b/ /ig;
	$model =~ s/dual[\-\s]+core\s+opteron/Opteron/ig;
	$model =~ s/XEON/Xeon/g;
	$model =~ s/\s+/ /g;
	$model =~ s/\s+$//;
	$model =~ s/^\s+//;
	$model =~ s/\/.*//;

	return $model;
}

sub grok_procs {
	if ($os_type eq "linux") {
		cpuinfo();
	}

	my($proc_id) = 1;
	my($index);
	for ($index=1; $index < 1025; $index++) {
		if ($procs{$index} && $procs{$index}{status}) {
			$proc_id = $index;
			last;
		}
	}

	if (!$procs{$proc_id}{vendor}) {
		$procs{$proc_id}{vendor} = $dmesg{proc}{vendor};
	}
	if (!$procs{$proc_id}{family}) {
		$procs{$proc_id}{family} = $dmesg{proc}{family};
	}
	if (!$procs{$proc_id}{speed}) {
		if ($dmesg{proc}{speed}) {
			$procs{$proc_id}{speed} = $dmesg{proc}{speed};
		} else {
			my($tsc) = `sysctl -n machdep.tsc_freq 2> /dev/null`;
			if ($tsc) {
				chomp($tsc);
				$procs{$proc_id}{speed} = sprintf("%4.2fMHz", $tsc / 1000000) if ($tsc);
			}
		}
	}

	my($vendor, $model);
	my($speed) = 0;

	if ($os_type eq "freebsd") {
		chomp($model = `sysctl -n hw.model 2> /dev/null`);
	}
	if (!$model) {
		if ($procs{$proc_id}{family}) {
			$model = $procs{$proc_id}{family};
		} elsif ($vendor) {
			$model = $vendor;
		} else {
			$model = "unknown";
		}
	}

	$vendor = canon_cpu_vendor($procs{$proc_id}{vendor} || $model);

	if ($model =~ /([\d\.]+)(MHz|GHz)/) {
		$speed = round($1 * (($2 eq "GHz") ? 1000 : 1));
	}

	$model = canon_cpu_model($model);

	if (!$model) {
		if ($vendor) {
			$model = $vendor;
		} else {
			$model = "uknown";
		}
	}

	my($cpuid) = hex($procs{$proc_id}{cpuid} || $dmesg{proc}{cpuid} || "0");
	my($id) = cpuid($vendor, $cpuid);

	my($cpu_info, $cpu_cores, $cpu_threads, $cpu_gen) = ('', 0, 0, 0);
	if ($cpu_models{$id}) {
		if (ref($cpu_models{$id}[0]) eq "ARRAY") {
			my($match) = "$model $speed";
			my($try);
			foreach $try (@{$cpu_models{$id}}) {
				($cpu_info, $cpu_cores, $cpu_threads, $cpu_gen) = @{$try->[1]};
				last if ($match =~ /$try->[0]/i);
			}
		} else {
			($cpu_info, $cpu_cores, $cpu_threads, $cpu_gen) = @{$cpu_models{$id}};
		}
	}
	$procs{gen} = $cpu_gen;

	if ($cpu_info && defined($procs{$proc_id}{'L2 Cache Handle'})) {
		my($handle) = $procs{$proc_id}{'L2 Cache Handle'};
		if (defined($dmi{$handle}) && defined($dmi{$handle}{'key'}{'Installed Size'})) {
			my($pretty) = print_bytes(parse_bytes($dmi{$handle}{'key'}{'Installed Size'}, 1024), 1024, 1);
			$cpu_info =~ s/L2: (\S+)/L2: $pretty/;
		}
	}
	if ($cpu_info && defined($procs{$proc_id}{'L3 Cache Handle'})) {
		my($handle) = $procs{$proc_id}{'L3 Cache Handle'};
		if (defined($dmi{$handle}) && defined($dmi{$handle}{'key'}{'Installed Size'})) {
			my($pretty) = print_bytes(parse_bytes($dmi{$handle}{'key'}{'Installed Size'}, 1024), 1024, 1);
			$cpu_info =~ s/L3: (\S+)/L3: $pretty/;
		}
	}

	my($hyper) = "UNKNOWN";
	if ($os_type eq "linux") {
		if ($cpu_cores == 0) {
			# lookup failed, so guess from what's active in cpuinfo
			if ($procs{threads} && $procs{cores}) {
				$cpu_cores = $procs{cores};
				$cpu_threads = ceil($procs{threads} / $procs{cores});
			} else {
				$cpu_cores = 1;
				$cpu_threads = 1;
			}
		}

		if ($procs{1} && $procs{1}{flags} =~ /\bht\b/i) {
			$hyper = "YES_ON";
		} elsif ($procs{threads} > $procs{cores}) {
			# cpu flags not working with Atom procs and dmidecode 2.9
			$hyper = "YES_ON";
		} else {
			$hyper = "NO";
		}
	} elsif ($os_type eq "freebsd") {
		if ($cpu_cores == 0) {
			# lookup failed, no way to tell in freebsd, so set to 1
			$cpu_cores = 1;
			$cpu_threads = 1;
		}

		chomp($procs{active_threads} = `sysctl -n hw.ncpu`);
		$procs{threads} = $procs{active_threads};

		my($sysctl, $allowed, $halt);
		chomp($sysctl = `sysctl -n machdep.hyperthreading_allowed 2> /dev/null`);
		if ($?) {
			$allowed = -1;
		} elsif ($sysctl) {
			$allowed = 1;
		} else {
			$allowed = 0;
		}
		chomp($sysctl = `sysctl -n machdep.hlt_logical_cpus 2> /dev/null`);
		if ($?) {
			$halt = -1;
		} elsif ($sysctl) {
			$halt = 1;
		} else {
			$halt = 0;
		}

		if ($halt == -1) {
			if ($procs{active_threads} == 1) {
				# if we have a UP kernel, have to grok dmesg looking for HTT feature
				if ($allowed != -1) {
					$debug .= "Debug-HT:\tmachdep.hlt_logical_cpus missing\n";
				}
				if ($dmesg_buf =~ /^\s*Features=.*\bHTT\b/m) {
					$hyper = "YES_UP";
				} else {
					$hyper = "NO";
				}
			} else {
				# we have old SMP kernel without machdep.hlt_logical_cpus
				if ($dmesg_buf =~ /^\s*Features=/m && $dmesg_buf !~ /^\s*Features=.*\bHTT\b/m) {
					$hyper = "NO";
				} else {
					# can't tell at this point, leave as UNKNOWN
				}
			}
		} elsif ($halt == 1) {
			$hyper = "YES_OFF";
		} elsif ($halt == 0) {
			if ($allowed == -1) {
				if ($os_version >= 600000000) {
					$hyper = "NO";
				} else {
					$hyper = "YES_ON";
				}
			} elsif ($allowed == 1) {
				$hyper = "YES_ON";
			} elsif ($allowed == 0) {
				$hyper = "YES_OFF";
			}
		}

		# figure out how many threads are really enabled
		chomp($sysctl = `sysctl -n machdep.hlt_cpus 2> /dev/null`);
		if ($?) {
			if ($procs{active_threads} > 1) {
				if ($hyper eq "YES_OFF") {
					if ($halt == 1) {
						$procs{active_threads} = $procs{active_threads} / ($cpu_threads * $cpu_cores);
					} elsif ($halt == 0) {
						$procs{active_threads} = $procs{active_threads} / $cpu_threads;
					}
				}
			}
		} elsif ($sysctl) {
			my($mask) = $sysctl;
			my($n) = $procs{active_threads};
			while ($n) {
				$procs{active_threads}-- if ($mask & 1);
				$mask = $mask >> 1;
				$n--;
			}
		}
	}

	# cpus will report HT even if they don't have it, e.g. dual/quad-core
	if ($cpu_threads == 1) {
		$hyper = "NO";
	} elsif ($hyper eq "NO") {
		$debug .= "Debug-HT:\tmissing HT detect\n" unless ($os_up);
	}

	if (!$speed && $procs{$proc_id}{speed} && $procs{$proc_id}{speed} =~ /^([\d\.]+)(MHz)?$/i) {
		$speed = $1;
	}
	if ($speed >= 1000) {
		$speed = sprintf("%4.2fGHz", $speed / 1000);
	} elsif ($speed >= 100) {
		$speed = sprintf("%.0fMHz", $speed);
	} elsif ($speed > 0) {
		$speed = sprintf("%4.2fMHz", $speed);
	}

	# fix bogus opteron models from HP - http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c00606132
	if ($model =~ /Opteron 852/ && $speed =~ /2\.40GHz/i) {
		$model = "Opteron 880";
	} elsif ($model =~ /Opteron 850/ && $speed =~ /2\.20GHz/i) {
		$model = "Opteron 875";
	}

	# fix bogus FSB speeds
	if ($procs{$proc_id}{clock} && $cpu_info) {
		if ($cpu_info =~ /Prestonia/ && $procs{$proc_id}{clock} eq "133MHz") {
			# for HP DL140 with Prestonia Bs
			$procs{$proc_id}{clock} = "533MHz";
			$cpu_info =~ s/Prestonia/Prestonia B/;
		} elsif ($cpu_info =~ /Sossaman/ && $procs{$proc_id}{clock} eq "167MHz") {
			# for Intel SE7520BB2 with Sossaman
			$procs{$proc_id}{clock} = "667MHz";
		}
	}
	
	# how many sockets does this particular system have (assuming we are correct on system type)
	# to be looked up in system property defintion table
	my($sockets) = 0;
	if (defined($system_model_index)) {
		$sockets = $system_models[$system_model_index]{sockets};
	} else {
		if ($procs{sockets}) {
			# got data from smbinfo
			$sockets = $procs{sockets};
		} else {
			# need to guess the number of sockets
			if ($procs{mptable}) {
				$sockets = $procs{mptable};
			} else {
				$sockets = $procs{threads};
			}
			if ($cpu_threads && ($hyper eq "YES_OFF" || $hyper eq "YES_ON")) {
				# if HT is supported, ncpu should be 2x number of sockets
				$sockets = ceil($sockets / $cpu_threads);
			}
			if ($cpu_cores) {
				$sockets = ceil($sockets / $cpu_cores);
			}
			$sockets = 1 if ($sockets < 1);
		}
	}
	if (!$procs{sockets} || $procs{sockets} != $sockets) {
		if ($procs{sockets}) {
			# only warn if we got bad info from smbios
			$debug .= sprintf("Debug-Sockets:\t(smbios=%d) != (lookup=%d) ==> setting sockets=%d\n", $procs{sockets}, $sockets, $sockets);
		}
		# we figured out sockets above, use it
		$procs{sockets} = $sockets;

		# our best guess, make it look like smbios thought we had these minimum number of active cpus
		my($i);
		for ($i=0; $i < ceil($procs{active_threads} / $cpu_cores / $cpu_threads); $i++) {
			$procs{$i+1}{status} = 1;
		}
	}

	# how many active chips do we have in the system
	my($chips) = 0;
	my($proc);
	foreach $proc (keys %procs) {
		$chips++ if $procs{$proc}{status};
	}
	# does /proc/cpuinfo agree?
	if ($procs{chips} && $procs{chips} != $chips) {
		# if not, let's go with /proc/cpuinfo
		$debug .= sprintf("Debug-Chips:\t(smbios=%d) != (cpuinfo=%d) ==> setting chips=%d\n", $chips, $procs{chips}, $procs{chips});
		$chips = $procs{chips};
	}

	if (!$cpu_info && $cpu_cores == 1) {
		# we don't know this cpu from cpuid, so guess at number of cores
		$cpu_cores = ceil($procs{active_threads} / $cpu_threads);
		$cpu_cores = ceil($cpu_cores / $chips) if ($chips);
	}

	# fixup chips and sockets
	if ($procs{sockets} && ($chips > $procs{sockets})) {
		# fix cases that are clearly broken
		# e.g. Tyan S2865 with Opteron 180 dual-core reports 2 sockets in smbios
		$debug .= sprintf("Debug-Sockets:\t(chips=%d) > (smbios=%d) ==> setting chips=%d\n", $chips, $procs{sockets}, $procs{sockets});
		$chips = $procs{sockets};
	}

	# was using active_threads here before, but we really want just threads which should be accurate
	if ($procs{threads} > $chips * $cpu_cores * $cpu_threads) {
		# most likely smbios said we had fewer chips
		# or we didn't have smbios so we guessed at one chip
		$debug .= sprintf("Debug-Chips:\t(threads=%d) > (chips=%d * cpu_cores=%d * cpu_threads=%d) ==> setting chips=%d\n", $procs{threads}, $chips, $cpu_cores, $cpu_threads, ceil($procs{threads} / $cpu_cores / $cpu_threads));
		$chips = ceil($procs{threads} / $cpu_cores / $cpu_threads);
		if ($procs{sockets} < $chips) {
			$procs{sockets} = $chips;
		}
	} elsif ($procs{threads} < $chips * $cpu_cores * $cpu_threads) {
		if ($cpu_threads > 1 && $procs{threads} <= $chips * $cpu_cores) {
			if ($hyper ne "YES_OFF") {
				# deal with case where HT is turned off in BIOS
				$hyper = "YES_MISSING";
			}
		} else {
			if ($procs{threads} == 1) {
				# should mean we have UP kernel running
				$debug .= sprintf("Debug-Threads:\t(threads=%d) < (chips=%d * cpu_cores=%d * cpu_threads=%d) ==> running UP kernel?\n", $procs{threads}, $chips, $cpu_cores, $cpu_threads);
			} elsif (defined($grub{'maxcpus'})) {
				# maxcpus is limited in grub.conf
				$debug .= sprintf("Debug-Threads:\t(threads=%d) < (chips=%d * cpu_cores=%d * cpu_threads=%d) ==> maxcpus=%d limited in $grub\n", $procs{threads}, $chips, $cpu_cores, $cpu_threads, $grub{'maxcpus'});
			} else {
				# we'll assume cores/threads are accurate, chips is not
				$debug .= sprintf("Debug-Threads:\t(threads=%d) < (chips=%d * cpu_cores=%d * cpu_threads=%d) ==> setting chips=%d\n", $procs{threads}, $chips, $cpu_cores, $cpu_threads, ceil($procs{threads} / $cpu_cores / $cpu_threads));
				$chips = ceil($procs{threads} / $cpu_cores / $cpu_threads);
				if ($procs{sockets} < $chips) {
					$procs{sockets} = $chips;
				}
			}
		}
	}

	$procs{cores} = $chips * $cpu_cores;
	$procs{threads} = $chips * $cpu_cores * $cpu_threads;
	if ($procs{cores} > $procs{active_threads}) {
		$procs{active_cores} = $procs{active_threads};
	} else {
		$procs{active_cores} = $procs{cores};
	}

	# sanity check on cores/threads
	if ($procs{active_threads} > $procs{threads}) {
		$debug .= sprintf("Debug-Threads-Active:\t(active_threads=%d) > (threads=%d)\n", $procs{active_threads}, $procs{threads});
	}
	if ($procs{active_cores} > $procs{cores}) {
		$debug .= sprintf("Debug-Cores-Active:\t(active_cores=%d) > (cores=%d)\n", $procs{active_cores}, $procs{cores});
	}
	if ($procs{sockets} && ($procs{cores} > $procs{sockets} * $cpu_cores)) {
		$debug .= sprintf("Debug-Cores:\t(cores=%d) > (sockets=%d * cpu_cores=%d)\n", $procs{cores}, $procs{sockets}, $cpu_cores);
	}
	if ($procs{sockets} && ($procs{threads} > $procs{sockets} * $cpu_cores * $cpu_threads)) {
		$debug .= sprintf("Debug-Threads:\t(threads=%d) > (sockets=%d * cpu_cores=%d * cpu_threads=%d)\n", $procs{threads}, $procs{sockets}, $cpu_cores, $cpu_threads);
	}
	if ($procs{cores} != $chips * $cpu_cores) {
		$debug .= sprintf("Debug-Cores:\t(cores=%d) != (chips=%d * cpu_cores=%d)\n", $procs{cores}, $chips, $cpu_cores);
	}

	# ready for results

	$procs = "$chips x $model";
	$procs .= " $speed" if ($speed);
	if ($chips >= $procs{sockets}) {
		$processors = "$chips x $model";
	} else {
		$processors = "$chips (of $procs{sockets}) x $model";
	}
	$processors .= " $speed" if ($speed);
	$processors .= " $procs{$proc_id}{clock} FSB" if ($procs{$proc_id}{clock});

	my($open, $close) = (" (", "");
	if ($hyper =~ /^YES/ && $vendor ne "AMD") {
		$processors .= $open . "HT ";
		$open = ", ";
		$close = ")";
		if ($hyper eq "YES_ON") {
			$processors .= "enabled";
		} elsif ($hyper eq "YES_OFF") {
			$processors .= "disabled";
		} elsif ($hyper eq "YES_MISSING" || $hyper eq "YES_UP") {
			$processors .= "missing";
		}
	}

	if ($procs{cores} > $chips || $procs{active_cores} < $procs{cores}) {
		if ($procs{active_cores} < $procs{cores}) {
			$processors .= $open . "$procs{active_cores}/$procs{cores} cores";
		} else {
			$processors .= $open . "$procs{active_cores} cores";
		}
		$open = ", ";
		$close = ")";
		if ($procs{threads} && $procs{threads} > $procs{cores}) {
			if ($procs{active_threads} < $procs{threads}) {
				$processors .= $open . "$procs{active_threads}/$procs{threads} threads";
			} else {
				$processors .= $open . "$procs{active_threads} threads";
			}
		}
	} elsif ($procs{threads} > $chips) {
		if ($procs{active_threads} < $procs{threads}) {
			$processors .= $open . "$procs{active_threads}/$procs{threads} threads";
		} else {
			$processors .= $open . "$procs{active_threads} threads";
		}
		$open = ", ";
		$close = ")";
	}
	$processors .= $close;

	if ($cpu_info) {
		$processors .= " - $cpu_info" if ($cpu_info);
	} elsif ($cpuid) {
		$debug .= "Debug-Processors:\t$id\n";
	}

#	my($features) = $procs{$proc_id}{features} || $dmesg{proc}{features};
#	if ($features) {
#		$processors .= " (features = $features)";
#	} elsif ($procs{$proc_id}{flags}) {
#		$processors .= " (flags = $procs{$proc_id}{flags})";
#	}
}

sub memory {
	my($mem, $memory) = ("", "");
	my($total) = 0;
	if (-r $meminfo) {
		my($in) = "MEMINFO";
		my($line);
		open($in, "< $meminfo") || warn "open: $meminfo: $!";
		while ($line = <$in>) {
			$debug_meminfo .= $line;
			chomp($line);
			if ($line =~ /^Mem:\s*(\d+)/i) {
				$total = $1;
			} elsif ($line =~ /^MemTotal:\s*(\d+)\s*kB/i) {
				$total = $1 * 1024 unless $total;
			}
		}
		close($in);
	} else {
		# first check with dmesg
		if ($dmesg_buf =~ /^Physical memory chunk/m) {
			my($line);
			my($match) = 0;
			foreach $line (split(/\n/, $dmesg_buf)) {
				if ($line =~ /^Physical memory chunk/) {
					$match = 1;
				} elsif ($match) {
					if ($line =~ /(\d+) bytes/) {
						$total += $1;
					} else {
						last;
					}
				}
			}
		} elsif ($dmesg_buf =~ /^avail memory = (\d+)/m) {
			$total = $1;
		}

		# now let's see what sysctl hw.* has to say
		my($arch, $pae, $pages, $pagesize, $physmem);
		chomp($arch = `sysctl -n hw.machine_arch 2> /dev/null` || 0);
		chomp($pae = `sysctl -n kern.features.pae 2> /dev/null` || 0);
		chomp($pages = `sysctl -n hw.maxmem 2> /dev/null` || `sysctl -n hw.availpages 2> /dev/null` || 0);
		chomp($pagesize = `sysctl -n hw.pagesize 2> /dev/null` || 0);
		chomp($physmem = `sysctl -n hw.physmem 2> /dev/null` || 0);

		my($try) = 0;
		if ($pae && !$total) {
			# hw.physmem seems to be accurate, but is only 32-bit int, so can't use in case of pae
			# however pages method seems to over estimate, so only use it if we don't have something from dmesg
			$try = $pages * $pagesize;
		}
		if (!$try) {
			$try = $physmem;
		}
		# prefer hw.physmem if we have it, but prefer dmesg if we resort to hw.maxmmem
		$total = $try if ($try > $total);
	}
	$mem = print_bytes($total, 1024, 1, 100);	# probably shouldn't force int=1 here, but didn't have .0 in legacy
	$total = $total / 1024 / 1024;

	if ($smbios{'Memory'}{'Count'}) {
		my($sum) = 0;
		my($i);
		my($device, %device);
		my($key);
		my($sep) = "";
		my($speed) = 0;
		my($type);
	
		for ($i=1; $i <= $smbios{'Memory'}{'Count'}; $i++) {
			my($size) = $smbios{'Memory'}{$i}{'Size'};
			if ($size =~ /(\d+)(MB)?/i) {
				$size = $1 * 1024 * 1024;
				# only use type/speed info if socket is populated
				$type = $smbios{'Memory'}{$i}{'Type'} if ($smbios{'Memory'}{$i}{'Type'});
				$speed = $smbios{'Memory'}{$i}{'Speed'} if ($smbios{'Memory'}{$i}{'Speed'});
			} else {
				$size = 0;
			}
			$sum += $size;
			$key = "$size,";
			$key .= $smbios{'Memory'}{$i}{'Part Number'} if ($smbios{'Memory'}{$i}{'Part Number'});
			$device{$key}++;
		}
		$mem .= " / " if ($mem && $sum);
		$mem .= print_bytes($sum, 1024, 1) if ($sum);
		if ($speed) {
			if ($speed eq "133MHz" && $system =~ /140 G3/) {
				$speed = "533MHz";
			}
		}
		if ($mem) {
			$mem .= " $speed" if ($speed);
			$mem .= " $type" if ($type && $type ne "Unknown" && $type ne "DRAM");

			my(%pretty);
			foreach $key (keys %device) {
				my($size, $part) = ($key =~ /^(.*?),(.*)/);
				my($try);
				my($found) = 0;
				if ($part) {
					foreach $try (keys %memory_models) {
						if ($part =~ /^$try$/i) {
							my($pretty_key) = "$size," . $memory_models{$try};
							$pretty{$pretty_key} += $device{$key};
							$found++;
							last;
						}
					}
					if (!$found) {
						$debug .= "Debug-Memory:\t$part\n";
					}
				}
				if (!$found) {
					$pretty{"$size,"} += $device{$key};
				}
			}

			$sep  = "";
			# sort by memory size first, then number of dimms, then length of description
			foreach $key (sort { (split(/,/,$b))[0] <=> (split(/,/,$a))[0] || $pretty{$b} <=> $pretty{$a} || length($b) <=> length($a) } keys %pretty) {
				my($size, $model) = ($key =~ /^(.*?),(.*)/);
				if ($size) {
					$size = print_bytes($size, 1024, 1);
				} else {
					$size = "empty";
				}
				$memory .= "$sep$pretty{$key} x $size";
				$memory .= " - $model" if ($model);
				$sep = ", ";
			}
		}
	}
	return ($mem, $memory, $total);
}

grok_procs();

my($mem, $memory, $meg) = memory();

if ($os_type eq 'freebsd') {
	freebsd_disks();
} elsif ($os_type eq 'linux') {
	grok_proc_ide() unless $success{'grok_lshal_ide'};
	grok_proc_scsi() unless $success{'grok_lshal_scsi'};
	grok_proc_cciss();
	grok_proc_partitions();
	grok_mdstat() if ($need_mdstat);
}

if ($need_hpacucli || $want_hpacucli) {
	hpacucli();
}

if ($need_twcli || $want_twcli) {
	twcli();
}

if ($need_mptutil || $want_mptutil) {
	mptutil();
}

if ($need_mfiutil || $want_mfiutil) {
	mfiutil();
}

if ($need_megarc || $want_megarc) {
	megarc() || omreport();
}

if ($need_megacli || $want_megacli) {
	megacli() || omreport();
}

if ($need_cissutil || $want_cissutil) {
	cissutil();
}

if ($need_arcconf || $want_arcconf) {
	arcconf() || omreport();
}

if ($os_type eq 'freebsd') {
	freebsd_disks_unknown();
	freebsd_disk_ioctls();
}

my($ymodel) = '';
if ($system_model_index && defined $system_models[$system_model_index]{'ymodel-type'}) {
	$ymodel = sprintf("%s%d%d%d", $system_models[$system_model_index]{'ymodel-type'}, $procs{gen} || 1, log($procs{cores})/log(2), log(($meg/1024)+0.5)/log(2));
}

$output .= "Summary:\t";
#$output .= "$ymodel - " if ($ymodel);
$output .= "$sys, $procs, $mem\n";
$output .= "System:\t\t$system";
$output .= " ($base_board)" if ($base_board && $base_board ne $system && !defined($smbios{'Base Board'}{2}));
$output .= "\nProcessors:\t$processors\n";

if (length($memory) > 70 && !$opts{'n'}) {
	my($sep) = sprintf("\n\t\t%*s", length($mem) + 4, " ");
	$memory =~ s/, /$sep/g;
}
$output .= "Memory:\t\t$mem";
$output .= " == $memory" if ($memory);
$output .= "\n";

lvdisplay() if ($need_lvm);
df();
ccdconfig() if ($need_ccdconfig);

print_disks();

# need this before other_devices()
grok_network();

other_devices();

my($interface);
foreach $interface (sort cmp_devs keys %interfaces) {
	next if ($interface =~ /^(vlan|bond|dummy|vmnet|sw|irda|venet|veth|xenbr|virbr|cipsec|wmaster|mskc|pan)\d+$/);
	my($driver) = $interface_to_driver{$interface};
	$output .= "Network:\t$interface";
	$output .= " ($driver)" if ($driver);
	$output .= ": ";
	if ($driver && $interfaces{$driver} && $interfaces{$driver}{model}) {
		# only example of this is msk0 (interface) -> mskc0 (pci-device/driver)
		$output .= $interfaces{$driver}{model} . ", ";
	} elsif ($interfaces{$interface}{model}) {
		$output .= $interfaces{$interface}{model} . ", ";
	}
	$output .= $interfaces{$interface}{mac} if ($interfaces{$interface}{mac});
	if (defined($interfaces{$interface}{status})) {
		if ($interfaces{$interface}{status} eq "active") {
			$output .= ", $interfaces{$interface}{media}" if ($interfaces{$interface}{media});
		} else {
			$output .= ", $interfaces{$interface}{status}";
		}
	}
	$output .= "\n";
}

if ($os) {
	$output .= "OS:\t\t$os";
	if ($os =~ /amd64|x86_64/) {
		$output .= ", 64-bit";
	} else {
		$output .= ", 32-bit";
	}
	$output .= "\n";
}
$output .= "BIOS:\t\t$bios\n" if ($bios);

if ($os_type eq "linux") {
	chomp($hostname = `hostname -f`);
} else {
	chomp($hostname = `hostname`);
}
if ($hostname) {
	$output .= "Hostname:\t$hostname";
	my($pre, $post) = (" (", "");
	if ($jailed) {
		$output .= $pre . "jailed";
		$output .= " on $jailer" if ($jailer);
		$pre = ", ";
		$post = ")";
	}
	if ($yroot) {
		$output .= $pre . "yroot $yroot";
		$pre = ", ";
		$post = ")";
	}
	$output .= $post . "\n";
}

$output .= "\n$debug" if ($opts{'d'} && $debug);

if ($need_cissutil && !$got_cissutil) {
	$stderr .= "$zero: warning: could not run $cissutil";
	$stderr .= "; please yinst $cissutil" unless ($have_cissutil);
	$stderr .= "\n";
}
if ($need_megarc && !$got_megarc && !$got_omreport) {
	$stderr .= "$zero: warning: could not run $megarc";
	$stderr .= "; please yinst $megarc" unless ($have_megarc);
	$stderr .= "\n";
}
if ($need_megacli && !$got_megacli && !$got_omreport) {
	$stderr .= "$zero: warning: could not run MegaCli";
	$stderr .= "; please yinst megacli" unless ($have_megacli);
	$stderr .= "\n";
}
if ($need_mfiutil && !$got_mfiutil) {
	$stderr .= "$zero: warning: could not run $mfiutil";
	$stderr .= "; please yinst $mfiutil" unless ($have_mfiutil);
	$stderr .= "\n";
}
if ($need_mptutil && !$got_mptutil) {
	$stderr .= "$zero: warning: could not run $mptutil";
	$stderr .= "; please yinst $mptutil" unless ($have_mptutil);
	$stderr .= "\n";
}
if ($need_hpacucli && !$got_hpacucli) {
	$stderr .= "$zero: warning: could not run $hpacucli";
	$stderr .= "; please yinst ports/$hpacucli" unless ($have_hpacucli);
	$stderr .= "\n";
}
if ($need_twcli && !$got_twcli) {
	$stderr .= "$zero: warning: could not run $twcli";
	$stderr .= "; please yinst $twcli" unless ($have_twcli);
	$stderr .= "\n";
}
if ($need_arcconf && !$got_arcconf && !$got_omreport) {
	$stderr .= "$zero: warning: could not run $arcconf";
	$stderr .= "\n";
}

if ($opts{'o'}) {
	my($out) = 'OUT';
	open($out, "> $opts{'o'}") || warn("open: $opts{'o'}: $!");
	print STDERR $stderr if ($stderr);
	print $out $output;
	close($out);
	if (($debug || $perl_warn) && !$jailed) {
		my($old, $new) = ("", "");
		my($tmp) = $opts{'o'};
		$tmp =~ s/^.*\///;
		$tmp = "/var/tmp/$tmp";
		if (-e $tmp) {
			$old = `cat $tmp`;
			# nuke disk usage and hostnames for diff
			$old =~ s/(\s*\d+)\%//gm;
			$old =~ s/^Hostname:.*//m;
			$new = $output;
			$new =~ s/(\s*\d+)\%//gm;
			$new =~ s/^Hostname:.*//m;
		}
		if (! -e $tmp || $old ne $new) {
			email($old, $new);
			open($out, "> $tmp") || warn("open: $tmp: $!");
			print $out $output;
			close($out);
		}
	}
	unlink($lock);
} else {
	print STDERR $stderr if ($stderr);
	print $output;
}

if ($opts{'m'}) {
	email('', '');
}

One thought on “服务器硬件信息脚本”

  1. Excellent web site. Lots of useful info here. I’m sending it to some friends ans additionally sharing
    in delicious. And certainly, thanks to your sweat!

发表评论

电子邮件地址不会被公开。 必填项已用*标注