root/lm-sensors/trunk/prog/eeprom/decode-dimms.pl @ 3303

Revision 3303, 39.3 KB (checked in by khali, 7 years ago)

Detect undefined manufacturer code and handle it properly.
Round up timing data.
Minor display adjustments.
Group cycle and access times, display the CAS value for each (SDRAM).
Refactor some bitfield tests into loops (SDRAM).
Display latencies and burst length on a single line (SDRAM).
Don't display manufacturing location when undefined.
Check that the manufacturing date is proper BCD, else fall back to
hexadecimal display.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#!/usr/bin/perl -w
2#
3# Copyright 1998, 1999 Philip Edelbrock <phil@netroedge.com>
4# modified by Christian Zuckschwerdt <zany@triq.net>
5# modified by Burkart Lingner <burkart@bollchen.de>
6#
7#    This program is free software; you can redistribute it and/or modify
8#    it under the terms of the GNU General Public License as published by
9#    the Free Software Foundation; either version 2 of the License, or
10#    (at your option) any later version.
11#
12#    This program is distributed in the hope that it will be useful,
13#    but WITHOUT ANY WARRANTY; without even the implied warranty of
14#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15#    GNU General Public License for more details.
16#
17#    You should have received a copy of the GNU General Public License
18#    along with this program; if not, write to the Free Software
19#    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20#
21# Version 0.4  1999  Philip Edelbrock <phil@netroedge.com>
22# Version 0.5  2000-03-30  Christian Zuckschwerdt <zany@triq.net>
23#  html output (selectable by commandline switches)
24# Version 0.6  2000-09-16  Christian Zuckschwerdt <zany@triq.net>
25#  updated according to SPD Spec Rev 1.2B
26#  see http://developer.intel.com/technology/memory/pc133sdram/spec/Spdsd12b.htm
27# Version 0.7  2002-11-08  Jean Delvare <khali@linux-fr.org>
28#  pass -w and use strict
29#  valid HTML 3.2 output (--format mode)
30#  miscellaneous formatting enhancements and bug fixes
31#  clearer HTML output (original patch by Nick Kurshev <nickols_k@mail.ru>)
32#  stop decoding on checksum error by default (--checksum option forces)
33# Version 0.8  2005-06-20  Burkart Lingner <burkart@bollchen.de>
34#  adapted to Kernel 2.6's /sys filesystem
35# Version 0.9  2005-07-15  Jean Delvare <khali@linux-fr.org>
36#  fix perl warning
37#  fix typo
38#  refactor some code
39# Version 1.0  2005-09-18  Jean Delvare <khali@linux-fr.org>
40#  add large lookup tables for manufacturer names, based on data
41#  provided by Rudolf Marek, taken from:
42#  http://www.jedec.org/download/search/JEP106r.pdf
43# Version 1.1  2006-01-22  Jean Delvare <khali@linux-fr.org>
44#  improve the text output, making it hopefully clearer
45#  read eeprom by 64-byte blocks, this allows some code cleanups
46#  use sysopen/sysread instead of open/read for better performance
47#  verify checksum before decoding anything
48# Version 1.2  2006-05-15  Jean Delvare <khali@linux-fr.org>
49#  implement per-memory-type decoding
50#  don't decode revision code, manufacturing date and assembly serial
51#  number where not set
52#  decode the manufacturing date to an ISO8601 date
53# Version 1.3  2006-05-21  Jean Delvare <khali@linux-fr.org>
54#  detect undefined manufacturer code and handle it properly
55#  round up timing data
56#  minor display adjustments
57#  group cycle and access times, display the CAS value for each (SDRAM)
58#  refactor some bitfield tests into loops (SDRAM)
59#  display latencies and burst length on a single line (SDRAM)
60#  don't display manufacturing location when undefined
61#  check that the manufacturing date is proper BCD, else fall back to
62#  hexadecimal display
63#
64#
65# EEPROM data decoding for SDRAM DIMM modules.
66#
67# Two assumptions: lm_sensors-2.x installed,
68# and Perl is at /usr/bin/perl
69#
70# use the following command line switches
71#  -f, --format            print nice html output
72#  -b, --bodyonly          don't print html header
73#                          (useful for postprocessing the output)
74#  -c, --checksum          decode completely even if checksum fails
75#  -h, --help              display this usage summary
76#
77# References:
78# PC SDRAM Serial Presence
79# Detect (SPD) Specification, Intel,
80# 1997,1999, Rev 1.2B
81#
82# Jedec Standards 4.1.x & 4.5.x
83# http://www.jedec.org
84#
85
86require 5.004;
87
88use strict;
89use POSIX;
90use Fcntl qw(:DEFAULT :seek);
91use vars qw($opt_html $opt_body $opt_bodyonly $opt_igncheck $use_sysfs
92            @vendors %decode_callback);
93
94@vendors = (
95["AMD", "AMI", "Fairchild", "Fujitsu",
96 "GTE", "Harris", "Hitachi", "Inmos",
97 "Intel", "I.T.T.", "Intersil", "Monolithic Memories",
98 "Mostek", "Freescale (formerly Motorola)", "National", "NEC",
99 "RCA", "Raytheon", "Conexant (Rockwell)", "Seeq",
100 "Philips Semi. (Signetics)", "Synertek", "Texas Instruments", "Toshiba",
101 "Xicor", "Zilog", "Eurotechnique", "Mitsubishi",
102 "Lucent (AT&T)", "Exel", "Atmel", "SGS/Thomson",
103 "Lattice Semi.", "NCR", "Wafer Scale Integration", "IBM",
104 "Tristar", "Visic", "Intl. CMOS Technology", "SSSI",
105 "MicrochipTechnology", "Ricoh Ltd.", "VLSI", "Micron Technology",
106 "Hyundai Electronics", "OKI Semiconductor", "ACTEL", "Sharp",
107 "Catalyst", "Panasonic", "IDT", "Cypress",
108 "DEC", "LSI Logic", "Zarlink (formerly Plessey)", "UTMC",
109 "Thinking Machine", "Thomson CSF", "Integrated CMOS (Vertex)", "Honeywell",
110 "Tektronix", "Sun Microsystems", "SST", "ProMos/Mosel Vitelic",
111 "Infineon (formerly Siemens)", "Macronix", "Xerox", "Plus Logic",
112 "SunDisk", "Elan Circuit Tech.", "European Silicon Str.", "Apple Computer",
113 "Xilinx", "Compaq", "Protocol Engines", "SCI",
114 "Seiko Instruments", "Samsung", "I3 Design System", "Klic",
115 "Crosspoint Solutions", "Alliance Semiconductor", "Tandem", "Hewlett-Packard",
116 "Intg. Silicon Solutions", "Brooktree", "New Media", "MHS Electronic",
117 "Performance Semi.", "Winbond Electronic", "Kawasaki Steel", "Bright Micro",
118 "TECMAR", "Exar", "PCMCIA", "LG Semi (formerly Goldstar)",
119 "Northern Telecom", "Sanyo", "Array Microsystems", "Crystal Semiconductor",
120 "Analog Devices", "PMC-Sierra", "Asparix", "Convex Computer",
121 "Quality Semiconductor", "Nimbus Technology", "Transwitch", "Micronas (ITT Intermetall)",
122 "Cannon", "Altera", "NEXCOM", "QUALCOMM",
123 "Sony", "Cray Research", "AMS(Austria Micro)", "Vitesse",
124 "Aster Electronics", "Bay Networks (Synoptic)", "Zentrum or ZMD", "TRW",
125 "Thesys", "Solbourne Computer", "Allied-Signal", "Dialog",
126 "Media Vision", "Level One Communication"],
127["Cirrus Logic", "National Instruments", "ILC Data Device", "Alcatel Mietec",
128 "Micro Linear", "Univ. of NC", "JTAG Technologies", "Loral",
129 "Nchip", "Galileo Tech", "Bestlink Systems", "Graychip",
130 "GENNUM", "VideoLogic", "Robert Bosch", "Chip Express",
131 "DATARAM", "United Microelec Corp.", "TCSI", "Smart Modular",
132 "Hughes Aircraft", "Lanstar Semiconductor", "Qlogic", "Kingston",
133 "Music Semi", "Ericsson Components", "SpaSE", "Eon Silicon Devices",
134 "Programmable Micro Corp", "DoD", "Integ. Memories Tech.", "Corollary Inc.",
135 "Dallas Semiconductor", "Omnivision", "EIV(Switzerland)", "Novatel Wireless",
136 "Zarlink (formerly Mitel)", "Clearpoint", "Cabletron", "Silicon Technology",
137 "Vanguard", "Hagiwara Sys-Com", "Vantis", "Celestica",
138 "Century", "Hal Computers", "Rohm Company Ltd.", "Juniper Networks",
139 "Libit Signal Processing", "Mushkin Enhanced Memory", "Tundra Semiconductor", "Adaptec Inc.",
140 "LightSpeed Semi.", "ZSP Corp.", "AMIC Technology", "Adobe Systems",
141 "Dynachip", "PNY Electronics", "Newport Digital", "MMC Networks",
142 "T Square", "Seiko Epson", "Broadcom", "Viking Components",
143 "V3 Semiconductor", "Flextronics (formerly Orbit)", "Suwa Electronics", "Transmeta",
144 "Micron CMS", "American Computer & Digital Components Inc", "Enhance 3000 Inc", "Tower Semiconductor",
145 "CPU Design", "Price Point", "Maxim Integrated Product", "Tellabs",
146 "Centaur Technology", "Unigen Corporation", "Transcend Information", "Memory Card Technology",
147 "CKD Corporation Ltd.", "Capital Instruments, Inc.", "Aica Kogyo, Ltd.", "Linvex Technology",
148 "MSC Vertriebs GmbH", "AKM Company, Ltd.", "Dynamem, Inc.", "NERA ASA",
149 "GSI Technology", "Dane-Elec (C Memory)", "Acorn Computers", "Lara Technology",
150 "Oak Technology, Inc.", "Itec Memory", "Tanisys Technology", "Truevision",
151 "Wintec Industries", "Super PC Memory", "MGV Memory", "Galvantech",
152 "Gadzoox Nteworks", "Multi Dimensional Cons.", "GateField", "Integrated Memory System",
153 "Triscend", "XaQti", "Goldenram", "Clear Logic",
154 "Cimaron Communications", "Nippon Steel Semi. Corp.", "Advantage Memory", "AMCC",
155 "LeCroy", "Yamaha Corporation", "Digital Microwave", "NetLogic Microsystems",
156 "MIMOS Semiconductor", "Advanced Fibre", "BF Goodrich Data.", "Epigram",
157 "Acbel Polytech Inc.", "Apacer Technology", "Admor Memory", "FOXCONN",
158 "Quadratics Superconductor", "3COM"],
159["Camintonn Corporation", "ISOA Incorporated", "Agate Semiconductor", "ADMtek Incorporated",
160 "HYPERTEC", "Adhoc Technologies", "MOSAID Technologies", "Ardent Technologies",
161 "Switchcore", "Cisco Systems, Inc.", "Allayer Technologies", "WorkX AG",
162 "Oasis Semiconductor", "Novanet Semiconductor", "E-M Solutions", "Power General",
163 "Advanced Hardware Arch.", "Inova Semiconductors GmbH", "Telocity", "Delkin Devices",
164 "Symagery Microsystems", "C-Port Corporation", "SiberCore Technologies", "Southland Microsystems",
165 "Malleable Technologies", "Kendin Communications", "Great Technology Microcomputer", "Sanmina Corporation",
166 "HADCO Corporation", "Corsair", "Actrans System Inc.", "ALPHA Technologies",
167 "Silicon Laboratories, Inc. (Cygnal)", "Artesyn Technologies", "Align Manufacturing", "Peregrine Semiconductor",
168 "Chameleon Systems", "Aplus Flash Technology", "MIPS Technologies", "Chrysalis ITS",
169 "ADTEC Corporation", "Kentron Technologies", "Win Technologies", "Tachyon Semiconductor (formerly ASIC Designs Inc.)",
170 "Extreme Packet Devices", "RF Micro Devices", "Siemens AG", "Sarnoff Corporation",
171 "Itautec Philco SA", "Radiata Inc.", "Benchmark Elect. (AVEX)", "Legend",
172 "SpecTek Incorporated", "Hi/fn", "Enikia Incorporated", "SwitchOn Networks",
173 "AANetcom Incorporated", "Micro Memory Bank", "ESS Technology", "Virata Corporation",
174 "Excess Bandwidth", "West Bay Semiconductor", "DSP Group", "Newport Communications",
175 "Chip2Chip Incorporated", "Phobos Corporation", "Intellitech Corporation", "Nordic VLSI ASA",
176 "Ishoni Networks", "Silicon Spice", "Alchemy Semiconductor", "Agilent Technologies",
177 "Centillium Communications", "W.L. Gore", "HanBit Electronics", "GlobeSpan",
178 "Element 14", "Pycon", "Saifun Semiconductors", "Sibyte, Incorporated",
179 "MetaLink Technologies", "Feiya Technology", "I & C Technology", "Shikatronics",
180 "Elektrobit", "Megic", "Com-Tier", "Malaysia Micro Solutions",
181 "Hyperchip", "Gemstone Communications", "Anadigm (formerly Anadyne)", "3ParData",
182 "Mellanox Technologies", "Tenx Technologies", "Helix AG", "Domosys",
183 "Skyup Technology", "HiNT Corporation", "Chiaro", "MCI Computer GMBH",
184 "Exbit Technology A/S", "Integrated Technology Express", "AVED Memory", "Legerity",
185 "Jasmine Networks", "Caspian Networks", "nCUBE", "Silicon Access Networks",
186 "FDK Corporation", "High Bandwidth Access", "MultiLink Technology", "BRECIS",
187 "World Wide Packets", "APW", "Chicory Systems", "Xstream Logic",
188 "Fast-Chip", "Zucotto Wireless", "Realchip", "Galaxy Power",
189 "eSilicon", "Morphics Technology", "Accelerant Networks", "Silicon Wave",
190 "SandCraft", "Elpida"],
191["Solectron", "Optosys Technologies", "Buffalo (Formerly Melco)", "TriMedia Technologies",
192 "Cyan Technologies", "Global Locate", "Optillion", "Terago Communications",
193 "Ikanos Communications", "Princeton Technology", "Nanya Technology", "Elite Flash Storage",
194 "Mysticom", "LightSand Communications", "ATI Technologies", "Agere Systems",
195 "NeoMagic", "AuroraNetics", "Golden Empire", "Mushkin",
196 "Tioga Technologies", "Netlist", "TeraLogic", "Cicada Semiconductor",
197 "Centon Electronics", "Tyco Electronics", "Magis Works", "Zettacom",
198 "Cogency Semiconductor", "Chipcon AS", "Aspex Technology", "F5 Networks",
199 "Programmable Silicon Solutions", "ChipWrights", "Acorn Networks", "Quicklogic",
200 "Kingmax Semiconductor", "BOPS", "Flasys", "BitBlitz Communications",
201 "eMemory Technology", "Procket Networks", "Purple Ray", "Trebia Networks",
202 "Delta Electronics", "Onex Communications", "Ample Communications", "Memory Experts Intl",
203 "Astute Networks", "Azanda Network Devices", "Dibcom", "Tekmos",
204 "API NetWorks", "Bay Microsystems", "Firecron Ltd", "Resonext Communications",
205 "Tachys Technologies", "Equator Technology", "Concept Computer", "SILCOM",
206 "3Dlabs", "c't Magazine", "Sanera Systems", "Silicon Packets",
207 "Viasystems Group", "Simtek", "Semicon Devices Singapore", "Satron Handelsges",
208 "Improv Systems", "INDUSYS GmbH", "Corrent", "Infrant Technologies",
209 "Ritek Corp", "empowerTel Networks", "Hypertec", "Cavium Networks",
210 "PLX Technology", "Massana Design", "Intrinsity", "Valence Semiconductor",
211 "Terawave Communications", "IceFyre Semiconductor", "Primarion", "Picochip Designs Ltd",
212 "Silverback Systems", "Jade Star Technologies", "Pijnenburg Securealink", "MemorySolutioN",
213 "Cambridge Silicon Radio", "Swissbit", "Nazomi Communications", "eWave System",
214 "Rockwell Collins", "Picocel Co., Ltd.", "Alphamosaic Ltd", "Sandburst",
215 "SiCon Video", "NanoAmp Solutions", "Ericsson Technology", "PrairieComm",
216 "Mitac International", "Layer N Networks", "MtekVision", "Allegro Networks",
217 "Marvell Semiconductors", "Netergy Microelectronic", "NVIDIA", "Internet Machines",
218 "Peak Electronics", "Litchfield Communication", "Accton Technology", "Teradiant Networks",
219 "Europe Technologies", "Cortina Systems", "RAM Components", "Raqia Networks",
220 "ClearSpeed", "Matsushita Battery", "Xelerated", "SimpleTech",
221 "Utron Technology", "Astec International", "AVM gmbH", "Redux Communications",
222 "Dot Hill Systems", "TeraChip"],
223["T-RAM Incorporated", "Innovics Wireless", "Teknovus", "KeyEye Communications",
224 "Runcom Technologies", "RedSwitch", "Dotcast", "Silicon Mountain Memory",
225 "Signia Technologies", "Pixim", "Galazar Networks", "White Electronic Designs",
226 "Patriot Scientific", "Neoaxiom Corporation", "3Y Power Technology", "Europe Technologies",
227 "Potentia Power Systems", "C-guys Incorporated", "Digital Communications Technology Incorporated", "Silicon-Based Technology",
228 "Fulcrum Microsystems", "Positivo Informatica Ltd", "XIOtech Corporation", "PortalPlayer",
229 "Zhiying Software", "Direct2Data", "Phonex Broadband", "Skyworks Solutions",
230 "Entropic Communications", "Pacific Force Technology", "Zensys A/S", "Legend Silicon Corp.",
231 "sci-worx GmbH", "Oasis Silicon Systems", "Renesas Technology", "Raza Microelectronics",
232 "Phyworks", "MediaTek", "Non-cents Productions", "US Modular",
233 "Wintegra Ltd", "Mathstar", "StarCore", "Oplus Technologies",
234 "Mindspeed", "Just Young Computer", "Radia Communications", "OCZ",
235 "Emuzed", "LOGIC Devices", "Inphi Corporation", "Quake Technologies",
236 "Vixel", "SolusTek", "Kongsberg Maritime", "Faraday Technology",
237 "Altium Ltd.", "Insyte", "ARM Ltd.", "DigiVision",
238 "Vativ Technologies", "Endicott Interconnect Technologies", "Pericom", "Bandspeed",
239 "LeWiz Communications", "CPU Technology", "Ramaxel Technology", "DSP Group",
240 "Axis Communications", "Legacy Electronics", "Chrontel", "Powerchip Semiconductor",
241 "MobilEye Technologies", "Excel Semiconductor", "A-DATA Technology", "VirtualDigm",
242 "G Skill Intl", "Quanta Computer", "Yield Microelectronics", "Afa Technologies",
243 "KINGBOX Technology Co. Ltd.", "Ceva", "iStor Networks", "Advance Modules",
244 "Microsoft", "Open-Silicon", "Goal Semiconductor", "ARC International",
245 "Simmtec", "Metanoia", "Key Stream", "Lowrance Electronics",
246 "Adimos", "SiGe Semiconductor", "Fodus Communications", "Credence Systems Corp.",
247 "Genesis Microchip Inc.", "Vihana, Inc.", "WIS Technologies", "GateChange Technologies",
248 "High Density Devices AS", "Synopsys", "Gigaram", "Enigma Semiconductor Inc.",
249 "Century Micro Inc.", "Icera Semiconductor", "Mediaworks Integrated Systems", "O'Neil Product Development",
250 "Supreme Top Technology Ltd.", "MicroDisplay Corporation", "Team Group Inc.", "Sinett Corporation",
251 "Toshiba Corporation", "Tensilica", "SiRF Technology", "Bacoc Inc.",
252 "SMaL Camera Technologies", "Thomson SC", "Airgo Networks", "Wisair Ltd.",
253 "SigmaTel", "Arkados", "Compete IT gmbH Co. KG", "Eudar Technology Inc.",
254 "Focus Enhancements", "Xyratex"],
255["Specular Networks", "Patriot Memory", "U-Chip Technology Corp.", "Silicon Optix",
256 "Greenfield Networks", "CompuRAM GmbH", "Stargen, Inc.", "NetCell Corporation",
257 "Excalibrus Technologies Ltd", "SCM Microsystems", "Xsigo Systems, Inc.", "CHIPS & Systems Inc",
258 "Tier 1 Multichip Solutions", "CWRL Labs", "Teradici", "Gigaram, Inc.",
259 "g2 Microsystems", "PowerFlash Semiconductor", "P.A. Semi, Inc.", "NovaTech Solutions, S.A.",
260 "c2 Microsystems, Inc.", "Level5 Networks", "COS Memory AG", "Innovasic Semiconductor",
261 "02IC Co. Ltd", "Tabula, Inc.", "Crucial Technology", "Chelsio Communications",
262 "Solarflare Communications", "Xambala Inc.", "EADS Astrium", "ATO Semicon Co. Ltd.",
263 "Imaging Works, Inc.", "Astute Networks, Inc.", "Tzero", "Emulex",
264 "Power-One", "Pulse~LINK Inc.", "Hon Hai Precision Industry", "White Rock Networks Inc.",
265 "Telegent Systems USA, Inc.", "Atrua Technologies, Inc.", "Acbel Polytech Inc.",
266 "eRide Inc.","ULi Electronics Inc.", "Magnum Semiconductor Inc.", "neoOne Technology, Inc.",
267 "Connex Technology, Inc.", "Stream Processors, Inc.", "Focus Enhancements", "Telecis Wireless, Inc.",
268 "uNav Microelectronics", "Tarari, Inc.", "Ambric, Inc.", "Newport Media, Inc.", "VMTS",
269 "Enuclia Semiconductor, Inc.", "Virtium Technology Inc.", "Solid State System Co., Ltd.", "Kian Tech LLC",
270 "Artimi", "Power Quotient International", "Avago Technologies", "ADTechnology", "Sigma Designs",
271 "SiCortex, Inc.", "Ventura Technology Group", "eASIC", "M.H.S. SAS", "Micro Star International", 
272 "Rapport Inc.", "Makway International", "Broad Reach Engineering Co.", 
273 "Semiconductor Mfg Intl Corp", "SiConnect", "FCI USA Inc.", "Validity Sensors", 
274 "Coney Technology Co. Ltd.", "Spans Logic", "Neterion Inc."]);
275
276$use_sysfs = -d '/sys/bus';
277
278# We consider that no data was written to this area of the SPD EEPROM if
279# all bytes read 0x00 or all bytes read 0xff
280sub spd_written(@)
281{
282        my $all_00 = 1;
283        my $all_ff = 1;
284       
285        foreach my $b (@_) {
286                $all_00 = 0 unless $b == 0x00;
287                $all_ff = 0 unless $b == 0xff;
288                return 1 unless $all_00 or $all_ff;
289        }
290
291        return 0;
292}
293
294sub parity($)
295{
296        my $n = shift;
297        my $parity = 0;
298
299        while ($n) {
300                $parity++ if ($n & 1);
301                $n >>= 1;
302        }
303
304        return ($parity & 1);
305}
306
307sub manufacturer(@)
308{
309        my @bytes = @_;
310        my $ai = 0;
311        my $first;
312
313        return ("Undefined", []) unless spd_written(@bytes);
314       
315        while (defined($first = shift(@bytes)) && $first == 0x7F) {
316                $ai++;
317        }
318
319        return ("Invalid", []) unless defined $first;
320        return ("Invalid", [$first, @bytes]) if parity($first) != 1;
321        return ("Unknown", \@bytes) unless (($first & 0x7F) - 1 <= $vendors[$ai]);
322
323        return ($vendors[$ai][($first & 0x7F) - 1], \@bytes);
324}
325
326sub manufacturer_data(@)
327{
328        my $hex = "";
329        my $asc = "";
330
331        return unless spd_written(@_);
332
333        foreach my $byte (@_) {
334                $hex .= sprintf("\%02X ", $byte);
335                $asc .= ($byte >= 32 && $byte < 127) ? chr($byte) : '?';
336        }
337
338        return "$hex(\"$asc\")";
339}
340
341sub part_number(@)
342{
343        my $asc = "";
344        my $byte;
345
346        while (defined ($byte = shift) && $byte >= 32 && $byte < 127) {
347                $asc .= chr($byte);
348        }
349
350        return ($asc eq "") ? "Undefined" : $asc;
351}
352
353sub printl ($$) # print a line w/ label and value
354{
355        my ($label, $value) = @_;
356        if ($opt_html) {
357                $label =~ s/</\&lt;/sg;
358                $label =~ s/>/\&gt;/sg;
359                $label =~ s/\n/<br>\n/sg;
360                $value =~ s/</\&lt;/sg;
361                $value =~ s/>/\&gt;/sg;
362                $value =~ s/\n/<br>\n/sg;
363                print "<tr><td valign=top>$label</td><td>$value</td></tr>\n";
364        } else {
365                my @values = split /\n/, $value;
366                printf "%-47s %-32s\n", $label, shift @values;
367                printf "%-47s %-32s\n", "", $_ foreach (@values);
368        }
369}
370
371sub printl2 ($$) # print a line w/ label and value (outside a table)
372{
373        my ($label, $value) = @_;
374        if ($opt_html) {
375                $label =~ s/</\&lt;/sg;
376                $label =~ s/>/\&gt;/sg;
377                $label =~ s/\n/<br>\n/sg;
378                $value =~ s/</\&lt;/sg;
379                $value =~ s/>/\&gt;/sg;
380                $value =~ s/\n/<br>\n/sg;
381        }
382        print "$label: $value\n";
383}
384
385sub prints ($) # print seperator w/ given text
386{
387        my ($label) = @_;
388        if ($opt_html) {
389                $label =~ s/</\&lt;/sg;
390                $label =~ s/>/\&gt;/sg;
391                $label =~ s/\n/<br>\n/sg;
392                print "<tr><td align=center colspan=2><b>$label</b></td></tr>\n";
393        } else {
394                print "\n---=== $label ===---\n";
395        }
396}
397
398sub printh ($) # print header w/ given text
399{
400        my ($label) = @_;
401        if ($opt_html) {
402                $label =~ s/</\&lt;/sg;
403                $label =~ s/>/\&gt;/sg;
404                $label =~ s/\n/<br>\n/sg;
405                print "<h1>$label</h1>\n";
406        } else {
407                print "\n$label\n";
408        }
409}
410
411# Parameter: bytes 0-63
412sub decode_sdr_sdram($)
413{
414        my $bytes = shift;
415        my ($l, $temp);
416
417#size computation
418
419        my $a = $bytes->[3];
420        my $b = $bytes->[4];
421        my $c = $bytes->[5];
422        my $d = $bytes->[17];
423        my $k=0;
424        my $ii=0;
425       
426        $ii = (($a) & 0x0f) + (( $b) & 0x0f) - 17;
427        if (( $c <= 8) && ( $d <= 8)) {
428                 $k = ( $c) * ( $d);
429        }
430       
431        if($ii > 0 && $ii <= 12 && $k > 0) {
432                printl "Size", ((1 << $ii) * $k) . " MB"; }
433        else { 
434                printl "INVALID SIZE", $a . "," . $b . "," . $c . "," . $d;
435        }
436
437        my @cas;
438        for ($ii = 0; $ii < 6; $ii++) {
439                push(@cas, $ii + 1) if ($bytes->[18] & (1 << $ii));
440        }
441
442        my $trcd;
443        my $trp;
444        my $tras;
445        my $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
446
447        $trcd =$bytes->[29];
448        $trp =$bytes->[27];;
449        $tras =$bytes->[30];
450
451        printl "tCL-tRCD-tRP-tRAS",
452                $cas[$#cas] . "-" .
453                ceil($trcd/$ctime) . "-" .
454                ceil($trp/$ctime) . "-" .
455                ceil($tras/$ctime);
456
457        $l = "Number of Row Address Bits";
458        if ($bytes->[3] == 0) { printl $l, "Undefined!"; }
459        elsif ($bytes->[3] == 1) { printl $l, "1/16"; }
460        elsif ($bytes->[3] == 2) { printl $l, "2/17"; }
461        elsif ($bytes->[3] == 3) { printl $l, "3/18"; }
462        else { printl $l, $bytes->[3]; }
463
464        $l = "Number of Col Address Bits";
465        if ($bytes->[4] == 0) { printl $l, "Undefined!"; }
466        elsif ($bytes->[4] == 1) { printl $l, "1/16"; }
467        elsif ($bytes->[4] == 2) { printl $l, "2/17"; }
468        elsif ($bytes->[4] == 3) { printl $l, "3/18"; }
469        else { printl $l, $bytes->[4]; }
470
471        $l = "Number of Module Rows";
472        if ($bytes->[5] == 0 ) { printl $l, "Undefined!"; }
473        else { printl $l, $bytes->[5]; }
474
475        $l = "Data Width";
476        if ($bytes->[7] > 1) {
477                printl $l, "Undefined!"
478        } else {
479                $temp = ($bytes->[7] * 256) + $bytes->[6];
480                printl $l, $temp;
481        }
482
483        $l = "Module Interface Signal Levels";
484        if ($bytes->[8] == 0) { printl $l, "5.0 Volt/TTL"; }
485        elsif ($bytes->[8] == 1) { printl $l, "LVTTL"; }
486        elsif ($bytes->[8] == 2) { printl $l, "HSTL 1.5"; }
487        elsif ($bytes->[8] == 3) { printl $l, "SSTL 3.3"; }
488        elsif ($bytes->[8] == 4) { printl $l, "SSTL 2.5"; }
489        elsif ($bytes->[8] == 255) { printl $l, "New Table"; }
490        else { printl $l, "Undefined!"; }
491
492        $l = "Module Configuration Type";
493        if ($bytes->[11] == 0) { printl $l, "No Parity"; }
494        elsif ($bytes->[11] == 1) { printl $l, "Parity"; }
495        elsif ($bytes->[11] == 2) { printl $l, "ECC"; }
496        else { printl $l, "Undefined!"; }
497
498        $l = "Refresh Type";
499        if ($bytes->[12] > 126) { printl $l, "Self Refreshing"; }
500        else { printl $l, "Not Self Refreshing"; }
501
502        $l = "Refresh Rate";
503        $temp = $bytes->[12] & 0x7f;
504        if ($temp == 0) { printl $l, "Normal (15.625 us)"; }
505        elsif ($temp == 1) { printl $l, "Reduced (3.9 us)"; }
506        elsif ($temp == 2) { printl $l, "Reduced (7.8 us)"; }
507        elsif ($temp == 3) { printl $l, "Extended (31.3 us)"; }
508        elsif ($temp == 4) { printl $l, "Extended (62.5 us)"; }
509        elsif ($temp == 5) { printl $l, "Extended (125 us)"; }
510        else { printl $l, "Undefined!"; }
511
512        $l = "Primary SDRAM Component Bank Config";
513        if ($bytes->[13] > 126) { printl $l, "Bank2 = 2 x Bank1"; }
514        else { printl $l, "No Bank2 OR Bank2 = Bank1 width"; }
515
516        $l = "Primary SDRAM Component Widths";
517        $temp = $bytes->[13] & 0x7f;
518        if ($temp == 0) { printl $l, "Undefined!\n"; }
519        else { printl $l, $temp; }
520
521        $l = "Error Checking SDRAM Component Bank Config";
522        if ($bytes->[14] > 126) { printl $l, "Bank2 = 2 x Bank1"; }
523        else { printl $l, "No Bank2 OR Bank2 = Bank1 width"; }
524
525        $l = "Error Checking SDRAM Component Widths";
526        $temp = $bytes->[14] & 0x7f;
527        if ($temp == 0) { printl $l, "Undefined!"; }
528        else { printl $l, $temp; }
529
530        $l = "Min Clock Delay for Back to Back Random Access";
531        if ($bytes->[15] == 0) { printl $l, "Undefined!"; }
532        else { printl $l, $bytes->[15]; }
533
534        prints "The Following Apply to SDRAM DIMMs ONLY";
535
536        $l = "Burst lengths supported";
537        my @array;
538        for ($ii = 0; $ii < 4; $ii++) {
539                push(@array, 1 << $ii) if ($bytes->[16] & (1 << $ii));
540        }
541        push(@array, "Page") if ($bytes->[16] & 128);
542        if (@array) { $temp = join ', ', @array; }
543        else { $temp = "None"; }
544        printl $l, $temp;
545
546        $l = "Number of Device Banks";
547        if ($bytes->[17] == 0) { printl $l, "Undefined/Reserved!"; }
548        else { printl $l, $bytes->[17]; }
549
550        $l = "Supported CAS Latencies";
551        if (@cas) { $temp = join ', ', @cas; }
552        else { $temp = "None"; }
553        printl $l, $temp;
554
555        $l = "Supported CS Latencies";
556        @array = ();
557        for ($ii = 0; $ii < 6; $ii++) {
558                push(@array, $ii) if ($bytes->[19] & (1 << $ii));
559        }
560        if (@array) { $temp = join ', ', @array; }
561        else { $temp = "None"; }
562        printl $l, $temp;
563
564        $l = "Supported WE Latencies";
565        @array = ();
566        for ($ii = 0; $ii < 6; $ii++) {
567                push(@array, $ii) if ($bytes->[20] & (1 << $ii));
568        }
569        if (@array) { $temp = join ', ', @array; }
570        else { $temp = "None"; }
571        printl $l, $temp;
572
573        if (@cas >= 1) {
574                $l = "Cycle Time (CAS ".$cas[$#cas].")";
575                $temp = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
576                printl $l, "$temp ns";
577
578                $l = "Access Time (CAS ".$cas[$#cas].")";
579                $temp = ($bytes->[10] >> 4) + ($bytes->[10] & 0xf) * 0.1;
580                printl $l, "$temp ns";
581        }
582
583        if (@cas >= 2 && spd_written(@$bytes[23..24])) {
584                $l = "Cycle Time (CAS ".$cas[$#cas-1].")";
585                $temp = $bytes->[23] >> 4;
586                if ($temp == 0) { printl $l, "Undefined!"; }
587                else {
588                        if ($temp < 4 ) { $temp=$temp + 15; }
589                        printl $l, $temp + (($bytes->[23] & 0xf) * 0.1) . " ns";
590                }
591
592                $l = "Access Time (CAS ".$cas[$#cas-1].")";
593                $temp = $bytes->[24] >> 4;
594                if ($temp == 0) { printl $l, "Undefined!"; }
595                else {
596                        if ($temp < 4 ) { $temp=$temp + 15; }
597                        printl $l, $temp + (($bytes->[24] & 0xf) * 0.1) . " ns";
598                }
599        }
600
601        if (@cas >= 3 && spd_written(@$bytes[25..26])) {
602                $l = "Cycle Time (CAS ".$cas[$#cas-2].")";
603                $temp = $bytes->[25] >> 2;
604                if ($temp == 0) { printl $l, "Undefined!"; }
605                else { printl $l, $temp + ($bytes->[25] & 0x3) * 0.25 . " ns"; }
606
607                $l = "Access Time (CAS ".$cas[$#cas-2].")";
608                $temp = $bytes->[26] >> 2;
609                if ($temp == 0) { printl $l, "Undefined!"; }
610                else { printl $l, $temp + ($bytes->[26] & 0x3) * 0.25 . " ns"; }
611        }
612
613        $l = "SDRAM Module Attributes";
614        $temp = "";
615        if ($bytes->[21] & 1) { $temp .= "Buffered Address/Control Inputs\n"; }
616        if ($bytes->[21] & 2) { $temp .= "Registered Address/Control Inputs\n"; }
617        if ($bytes->[21] & 4) { $temp .= "On card PLL (clock)\n"; }
618        if ($bytes->[21] & 8) { $temp .= "Buffered DQMB Inputs\n"; }
619        if ($bytes->[21] & 16) { $temp .= "Registered DQMB Inputs\n"; }
620        if ($bytes->[21] & 32) { $temp .= "Differential Clock Input\n"; }
621        if ($bytes->[21] & 64) { $temp .= "Redundant Row Address\n"; }
622        if ($bytes->[21] & 128) { $temp .= "Undefined (bit 7)\n"; }
623        if ($bytes->[21] == 0) { $temp .= "(None Reported)\n"; }
624        printl $l, $temp;
625
626        $l = "SDRAM Device Attributes (General)";
627        $temp = "";
628        if ($bytes->[22] & 1) { $temp .= "Supports Early RAS# Recharge\n"; }
629        if ($bytes->[22] & 2) { $temp .= "Supports Auto-Precharge\n"; }
630        if ($bytes->[22] & 4) { $temp .= "Supports Precharge All\n"; }
631        if ($bytes->[22] & 8) { $temp .= "Supports Write1/Read Burst\n"; }
632        if ($bytes->[22] & 16) { $temp .= "Lower VCC Tolerance: 5%\n"; }
633        else { $temp .= "Lower VCC Tolerance: 10%\n"; }
634        if ($bytes->[22] & 32) { $temp .= "Upper VCC Tolerance: 5%\n"; }
635        else { $temp .= "Upper VCC Tolerance: 10%\n"; }
636        if ($bytes->[22] & 64) { $temp .= "Undefined (bit 6)\n"; }
637        if ($bytes->[22] & 128) { $temp .= "Undefined (bit 7)\n"; }
638        printl $l, $temp;
639
640        prints "The Following are Required (for SDRAMs)";
641
642        $l = "Minimum Row Precharge Time";
643        if ($bytes->[27] == 0) { printl $l, "Undefined!"; }
644        else { printl $l, "$bytes->[27] ns"; }
645
646        $l = "Row Active to Row Active Min";
647        if ($bytes->[28] == 0) { printl $l, "Undefined!"; }
648        else { printl $l, "$bytes->[28] ns"; }
649
650        $l = "RAS to CAS Delay";
651        if ($bytes->[29] == 0) { printl $l, "Undefined!"; }
652        else { printl $l, "$bytes->[29] ns"; }
653
654        $l = "Min RAS Pulse Width";
655        if ($bytes->[30] == 0) { printl $l, "Undefined!"; }
656        else { printl $l, "$bytes->[30] ns"; }
657
658        prints "The Following are Required and Apply to ALL DIMMs";
659
660        $l = "Row Densities";
661        $temp = "";
662        if ($bytes->[31] & 1) { $temp .= "4 MByte\n"; }
663        if ($bytes->[31] & 2) { $temp .= "8 MByte\n"; }
664        if ($bytes->[31] & 4) { $temp .= "16 MByte\n"; }
665        if ($bytes->[31] & 8) { $temp .= "32 MByte\n"; }
666        if ($bytes->[31] & 16) { $temp .= "64 MByte\n"; }
667        if ($bytes->[31] & 32) { $temp .= "128 MByte\n"; }
668        if ($bytes->[31] & 64) { $temp .= "256 MByte\n"; }
669        if ($bytes->[31] & 128) { $temp .= "512 MByte\n"; }
670        if ($bytes->[31] == 0) { $temp .= "(Undefined! -- None Reported!)\n"; }
671        printl $l, $temp;
672
673        prints "The Following are Proposed and Apply to SDRAM DIMMs";
674
675        $l = "Command and Address Signal Setup Time";
676        $temp = (($bytes->[32] & 0x7f) >> 4) + ($bytes->[32] & 0xf) * 0.1;
677        printl $l, (($bytes->[32] >> 7) ? -$temp : $temp) . " ns";
678
679        $l = "Command and Address Signal Hold Time";
680        $temp = (($bytes->[33] & 0x7f) >> 4) + ($bytes->[33] & 0xf) * 0.1;
681        printl $l, (($bytes->[33] >> 7) ? -$temp : $temp) . " ns";
682
683        $l = "Data Signal Setup Time";
684        $temp = (($bytes->[34] & 0x7f) >> 4) + ($bytes->[34] & 0xf) * 0.1;
685        printl $l, (($bytes->[34] >> 7) ? -$temp : $temp) . " ns";
686
687        $l = "Data Signal Hold Time";
688        $temp = (($bytes->[35] & 0x7f) >> 4) + ($bytes->[35] & 0xf) * 0.1;
689        printl $l, (($bytes->[35] >> 7) ? -$temp : $temp) . " ns";
690}
691
692# Parameter: bytes 0-63
693sub decode_ddr_sdram($)
694{
695        my $bytes = shift;
696        my ($l, $temp);
697
698        $l = "Maximum module speed";
699        $temp = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
700        my $ddrclk = 2 * (1000 / $temp);
701        my $tbits = ($bytes->[7] * 256) + $bytes->[6];
702        if (($bytes->[11] == 2) || ($bytes->[11] == 1)) { $tbits = $tbits - 8; }
703        my $pcclk = int ($ddrclk * $tbits / 8);
704        $pcclk += 100 if ($pcclk % 100) >= 50; # Round properly
705        $pcclk = $pcclk - ($pcclk % 100);
706        $ddrclk = int ($ddrclk);
707        printl $l, "${ddrclk}MHz (PC${pcclk})";
708
709#size computation
710
711        my $a = $bytes->[3];
712        my $b = $bytes->[4];
713        my $c = $bytes->[5];
714        my $d = $bytes->[17];
715        my $k=0;
716        my $ii=0;
717       
718        $ii = (($a) & 0x0f) + (( $b) & 0x0f) - 17;
719        if (( $c <= 8) && ( $d <= 8)) {
720                 $k = ( $c) * ( $d);
721        }
722       
723        if($ii > 0 && $ii <= 12 && $k > 0) {
724                printl "Size", ((1 << $ii) * $k) . " MB"; }
725        else { 
726                printl "INVALID SIZE", $a . "," . $b . "," . $c . "," . $d;
727        }
728
729        my $highestCAS = 0;
730        my $trcd;
731        my $trp;
732        my $tras;
733        my $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
734
735        if ($bytes->[18] & 1) { $highestCAS = 1; }
736        if ($bytes->[18] & 2) { $highestCAS = 1.5; }
737        if ($bytes->[18] & 4) { $highestCAS = 2; }
738        if ($bytes->[18] & 8) { $highestCAS = 2.5; }
739        if ($bytes->[18] & 16) { $highestCAS = 3.5; }
740        if ($bytes->[18] & 32) { $highestCAS = 4; }
741        if ($bytes->[18] & 64) { $highestCAS = 4.5; }
742        if ($bytes->[18] & 128) { $highestCAS = 5; }
743       
744        $trcd =($bytes->[29] >> 2)+(($bytes->[29] & 3)*0.25);
745        $trp =($bytes->[27] >> 2)+(($bytes->[27] & 3)*0.25);
746        $tras = $bytes->[30];
747
748        printl "tCL-tRCD-tRP-tRAS",
749                $highestCAS . "-" .
750                ceil($trcd/$ctime) . "-" .
751                ceil($trp/$ctime) . "-" .
752                ceil($tras/$ctime);
753}
754
755# Parameter: bytes 0-63
756sub decode_ddr2_sdram($)
757{
758        my $bytes = shift;
759        my ($l, $temp);
760
761        $l = "Maximum module speed";
762        $temp = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
763        my $ddrclk = 4 * (1000 / $temp);
764        my $tbits = ($bytes->[7] * 256) + $bytes->[6];
765        if (($bytes->[11] == 2) || ($bytes->[11] == 1)) { $tbits = $tbits - 8; }
766        my $pcclk = int ($ddrclk * $tbits / 8);
767        $pcclk += 100 if ($pcclk % 100) >= 50; # Round properly
768        $pcclk = $pcclk - ($pcclk % 100);
769        $ddrclk = int ($ddrclk);
770        printl $l, "${ddrclk}MHz (PC${pcclk})";
771
772#size computation
773        my $a = $bytes->[3];
774        my $b = $bytes->[4];
775        my $c = $bytes->[5];
776        my $d = $bytes->[17];
777        my $k=0;
778        my $ii=0;
779
780        $ii = ($a & 0x0f) + ($b & 0x0f) - 17;
781        $k = (($c & 0x7) + 1) * $d;
782       
783        if($ii > 0 && $ii <= 12 && $k > 0) {
784                printl "Size", ((1 << $ii) * $k) . " MB"; 
785        } else {
786                printl "INVALID SIZE", $a . "," . $b . "," . $c . "," . $d;
787        }
788
789        my $highestCAS = 0;
790        my $trcd;
791        my $trp;
792        my $tras;
793        my $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
794
795        if ($bytes->[18] & 4) { $highestCAS = 2; }
796        if ($bytes->[18] & 8) { $highestCAS = 3; }
797        if ($bytes->[18] & 16) { $highestCAS = 4; }
798        if ($bytes->[18] & 32) { $highestCAS = 5; }
799       
800        $trcd =($bytes->[29] >> 2)+(($bytes->[29] & 3)*0.25);
801        $trp =($bytes->[27] >> 2)+(($bytes->[27] & 3)*0.25);
802        $tras =$bytes->[30];
803
804        printl "tCL-tRCD-tRP-tRAS",
805                $highestCAS . "-" .
806                ceil($trcd/$ctime) . "-" .
807                ceil($trp/$ctime) . "-" .
808                ceil($tras/$ctime);
809}
810
811%decode_callback = (
812        "SDR SDRAM"     => \&decode_sdr_sdram,
813        "DDR SDRAM"     => \&decode_ddr_sdram,
814        "DDR2 SDRAM"    => \&decode_ddr2_sdram,
815);
816
817# Parameter: bytes 64-127
818sub decode_intel_spec_freq($)
819{
820        my $bytes = shift;
821        my ($l, $temp);
822
823        $l = "Intel Specification for Frequency";
824        if ($bytes->[62] == 0x66) { $temp = "66MHz\n"; }
825        elsif ($bytes->[62] == 100) { $temp = "100MHz or 133MHz\n"; }
826        elsif ($bytes->[62] == 133) { $temp = "133MHz\n"; }
827        else { $temp = "Undefined!\n"; }
828        printl $l, $temp;
829
830        $l = "Intel Spec Details for 100MHz Support";
831        $temp="";
832        if ($bytes->[63] & 1) { $temp .= "Intel Concurrent Auto-precharge\n"; }
833        if ($bytes->[63] & 2) { $temp .= "CAS Latency = 2\n"; }
834        if ($bytes->[63] & 4) { $temp .= "CAS Latency = 3\n"; }
835        if ($bytes->[63] & 8) { $temp .= "Junction Temp A (100 degrees C)\n"; }
836        else { $temp .= "Junction Temp B (90 degrees C)\n"; }
837        if ($bytes->[63] & 16) { $temp .= "CLK 3 Connected\n"; }
838        if ($bytes->[63] & 32) { $temp .= "CLK 2 Connected\n"; }
839        if ($bytes->[63] & 64) { $temp .= "CLK 1 Connected\n"; }
840        if ($bytes->[63] & 128) { $temp .= "CLK 0 Connected\n"; }
841        if (($bytes->[63] & 192) == 192) { $temp .= "Double-sided DIMM\n"; }
842        elsif (($bytes->[63] & 192) != 0) { $temp .= "Single-sided DIMM\n"; }
843        printl $l, $temp;
844}
845
846sub readspd64 ($$) { # reads 64 bytes from SPD-EEPROM
847        my ($offset, $dimm_i) = @_;
848        my @bytes;
849        if ($use_sysfs) {
850                # Kernel 2.6 with sysfs
851                sysopen(HANDLE, "/sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom", O_RDONLY)
852                        or die "Cannot open /sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom";
853                binmode HANDLE;
854                sysseek(HANDLE, $offset, SEEK_SET);
855                sysread(HANDLE, my $eeprom, 64);
856                close HANDLE;
857                @bytes = unpack("C64", $eeprom);
858        } else {
859                # Kernel 2.4 with procfs
860                for my $i (0 .. 3) {
861                        my $hexoff = sprintf('%02x', $offset + $i * 16);
862                        push @bytes, split(" ", `cat /proc/sys/dev/sensors/$dimm_i/$hexoff`);
863                }
864        }
865        return @bytes;
866}
867
868for (@ARGV) {
869    if (/-h/) {
870                print "Usage: $0 [-c] [-f [-b]]\n",
871                        "       $0 -h\n\n",
872                        "  -f, --format            print nice html output\n",
873                        "  -b, --bodyonly          don't print html header\n",
874                        "                          (useful for postprocessing the output)\n",
875                        "  -c, --checksum          decode completely even if checksum fails\n",
876                        "  -h, --help              display this usage summary\n";
877                exit;
878    }
879    $opt_html = 1 if (/-f/);
880    $opt_bodyonly = 1 if (/-b/);
881    $opt_igncheck = 1 if (/-c/);
882}
883$opt_body = $opt_html && ! $opt_bodyonly;
884
885if ($opt_body)
886{
887        print "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
888              "<html><head>\n",
889                  "\t<meta HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">\n",
890                  "\t<title>PC DIMM Serial Presence Detect Tester/Decoder Output</title>\n",
891                  "</head><body>\n";
892}
893
894printh 'PC DIMM Serial Presence Detect Tester/Decoder
895By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner,
896Jean Delvare and others
897Version 2.10.1';
898
899
900my $dimm_count=0;
901if ($use_sysfs) { $_=`ls /sys/bus/i2c/drivers/eeprom`; }
902else { $_=`ls /proc/sys/dev/sensors/`; }
903my @dimm_list=split();
904
905for my $i ( 0 .. $#dimm_list ) {
906        $_=$dimm_list[$i];
907        if (($use_sysfs && /^\d+-\d+$/)
908         || (!$use_sysfs && /^eeprom-/)) {
909                print "<b><u>" if $opt_html;
910                printl2 "\n\nDecoding EEPROM", ($use_sysfs ?
911                        "/sys/bus/i2c/drivers/eeprom/$dimm_list[$i]" :
912                        "/proc/sys/dev/sensors/$dimm_list[$i]");
913                print "</u></b>" if $opt_html;
914                print "<table border=1>\n" if $opt_html;
915                if (($use_sysfs && /^[^-]+-([^-]+)$/)
916                 || (!$use_sysfs && /^[^-]+-[^-]+-[^-]+-([^-]+)$/)) {
917                        my $dimm_num=$1 - 49;
918                        printl "Guessing DIMM is in", "bank $dimm_num";
919                }
920
921# Decode first 3 bytes (0-2)
922                prints "The Following is Required Data and is Applicable to all DIMM Types";
923
924                my @bytes = readspd64(0, $dimm_list[$i]);
925                my $dimm_checksum = 0;
926                $dimm_checksum += $bytes[$_] foreach (0 .. 62);
927                $dimm_checksum &= 0xff;
928
929                my $l = "EEPROM Checksum of bytes 0-62";
930                printl $l, ($bytes[63] == $dimm_checksum ?
931                        sprintf("OK (0x%.2X)", $bytes[63]):
932                        sprintf("Bad\n(found 0x%.2X, calculated 0x%.2X)\n",
933                                $bytes[63], $dimm_checksum));
934
935                next unless $bytes[63] == $dimm_checksum or $opt_igncheck;
936               
937                $dimm_count++;
938                # Simple heuristic to detect Rambus
939                my $is_rambus = $bytes[0] < 4;
940                my $temp;
941                if ($is_rambus) {
942                        if ($bytes[0] == 1) { $temp = "0.7"; }
943                        elsif ($bytes[0] == 2) { $temp = "1.0"; }
944                        elsif ($bytes[0] == 0 || $bytes[0] == 255) { $temp = "Invalid"; }
945                        else { $temp = "Reserved"; }
946                        printl "SPD Revision", $temp;
947                } else {
948                        printl "# of bytes written to SDRAM EEPROM",
949                               $bytes[0];
950                }
951
952                $l = "Total number of bytes in EEPROM";
953                if ($bytes[1] <= 14) {
954                        printl $l, 2**$bytes[1];
955                } elsif ($bytes[1] == 0) {
956                        printl $l, "RFU"; 
957                } else { printl $l, "ERROR!"; }
958
959                $l = "Fundamental Memory type";
960                my $type = "Unknown";
961                if ($is_rambus) {
962                        if ($bytes[2] == 1) { $type = "Direct Rambus"; }
963                        elsif ($bytes[2] == 17) { $type = "Rambus"; }
964                } else {
965                        if ($bytes[2] == 1) { $type = "FPM DRAM"; }
966                        elsif ($bytes[2] == 2) { $type = "EDO"; }
967                        elsif ($bytes[2] == 3) { $type = "Pipelined Nibble"; }
968                        elsif ($bytes[2] == 4) { $type = "SDR SDRAM"; }
969                        elsif ($bytes[2] == 5) { $type = "Multiplexed ROM"; }
970                        elsif ($bytes[2] == 6) { $type = "DDR SGRAM"; }
971                        elsif ($bytes[2] == 7) { $type = "DDR SDRAM"; }
972                        elsif ($bytes[2] == 8) { $type = "DDR2 SDRAM"; }
973                }
974                printl $l, $type;
975
976# Decode next 59 bytes (3-61, depend on memory type)
977                $decode_callback{$type}->(\@bytes)
978                        if exists $decode_callback{$type};
979
980# Decode next 2 bytes (62-63)
981                printl "SPD Revision code ", sprintf("%x", $bytes[62]);
982
983# Decode next 35 bytes (64-98, common to all memory types)
984                @bytes = readspd64(64, $dimm_list[$i]);
985               
986                $l = "Manufacturer";
987                # $extra is a reference to an array containing up to
988                # 7 extra bytes from the Manufacturer field. Sometimes
989                # these bytes are filled with interesting data.
990                ($temp, my $extra) = manufacturer(@bytes[0..7]);
991                printl $l, $temp;
992                $l = "Custom Manufacturer Data";
993                $temp = manufacturer_data(@{$extra});
994                printl $l, $temp if defined $temp;
995               
996                if (spd_written($bytes[8])) {
997                        # Try the location code as ASCII first, as earlier specifications
998                        # suggested this. As newer specifications don't mention it anymore,
999                        # we still fall back to binary.
1000                        $l = "Manufacturing Location Code";
1001                        $temp = (chr($bytes[8]) =~ m/^[\w\d]$/) ? chr($bytes[8])
1002                              : sprintf("0x%.2X", $bytes[8]);
1003                        printl $l, $temp;
1004                }
1005               
1006                $l = "Part Number";
1007                $temp = part_number(@bytes[9..26]);
1008                printl $l, $temp;
1009               
1010                if (spd_written(@bytes[27..28])) {
1011                        $l = "Revision Code";
1012                        $temp = sprintf("0x%02X%02X\n", @bytes[27..28]);
1013                        printl $l, $temp;
1014                }
1015               
1016                if (spd_written(@bytes[29..30])) {
1017                        $l = "Manufacturing Date";
1018                        # In theory the year and week are in BCD format, but
1019                        # this is not always true in practice :(
1020                        if (($bytes[29] & 0xf0) <= 0x90
1021                         && ($bytes[29] & 0x0f) <= 0x09
1022                         && ($bytes[30] & 0xf0) <= 0x90
1023                         && ($bytes[30] & 0x0f) <= 0x09) {
1024                                # Note that this heuristic will break in year 2080
1025                                $temp = sprintf("%d%02X-W%02X\n",
1026                                                $bytes[29] >= 0x80 ? 19 : 20,
1027                                                @bytes[29..30]);
1028                        } else {
1029                                $temp = sprintf("0x%02X%02X\n",
1030                                                @bytes[29..30]);
1031                        }
1032                        printl $l, $temp;
1033                }
1034               
1035                if (spd_written(@bytes[31..34])) {
1036                        $l = "Assembly Serial Number";
1037                        $temp = sprintf("0x%02X%02X%02X%02X\n",
1038                                        @bytes[31..34]);
1039                        printl $l, $temp;
1040                }
1041
1042# Next 27 bytes (99-125) are manufacturer specific, can't decode
1043
1044# Last 2 bytes (126-127) are reserved, Intel used them as an extension
1045                if ($type eq "SDR SDRAM") {
1046                        decode_intel_spec_freq(\@bytes);
1047                }
1048               
1049                print "</table>\n" if $opt_html;
1050        }
1051}
1052printl2 "\n\nNumber of SDRAM DIMMs detected and decoded", $dimm_count;
1053
1054print "</body></html>\n" if $opt_body;
Note: See TracBrowser for help on using the browser.