Project import generated by Copybara.
GitOrigin-RevId: 07a2c2bb35d1229644d413b92f6369def6a29f3f
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..16084d3
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,3 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(LOCAL_PATH)/tools/Android.mk
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..15ff761
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,145 @@
+i2c-tools CHANGES
+-----------------
+
+4.0 (2017-10-30)
+ tools: Fix build with recent compilers (gcc 4.6+)
+ Add examples to the manual pages
+ README: Clarify licenses
+ Mention the current maintainer
+ decode-dimms: Decode module configuration type of DDR2 SDRAM
+ Decode bus width extension of DDR3 SDRAM
+ Don't choke when no EEPROM is found
+ Don't make columns larger than they need to be
+ Make side-by-side output more robust
+ Print module organization of DDR SDRAM
+ Merge cells by default in side-by-side output
+ Print extra timing values of DDR SDRAM
+ Print DDR and DDR2 core timings for all supported CAS values
+ Print DDR2 equivalent speed of tCK max
+ Don't print undefined DDR2 SDRAM timings
+ Print SDR, DDR, DDR2, DDR3 core timings for all standard speeds
+ Update manufacturer IDs
+ Make DDR3 manufacturer count parity error non-fatal
+ Strip former manufacturer name in side-by-side output mode
+ Remove duplicate "ns" in SDR timings
+ Add section headers for SDR modules
+ Fix decoding of SDR SPD revision
+ Prevent hang on reserved DDR3 module type
+ Decode more DDR3 module types
+ Fix DDR3 tRAS decoding
+ Fix DDR3 core timings rounding
+ Round down PC3 numbers to comply with Jedec
+ Don't print the DDR3 time bases
+ Decode the FTB fields of DDR3 tCk, tAA, tRCD, tRP and tRC
+ Fix speed and PC3 number of high-speed DDR3 modules
+ Decode DDR3 reference card revision
+ Print width of all known DDR3 module types
+ Print physical characteristics for all DDR3 module types
+ Don't print raw SSTE32882 register values
+ Add support for Load Reduced DIMM (LRDIMM) DDR3 modules
+ Fully decode the DDR3 SDRAM Device Type field
+ Fix DDR3 extended temp range refresh rate decoding
+ Encode "degrees" to HTML degree symbol
+ Generate XHTML 1.1 compliant markup
+ Add a manual page
+ Correctly check for out-of-bounds vendor ID
+ Update manufacturer IDs (JEP106AQ)
+ decode-vaio: Add a manual page
+ eeprog: Add a manual page
+ Moved to a separate subdirectory
+ Increase delay after writes
+ eeprom: Add a manual page
+ Marked as deprecated
+ eepromer: Add a manual page
+ Marked as deprecated
+ i2cdetect: Do a best effort detection if functionality is missing
+ Clarify the SMBus commands used for probing by default
+ i2c-dev.h: Minimize differences with kernel flavor
+ Move SMBus helper functions to include/i2c/smbus.h
+ i2c-stub-from-dump: Be more tolerant on input dump format
+ library: New libi2c library
+ Properly propagate real error codes on read errors
+ Use I2C_SMBUS_BLOCK_MAX instead of hard-coding 32
+ lib/smbus.c: Add missing include which was causing a build error
+ py-smbus: Fix module level docs
+ Add support for python 3
+
+3.1.0 (2011-12-04)
+ decode-dimms: Decode module configuration type of DDR SDRAM
+ Decode refresh rate of DDR SDRAM
+ Add support for the at24 kernel driver
+ i2c-dev.h: Make value arrays const for block write functions
+ i2cset: Add support for SMBus and I2C block writes
+ Removed obsolete method to specify value mask
+ More stringent parameter validation
+
+3.0.3 (2010-12-12)
+ Makefile: Let the environment set CC and CFLAGS
+ Integrate py-smbus into the build system
+ README: Point users to the linux-i2c mailing list
+ decode-dimms: Handle CRC of FB-DIMM and DDR3 SDRAM memory modules
+ Add support for DDR3 SDRAM
+ Fix decoding of SDR SDRAM bytes 12-14
+ Add side-by-side formatting option
+ Add merged cells formatting option
+ Try harder to decode the manufacturing date
+ Handle read errors on sysfs
+ Decode voltage interface level of DDR SDRAM
+ decode-xeon: Delete
+ eepromer: Fix array initialization overrun
+ i2cdetect: Drop legacy reference to ISA bus
+ i2cset: Add support for short writes with PEC
+ i2c-stub-from-dump: Use udev settle to speed up initialization
+ Unload i2c-stub automatically if needed
+ Add support for multiple dumps
+ tools: Properly handle /dev/i2c not being a directory
+ Increase limit on I2C bus number
+
+3.0.2 (2008-11-29)
+ i2c-dev.h: Drop I2C_FUNC_SMBUS_*I2C_BLOCK_2 defines
+ decode-dimms: Add support for little-endian word hexdumps
+ Only export the ceil function from POSIX
+ decode-vaio: Remove history
+ i2cdetect: Support i2c bus passed by name
+ Shorten the usage message
+ i2cdump: Support i2c bus passed by name
+ Shorten the usage message
+ Restrict the chip address to 0x03-0x77
+ Split the functionality checking code into a separate function
+ Better error message on missing adapter functionality
+ i2cget: Support i2c bus passed by name
+ Shorten the usage message
+ Better error message on missing adapter functionality
+ i2cset: Support i2c bus passed by name
+ Shorten the usage message
+ Restrict the chip address to 0x03-0x77
+ Split the code into several functions for clarity
+ Add support for short writes (SMBus send byte)
+ Better error message on missing adapter functionality
+ Set the data value mask with -m
+ Make reading back the written value optional
+ i2c-stub-from-dump: Add support for partial dumps
+ Report if only garbage is found in dump file
+ Behave properly when i2c-stub is already loaded
+ Stop on i2cset error
+
+3.0.1 (2008-04-04)
+ Drop the trailing .pl from all perl script names
+ decode-dimms: Fix DDR2 SDRAM module speed decoding
+ Update manufacturer IDs
+ Don't print anything by default if checksum fails
+ Decode all DDR2 SDRAM timing information
+ Add support for reading SPD data from hexdumps
+ Make command line parsing more robust
+ decode-vaio: Private data might not be readable by non-root users
+ Print the asset tag
+ Fix the timestamp decoding
+ i2cdump: Fix I2C block mode error code
+ Remove man page reference to hardware monitoring chips
+ Let the user specify a register range
+ i2cset: Final status messages go to stdout
+ Return success even when readback fails or doesn't match
+ i2c-stub-from-dump: New helper script to use with i2c-stub
+
+3.0.0 (2007-10-14)
+ Initial release
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/COPYING.LGPL b/COPYING.LGPL
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/COPYING.LGPL
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/MODULE_LICENSE_LGPL b/MODULE_LICENSE_LGPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_LGPL
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c85317c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,50 @@
+# I2C tools for Linux
+#
+# Copyright (C) 2007-2012 Jean Delvare <jdelvare@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+DESTDIR =
+prefix = /usr/local
+bindir = $(prefix)/bin
+sbindir = $(prefix)/sbin
+mandir = $(prefix)/share/man
+man8dir = $(mandir)/man8
+incdir = $(prefix)/include
+libdir = $(prefix)/lib
+
+INSTALL := install
+INSTALL_DATA := $(INSTALL) -m 644
+INSTALL_DIR := $(INSTALL) -m 755 -d
+INSTALL_PROGRAM := $(INSTALL) -m 755
+LN := ln -sf
+RM := rm -f
+
+CC ?= gcc
+AR ?= ar
+
+CFLAGS ?= -O2
+# When debugging, use the following instead
+#CFLAGS := -O -g
+CFLAGS += -Wall
+SOCFLAGS := -fpic -D_REENTRANT $(CFLAGS)
+
+USE_STATIC_LIB ?= 0
+BUILD_STATIC_LIB ?= 1
+ifeq ($(USE_STATIC_LIB),1)
+BUILD_STATIC_LIB := 1
+endif
+
+KERNELVERSION := $(shell uname -r)
+
+.PHONY: all strip clean install uninstall
+
+all:
+
+EXTRA :=
+#EXTRA += eeprog py-smbus
+SRCDIRS := include lib eeprom stub tools $(EXTRA)
+include $(SRCDIRS:%=%/Module.mk)
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/README b/README
new file mode 100644
index 0000000..4ccdf5b
--- /dev/null
+++ b/README
@@ -0,0 +1,97 @@
+I2C TOOLS FOR LINUX
+===================
+
+This package contains an heterogeneous set of I2C tools for the Linux kernel
+as well as an I2C library. The tools were originally part of the lm-sensors
+project but were finally split into their own package for convenience. The
+library is used by some of the tools, but can also be used by third-party
+applications. The tools and library compile, run and have been tested on
+GNU/Linux.
+
+The latest version of the code can be downloaded from:
+ https://i2c.wiki.kernel.org/index.php/I2C_Tools
+
+
+CONTENTS
+--------
+
+The various tools included in this package are grouped by category, each
+category has its own sub-directory:
+
+* eeprom
+ Perl scripts for decoding different types of EEPROMs (SPD, EDID...) These
+ scripts rely on the "eeprom" kernel driver. They are installed by default.
+
+* eeprog, eepromer
+ Tools for writing to EEPROMs. These tools rely on the "i2c-dev" kernel
+ driver. They are not installed by default.
+
+* include
+ C/C++ header files for I2C and SMBus access over i2c-dev. Installed by
+ default.
+
+* lib
+ The I2C library, used by eeprog, py-smbus and tools. Installed by
+ default.
+
+* py-smbus
+ Python wrapper for SMBus access over i2c-dev. Not installed by default.
+
+* stub
+ A helper script to use with the i2c-stub kernel driver. Installed by
+ default.
+
+* tools
+ I2C device detection and register dump tools. These tools rely on the
+ "i2c-dev" kernel driver. They are installed by default.
+
+
+LICENSE
+-------
+
+Check the documentation of individual tools for licensing information.
+The library is released under the LGPL version 2.1 or later, while most
+tools are released under the GPL version 2 or later, but there are a few
+exceptions.
+
+
+INSTALLATION
+------------
+
+There's no configure script, so simply run "make" to build the library and
+tools, and "make install" to install them. You also can use "make uninstall"
+to remove all the files you installed. By default, files are installed in
+/usr/local but you can change the location by editing the Makefile file and
+setting prefix to wherever you want. You may change the C compiler and the
+compilation flags as well, and also decide whether to build the static
+library or not.
+
+Optionally, you can run "make strip" prior to "make install" if you want
+smaller binaries. However, be aware that this will prevent any further
+attempt to debug the library and tools.
+
+If you wish to include sub-directories that are not enabled by default, then
+just set them via the EXTRA make variable. For example, to build py-smbus,
+do:
+ $ make EXTRA="py-smbus"
+
+
+DOCUMENTATION
+-------------
+
+The main tools have manual pages, which are installed by "make install".
+See these manual pages for command line interface details and tool specific
+information.
+
+The other tools come with simple text documentation, which isn't installed.
+
+
+QUESTIONS AND BUG REPORTS
+-------------------------
+
+Please post your questions and bug reports to the linux-i2c mailing list:
+ linux-i2c@vger.kernel.org
+with Cc to the current maintainer:
+ Jean Delvare <jdelvare@suse.de>
+For additional information about this list, see:
+ http://vger.kernel.org/vger-lists.html#linux-i2c
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..30cd08e
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,120 @@
+i2c-tools (3.1.1-1) unstable; urgency=medium
+
+ * New upstream version.
+ * Refresh 01-decode-dimms-no-eeprom.diff.
+ * Install new manpages.
+ * Set CFLAGS, CPPFLAGS and LDFLAGS to build with hardening support.
+ * Recommends read-edid. (Closes: #740541).
+ * Bump Standards-Version to 3.9.5 (no changes).
+
+ -- Aurelien Jarno <aurel32@debian.org> Sun, 02 Mar 2014 23:32:19 +0100
+
+i2c-tools (3.1.0-2) unstable; urgency=low
+
+ * decode-dimms: exit gracefully when no EEPROM are found on the system
+ (Closes: #681057).
+
+ -- Aurelien Jarno <aurel32@debian.org> Sat, 14 Jul 2012 17:09:59 +0200
+
+i2c-tools (3.1.0-1) unstable; urgency=low
+
+ * New usptream version. (Closes: #670956).
+ * Bump Standards-Version to 3.9.3 (no changes).
+
+ -- Aurelien Jarno <aurel32@debian.org> Tue, 01 May 2012 13:28:19 +0200
+
+i2c-tools (3.0.3-5) unstable; urgency=low
+
+ * Switch to dh_python2:
+ - Build-depends on python-all-dev (>= 2.6.6-3~).
+ - Drop build-depends on python-support.
+ - Remove XB-Python-Version field.
+ - Call dh_python2 instead of dh_pysupport.
+ - Replace debian/pyversion by X-Python-Version.
+ * Add build-arch and build-indep targets.
+
+ -- Aurelien Jarno <aurel32@debian.org> Mon, 27 Jun 2011 23:42:49 +0200
+
+i2c-tools (3.0.3-4) unstable; urgency=low
+
+ * Don't try to change the i2c devices permissions if MAKEDEV doesn't
+ create them. (Closes: #622156).
+
+ -- Aurelien Jarno <aurel32@debian.org> Sun, 10 Apr 2011 19:19:15 +0200
+
+i2c-tools (3.0.3-3) unstable; urgency=low
+
+ * Fix inverted logic in postinst. (Closes: #621898).
+
+ -- Aurelien Jarno <aurel32@debian.org> Sun, 10 Apr 2011 10:47:39 +0200
+
+i2c-tools (3.0.3-2) unstable; urgency=low
+
+ * Fix a typo in i2cset manpage. (Closes: #602368).
+ * Don't try to detect udev before creating a device, it's already done
+ by MAKEDEV. (Closes: #620785).
+ * Bump standard versions to 3.9.2 (no changes).
+
+ -- Aurelien Jarno <aurel32@debian.org> Sat, 09 Apr 2011 14:25:18 +0200
+
+i2c-tools (3.0.3-1) unstable; urgency=low
+
+ * New upstream version.
+ * Bump standard versions to 3.9.1 (no changes).
+
+ -- Aurelien Jarno <aurel32@debian.org> Sun, 06 Feb 2011 15:44:11 +0100
+
+i2c-tools (3.0.2-5) unstable; urgency=low
+
+ * Add depends on adduser to i2ctools. (Closes: #608835).
+
+ -- Aurelien Jarno <aurel32@debian.org> Mon, 03 Jan 2011 23:22:26 +0100
+
+i2c-tools (3.0.2-4) unstable; urgency=low
+
+ * Add ${misc:Depends} to all binaries.
+ * Create an i2c group on install, and add a udev file to set the group
+ and the mode of /dev/i2c-* nodes.
+
+ -- Aurelien Jarno <aurel32@debian.org> Thu, 14 Jan 2010 06:39:06 +0100
+
+i2c-tools (3.0.2-3) unstable; urgency=low
+
+ * Correctly call dh_pysupport. (Closes: #556147).
+
+ -- Aurelien Jarno <aurel32@debian.org> Sat, 14 Nov 2009 14:27:26 +0100
+
+i2c-tools (3.0.2-2) unstable; urgency=low
+
+ * i2c-tools: depends on udev | makedev and create /dev/i2c-0 (Closes:
+ bug#546871).
+ * Bump standard versions to 3.8.3 (no changes).
+
+ -- Aurelien Jarno <aurel32@debian.org> Wed, 14 Oct 2009 00:19:49 +0200
+
+i2c-tools (3.0.2-1) unstable; urgency=low
+
+ * New upstream version.
+ * Bump standard versions to 3.8.0 (no changes).
+
+ -- Aurelien Jarno <aurel32@debian.org> Wed, 28 Jan 2009 23:37:31 +0100
+
+i2c-tools (3.0.1-1) unstable; urgency=low
+
+ * New upstream version.
+
+ -- Aurelien Jarno <aurel32@debian.org> Fri, 04 Apr 2008 22:14:04 +0200
+
+i2c-tools (3.0.0-2) unstable; urgency=low
+
+ * Bump standard versions to 3.7.3 (no changes).
+ * Fix python-pysmbus description.
+ * Fix debian/copyright.
+
+ -- Aurelien Jarno <aurel32@debian.org> Sun, 02 Mar 2008 18:54:37 +0100
+
+i2c-tools (3.0.0-1) unstable; urgency=low
+
+ * New upstream package split from lm-sensors.
+
+ -- Aurelien Jarno <aurel32@debian.org> Tue, 27 Nov 2007 16:46:35 +0100
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..42c26a5
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,41 @@
+Source: i2c-tools
+Section: utils
+Priority: extra
+Build-Depends: debhelper (>= 5), python-all-dev (>= 2.6.6-3~)
+Maintainer: Aurelien Jarno <aurel32@debian.org>
+Standards-Version: 3.9.5
+Homepage: http://www.lm-sensors.org
+X-Python-Version: >= 2.2
+
+Package: i2c-tools
+Architecture: any
+Section: utils
+Conflicts: lm-sensors (<< 1:3.0.0-1)
+Depends: ${shlibs:Depends}, ${perl:Depends}, ${misc:Depends}, adduser, udev | makedev
+Recommends: read-edid
+Suggests: libi2c-dev, python-smbus
+Description: heterogeneous set of I2C tools for Linux
+ This package contains a heterogeneous set of I2C tools for Linux: a bus
+ probing tool, a chip dumper, register-level access helpers, EEPROM
+ decoding scripts, and more.
+
+Package: libi2c-dev
+Architecture: all
+Depends: ${misc:Depends}
+Section: libdevel
+Recommends: i2c-tools
+Description: userspace I2C programming library development files
+ I2C devices are usually controlled by a kernel driver. Using this
+ library it is also possible to access all devices on an adapter
+ from userspace and without the knowledge of Linux kernel internals.
+
+Package: python-smbus
+Architecture: any
+Section: python
+Depends: ${shlibs:Depends}, ${python:Depends}, ${misc:Depends}
+Provides: ${python:Provides}
+Recommends: i2c-tools
+Description: Python bindings for Linux SMBus access through i2c-dev
+ This Python module allows SMBus access through the I2C /dev interface on
+ Linux hosts. The host kernel must have I2C support, I2C device interface
+ support, and a bus adapter driver.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..080ea16
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,35 @@
+This package was debianized by Aurelien Jarno <aurel32@debian.org>.
+
+It was downloaded from http://www.lm-sensors.org/
+
+Copyright
+
+ Copyright (C) 1995-1997 Simon G. Vogl
+ Copyright (C) 1998-2004 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 1998, 1999 Philip Edelbrock <phil@netroedge.com>
+ Copyright (C) 1998-2004 Mark D. Studebaker <mdsxyz123@yahoo.com>
+ Copyright (C) 2002 James Simmons <jsimmons@users.sf.net>
+ Copyright (C) 2002-2007 Jean Delvare <khali@linux-fr.org>
+ Copyright (c) 2003 Stefano Barbato
+ Copyright (C) 2005-2007 Mark M. Hoffman <mhoffman@lightlink.com>
+
+License:
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 51 Franklin St,
+ Fifth Floor, Boston, MA 02110-1301 USA
+
+On Debian systems, the complete text of the GNU General Public
+License, version 2, can be found in /usr/share/common-licenses/GPL-2.
+
diff --git a/debian/dev-interface b/debian/dev-interface
new file mode 100644
index 0000000..9dd7912
--- /dev/null
+++ b/debian/dev-interface
@@ -0,0 +1,154 @@
+Usually, i2c devices are controlled by a kernel driver. But it is also
+possible to access all devices on an adapter from userspace, through
+the /dev interface. You need to load module i2c-dev for this.
+
+Each registered i2c adapter gets a number, counting from 0. You can
+examine /sys/class/i2c-dev/ to see what number corresponds to which adapter.
+I2C device files are character device files with major device number 89
+and a minor device number corresponding to the number assigned as
+explained above. They should be called "i2c-%d" (i2c-0, i2c-1, ...,
+i2c-10, ...). All 256 minor device numbers are reserved for i2c.
+
+
+C example
+=========
+
+So let's say you want to access an i2c adapter from a C program. The
+first thing to do is "#include <linux/i2c-dev.h>". Please note that
+there are two files named "i2c-dev.h" out there, one is distributed
+with the Linux kernel and is meant to be included from kernel
+driver code, the other one is distributed with lm_sensors and is
+meant to be included from user-space programs. You obviously want
+the second one here.
+
+Now, you have to decide which adapter you want to access. You should
+inspect /sys/class/i2c-dev/ to decide this. Adapter numbers are assigned
+somewhat dynamically, so you can not even assume /dev/i2c-0 is the
+first adapter.
+
+Next thing, open the device file, as follows:
+ int file;
+ int adapter_nr = 2; /* probably dynamically determined */
+ char filename[20];
+
+ sprintf(filename,"/dev/i2c-%d",adapter_nr);
+ if ((file = open(filename,O_RDWR)) < 0) {
+ /* ERROR HANDLING; you can check errno to see what went wrong */
+ exit(1);
+ }
+
+When you have opened the device, you must specify with what device
+address you want to communicate:
+ int addr = 0x40; /* The I2C address */
+ if (ioctl(file,I2C_SLAVE,addr) < 0) {
+ /* ERROR HANDLING; you can check errno to see what went wrong */
+ exit(1);
+ }
+
+Well, you are all set up now. You can now use SMBus commands or plain
+I2C to communicate with your device. SMBus commands are preferred if
+the device supports them. Both are illustrated below.
+ __u8 register = 0x10; /* Device register to access */
+ __s32 res;
+ char buf[10];
+ /* Using SMBus commands */
+ res = i2c_smbus_read_word_data(file,register);
+ if (res < 0) {
+ /* ERROR HANDLING: i2c transaction failed */
+ } else {
+ /* res contains the read word */
+ }
+ /* Using I2C Write, equivalent of
+ i2c_smbus_write_word_data(file,register,0x6543) */
+ buf[0] = register;
+ buf[1] = 0x43;
+ buf[2] = 0x65;
+ if ( write(file,buf,3) != 3) {
+ /* ERROR HANDLING: i2c transaction failed */
+ }
+ /* Using I2C Read, equivalent of i2c_smbus_read_byte(file) */
+ if (read(file,buf,1) != 1) {
+ /* ERROR HANDLING: i2c transaction failed */
+ } else {
+ /* buf[0] contains the read byte */
+ }
+
+IMPORTANT: because of the use of inline functions, you *have* to use
+'-O' or some variation when you compile your program!
+
+
+Full interface description
+==========================
+
+The following IOCTLs are defined and fully supported
+(see also i2c-dev.h):
+
+ioctl(file,I2C_SLAVE,long addr)
+ Change slave address. The address is passed in the 7 lower bits of the
+ argument (except for 10 bit addresses, passed in the 10 lower bits in this
+ case).
+
+ioctl(file,I2C_TENBIT,long select)
+ Selects ten bit addresses if select not equals 0, selects normal 7 bit
+ addresses if select equals 0. Default 0. This request is only valid
+ if the adapter has I2C_FUNC_10BIT_ADDR.
+
+ioctl(file,I2C_PEC,long select)
+ Selects SMBus PEC (packet error checking) generation and verification
+ if select not equals 0, disables if select equals 0. Default 0.
+ Used only for SMBus transactions. This request only has an effect if the
+ the adapter has I2C_FUNC_SMBUS_PEC; it is still safe if not, it just
+ doesn't have any effect.
+
+ioctl(file,I2C_FUNCS,unsigned long *funcs)
+ Gets the adapter functionality and puts it in *funcs.
+
+ioctl(file,I2C_RDWR,struct i2c_rdwr_ioctl_data *msgset)
+
+ Do combined read/write transaction without stop in between.
+ Only valid if the adapter has I2C_FUNC_I2C. The argument is
+ a pointer to a
+
+ struct i2c_rdwr_ioctl_data {
+ struct i2c_msg *msgs; /* ptr to array of simple messages */
+ int nmsgs; /* number of messages to exchange */
+ }
+
+ The msgs[] themselves contain further pointers into data buffers.
+ The function will write or read data to or from that buffers depending
+ on whether the I2C_M_RD flag is set in a particular message or not.
+ The slave address and whether to use ten bit address mode has to be
+ set in each message, overriding the values set with the above ioctl's.
+
+
+Other values are NOT supported at this moment, except for I2C_SMBUS,
+which you should never directly call; instead, use the access functions
+below.
+
+You can do plain i2c transactions by using read(2) and write(2) calls.
+You do not need to pass the address byte; instead, set it through
+ioctl I2C_SLAVE before you try to access the device.
+
+You can do SMBus level transactions (see documentation file smbus-protocol
+for details) through the following functions:
+ __s32 i2c_smbus_write_quick(int file, __u8 value);
+ __s32 i2c_smbus_read_byte(int file);
+ __s32 i2c_smbus_write_byte(int file, __u8 value);
+ __s32 i2c_smbus_read_byte_data(int file, __u8 command);
+ __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value);
+ __s32 i2c_smbus_read_word_data(int file, __u8 command);
+ __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);
+ __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value);
+ __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values);
+ __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
+ __u8 *values);
+All these transactions return -1 on failure; you can read errno to see
+what happened. The 'write' transactions return 0 on success; the
+'read' transactions return the read value, except for read_block, which
+returns the number of values read. The block buffers need not be longer
+than 32 bytes.
+
+The above functions are all macros, that resolve to calls to the
+i2c_smbus_access function, that on its turn calls a specific ioctl
+with the data in a specific format. Read the source code if you
+want to know what happens behind the screens.
diff --git a/debian/i2c-tools.doc b/debian/i2c-tools.doc
new file mode 100644
index 0000000..55bfb34
--- /dev/null
+++ b/debian/i2c-tools.doc
@@ -0,0 +1 @@
+eeprom/README
diff --git a/debian/i2c-tools.install b/debian/i2c-tools.install
new file mode 100644
index 0000000..f7e549b
--- /dev/null
+++ b/debian/i2c-tools.install
@@ -0,0 +1,3 @@
+usr/bin
+usr/share/man
+usr/sbin
diff --git a/debian/i2c-tools.postinst b/debian/i2c-tools.postinst
new file mode 100644
index 0000000..365b0f6
--- /dev/null
+++ b/debian/i2c-tools.postinst
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+set -e
+
+case "$1" in
+ configure)
+ # Add the i2c group unless it's already there
+ if ! getent group i2c >/dev/null; then
+ addgroup --quiet --system i2c || true
+ fi
+
+ # Create the device node if MAKEDEV exists
+ if [ -x /sbin/MAKEDEV ] && [ ! -c /dev/i2c-0 ]; then
+ (cd /dev && MAKEDEV i2c)
+ for i in `seq 1 16` ; do
+ dev=/dev/i2c-$i
+ if [ -c $dev ]; then
+ chmod 0660 $dev
+ chown root:i2c $dev
+ fi
+ done
+ fi
+ ;;
+ abort-upgrade|abort-remove|abort-deconfigure)
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/debian/i2c-tools.udev b/debian/i2c-tools.udev
new file mode 100644
index 0000000..236cb59
--- /dev/null
+++ b/debian/i2c-tools.udev
@@ -0,0 +1 @@
+KERNEL=="i2c-[0-9]*", GROUP="i2c", MODE="0660"
diff --git a/debian/libi2c-dev.docs b/debian/libi2c-dev.docs
new file mode 100644
index 0000000..8270c5d
--- /dev/null
+++ b/debian/libi2c-dev.docs
@@ -0,0 +1 @@
+debian/dev-interface
diff --git a/debian/libi2c-dev.install b/debian/libi2c-dev.install
new file mode 100644
index 0000000..95447ce
--- /dev/null
+++ b/debian/libi2c-dev.install
@@ -0,0 +1 @@
+usr/include/linux/i2c-dev.h
diff --git a/debian/libi2c-dev.postrm b/debian/libi2c-dev.postrm
new file mode 100644
index 0000000..01be43a
--- /dev/null
+++ b/debian/libi2c-dev.postrm
@@ -0,0 +1,20 @@
+#! /bin/sh
+# postrm script for lib-i2c-dev
+
+set -e
+
+case "$1" in
+ remove|purge)
+ dpkg-divert --package libi2c-dev --rename --remove /usr/include/linux/i2c-dev.h
+ ;;
+ upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ ;;
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 0
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
diff --git a/debian/libi2c-dev.preinst b/debian/libi2c-dev.preinst
new file mode 100644
index 0000000..9a275d5
--- /dev/null
+++ b/debian/libi2c-dev.preinst
@@ -0,0 +1,39 @@
+#! /bin/sh
+# preinst script for libi2c-dev
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <new-preinst> `install'
+# * <new-preinst> `install' <old-version>
+# * <new-preinst> `upgrade' <old-version>
+# * <old-preinst> `abort-upgrade' <new-version>
+#
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ install|upgrade)
+ dpkg-divert --package libi2c-dev --divert /usr/include/linux/i2c-dev.h.kernel --rename /usr/include/linux/i2c-dev.h
+ ;;
+
+ abort-upgrade)
+ ;;
+
+ *)
+ echo "preinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/patches/01-decode-dimms-no-eeprom.diff b/debian/patches/01-decode-dimms-no-eeprom.diff
new file mode 100644
index 0000000..0cf62a7
--- /dev/null
+++ b/debian/patches/01-decode-dimms-no-eeprom.diff
@@ -0,0 +1,20 @@
+--- i2c-tools-3.1.1.orig/eeprom/decode-dimms
++++ i2c-tools-3.1.1/eeprom/decode-dimms
+@@ -1826,9 +1826,14 @@ sub get_dimm_list
+ close(DIR);
+ }
+
+- if (!$opened) {
+- print STDERR "No EEPROM found, try loading the eeprom or at24 module\n";
+- exit;
++ if (!@files) {
++ if (!$opened) {
++ print STDERR "No EEPROM found, try loading the eeprom or at24 module\n";
++ exit;
++ } else {
++ print STDERR "No EEPROM found, the kernel probably does not support your hardware.\n";
++ exit;
++ }
+ }
+
+ return sort { $a->{file} cmp $b->{file} } @files;
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..261a8aa
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1 @@
+01-decode-dimms-no-eeprom.diff
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..a10045f
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,118 @@
+#!/usr/bin/make -f
+
+# Sample debian/rules that uses debhelper.
+# GNU copyright 1997 to 1999 by Joey Hess.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# These are used for cross-compiling and for saving the configure script
+# from having to guess our platform (since we know it already)
+DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+DEB_HOST_GNU_SYSTEM ?= $(shell dpkg-architecture -qDEB_HOST_GNU_SYSTEM)
+
+ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
+ INSTALL_PROGRAM += -s
+endif
+
+CFLAGS += $(shell dpkg-buildflags --get CFLAGS)
+CPPFLAGS += $(shell dpkg-buildflags --get CPPFLAGS)
+LDFLAGS += $(shell dpkg-buildflags --get LDFLAGS)
+export CFLAGS CPPFLAGS LDFLAGS
+
+PYVERS = $(shell pyversions -v -r debian/control)
+
+clean:
+ dh_testdir
+ dh_testroot
+
+ $(MAKE) clean
+
+ rm -rf py-smbus/build
+ rm -f *-stamp*
+ dh_clean
+
+
+# Build everything that goes into the Debian package. Use recursive make
+# invocations to build all of the interesting components.
+build: build-arch build-indep
+build-arch: build-stamp-binaries $(PYVERS:%=build-stamp-python-%)
+build-indep: build-stamp-binaries
+
+build-stamp-binaries:
+ dh_testdir
+ $(MAKE) CFLAGS="$(CFLAGS)"
+ touch $@
+
+build-stamp-python-%:
+ dh_testdir
+ cd py-smbus && \
+ CFLAGS="$(CFLAGS) -I../include" python$* setup.py build
+ touch $@
+
+install: install-stamp-binaries $(PYVERS:%=install-stamp-python-%)
+
+install-stamp-binaries: build-stamp-binaries
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+
+ $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp prefix=/usr
+
+ dh_install --sourcedir=debian/tmp
+
+ touch $@
+
+install-stamp-python-%: build-stamp-python-%
+ dh_testdir
+ dh_testroot
+ dh_installdirs
+
+ $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp prefix=/usr
+ cd py-smbus && \
+ CFLAGS="$(CFLAGS) -I../include" python$* setup.py install --install-layout=deb --root=$(CURDIR)/debian/python-smbus
+
+ touch $@
+
+
+# Build architecture-independent files here.
+binary-indep: build install
+ dh_testdir
+ dh_testroot
+ dh_installchangelogs -i CHANGES
+ dh_installdocs -i
+ dh_installman -i
+ dh_link -i
+ dh_strip -i
+ dh_compress -i
+ dh_fixperms -i
+ dh_installdeb -i
+ dh_gencontrol -i
+ dh_md5sums -i
+ dh_builddeb -i
+
+# Build architecture-dependant files that arn't kernel modules here.
+binary-arch: build install
+ dh_testdir
+ dh_testroot
+ dh_installchangelogs -a CHANGES
+ dh_installdocs -a
+ dh_installman -a
+ dh_installudev -a
+ dh_link -a
+ dh_strip -a
+ dh_compress -a
+ dh_fixperms -a
+ dh_perl -a
+ dh_makeshlibs -a
+ dh_python2 -a
+ dh_installdeb -a
+ dh_shlibdeps -a
+ dh_gencontrol -a
+ dh_md5sums -a
+ dh_builddeb -a
+
+binary: binary-arch binary-indep
+.PHONY: clean build build-arch build-indep install binary-indep binary-arch binary
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/debian/source/options b/debian/source/options
new file mode 100644
index 0000000..cce1d53
--- /dev/null
+++ b/debian/source/options
@@ -0,0 +1 @@
+compression="xz"
diff --git a/eeprog/.gitignore b/eeprog/.gitignore
new file mode 100644
index 0000000..916cb72
--- /dev/null
+++ b/eeprog/.gitignore
@@ -0,0 +1 @@
+/eeprog
diff --git a/eeprog/24cXX.c b/eeprog/24cXX.c
new file mode 100644
index 0000000..b21f6b8
--- /dev/null
+++ b/eeprog/24cXX.c
@@ -0,0 +1,190 @@
+/***************************************************************************
+ copyright : (C) by 2002-2003 Stefano Barbato
+ email : stefano@codesink.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <i2c/smbus.h>
+#include "24cXX.h"
+
+/*
+ * EEPROMs need some time to process writes
+ * Ideally this value should either be settable with a command line parameter,
+ * or "guessed" at runtime using a retry loop.
+ */
+#define WRITE_DELAY_US 5000
+
+static int i2c_write_1b(struct eeprom *e, __u8 buf)
+{
+ int r;
+ // we must simulate a plain I2C byte write with SMBus functions
+ r = i2c_smbus_write_byte(e->fd, buf);
+ if(r < 0)
+ fprintf(stderr, "Error i2c_write_1b: %s\n", strerror(errno));
+ usleep(WRITE_DELAY_US);
+ return r;
+}
+
+static int i2c_write_2b(struct eeprom *e, __u8 buf[2])
+{
+ int r;
+ // we must simulate a plain I2C byte write with SMBus functions
+ r = i2c_smbus_write_byte_data(e->fd, buf[0], buf[1]);
+ if(r < 0)
+ fprintf(stderr, "Error i2c_write_2b: %s\n", strerror(errno));
+ usleep(WRITE_DELAY_US);
+ return r;
+}
+
+static int i2c_write_3b(struct eeprom *e, __u8 buf[3])
+{
+ int r;
+ // we must simulate a plain I2C byte write with SMBus functions
+ // the __u16 data field will be byte swapped by the SMBus protocol
+ r = i2c_smbus_write_word_data(e->fd, buf[0], buf[2] << 8 | buf[1]);
+ if(r < 0)
+ fprintf(stderr, "Error i2c_write_3b: %s\n", strerror(errno));
+ usleep(WRITE_DELAY_US);
+ return r;
+}
+
+
+#define CHECK_I2C_FUNC( var, label ) \
+ do { if(0 == (var & label)) { \
+ fprintf(stderr, "\nError: " \
+ #label " function is required. Program halted.\n\n"); \
+ exit(1); } \
+ } while(0);
+
+int eeprom_open(char *dev_fqn, int addr, int type, struct eeprom* e)
+{
+ int fd, r;
+ unsigned long funcs;
+ e->fd = e->addr = 0;
+ e->dev = 0;
+
+ fd = open(dev_fqn, O_RDWR);
+ if(fd <= 0)
+ return -1;
+
+ // get funcs list
+ if((r = ioctl(fd, I2C_FUNCS, &funcs) < 0))
+ return r;
+
+
+ // check for req funcs
+ CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_READ_BYTE );
+ CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_WRITE_BYTE );
+ CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_READ_BYTE_DATA );
+ CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_WRITE_BYTE_DATA );
+ CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_READ_WORD_DATA );
+ CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_WRITE_WORD_DATA );
+
+ // set working device
+ if( ( r = ioctl(fd, I2C_SLAVE, addr)) < 0)
+ return r;
+ e->fd = fd;
+ e->addr = addr;
+ e->dev = dev_fqn;
+ e->type = type;
+ return 0;
+}
+
+int eeprom_close(struct eeprom *e)
+{
+ close(e->fd);
+ e->fd = -1;
+ e->dev = 0;
+ e->type = EEPROM_TYPE_UNKNOWN;
+ return 0;
+}
+
+#if 0
+int eeprom_24c32_write_byte(struct eeprom *e, __u16 mem_addr, __u8 data)
+{
+ __u8 buf[3] = { (mem_addr >> 8) & 0x00ff, mem_addr & 0x00ff, data };
+ return i2c_write_3b(e, buf);
+}
+
+
+int eeprom_24c32_read_current_byte(struct eeprom* e)
+{
+ ioctl(e->fd, BLKFLSBUF); // clear kernel read buffer
+ return i2c_smbus_read_byte(e->fd);
+}
+
+int eeprom_24c32_read_byte(struct eeprom* e, __u16 mem_addr)
+{
+ int r;
+ ioctl(e->fd, BLKFLSBUF); // clear kernel read buffer
+ __u8 buf[2] = { (mem_addr >> 8) & 0x0ff, mem_addr & 0x0ff };
+ r = i2c_write_2b(e, buf);
+ if (r < 0)
+ return r;
+ r = i2c_smbus_read_byte(e->fd);
+ return r;
+}
+#endif
+
+
+int eeprom_read_current_byte(struct eeprom* e)
+{
+ ioctl(e->fd, BLKFLSBUF); // clear kernel read buffer
+ return i2c_smbus_read_byte(e->fd);
+}
+
+int eeprom_read_byte(struct eeprom* e, __u16 mem_addr)
+{
+ int r;
+ ioctl(e->fd, BLKFLSBUF); // clear kernel read buffer
+ if(e->type == EEPROM_TYPE_8BIT_ADDR)
+ {
+ __u8 buf = mem_addr & 0x0ff;
+ r = i2c_write_1b(e, buf);
+ } else if(e->type == EEPROM_TYPE_16BIT_ADDR) {
+ __u8 buf[2] = { (mem_addr >> 8) & 0x0ff, mem_addr & 0x0ff };
+ r = i2c_write_2b(e, buf);
+ } else {
+ fprintf(stderr, "ERR: unknown eeprom type\n");
+ return -1;
+ }
+ if (r < 0)
+ return r;
+ r = i2c_smbus_read_byte(e->fd);
+ return r;
+}
+
+int eeprom_write_byte(struct eeprom *e, __u16 mem_addr, __u8 data)
+{
+ if(e->type == EEPROM_TYPE_8BIT_ADDR) {
+ __u8 buf[2] = { mem_addr & 0x00ff, data };
+ return i2c_write_2b(e, buf);
+ } else if(e->type == EEPROM_TYPE_16BIT_ADDR) {
+ __u8 buf[3] =
+ { (mem_addr >> 8) & 0x00ff, mem_addr & 0x00ff, data };
+ return i2c_write_3b(e, buf);
+ } else {
+ fprintf(stderr, "ERR: unknown eeprom type\n");
+ return -1;
+ }
+}
+
diff --git a/eeprog/24cXX.h b/eeprog/24cXX.h
new file mode 100644
index 0000000..3cc6593
--- /dev/null
+++ b/eeprog/24cXX.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ copyright : (C) by 2002-2003 Stefano Barbato
+ email : stefano@codesink.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+#ifndef _24CXX_H_
+#define _24CXX_H_
+#include <linux/types.h>
+
+#define EEPROM_TYPE_UNKNOWN 0
+#define EEPROM_TYPE_8BIT_ADDR 1
+#define EEPROM_TYPE_16BIT_ADDR 2
+
+struct eeprom
+{
+ char *dev; // device file i.e. /dev/i2c-N
+ int addr; // i2c address
+ int fd; // file descriptor
+ int type; // eeprom type
+};
+
+/*
+ * opens the eeprom device at [dev_fqn] (i.e. /dev/i2c-N) whose address is
+ * [addr] and set the eeprom_24c32 [e]
+ */
+int eeprom_open(char *dev_fqn, int addr, int type, struct eeprom*);
+/*
+ * closees the eeprom device [e]
+ */
+int eeprom_close(struct eeprom *e);
+/*
+ * read and returns the eeprom byte at memory address [mem_addr]
+ * Note: eeprom must have been selected by ioctl(fd,I2C_SLAVE,address)
+ */
+int eeprom_read_byte(struct eeprom* e, __u16 mem_addr);
+/*
+ * read the current byte
+ * Note: eeprom must have been selected by ioctl(fd,I2C_SLAVE,address)
+ */
+int eeprom_read_current_byte(struct eeprom *e);
+/*
+ * writes [data] at memory address [mem_addr]
+ * Note: eeprom must have been selected by ioctl(fd,I2C_SLAVE,address)
+ */
+int eeprom_write_byte(struct eeprom *e, __u16 mem_addr, __u8 data);
+
+#endif
+
diff --git a/eeprog/Module.mk b/eeprog/Module.mk
new file mode 100644
index 0000000..9d36869
--- /dev/null
+++ b/eeprog/Module.mk
@@ -0,0 +1,67 @@
+# eeprog
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+EEPROG_DIR := eeprog
+
+EEPROG_CFLAGS := -Iinclude
+ifeq ($(USE_STATIC_LIB),1)
+EEPROG_LDFLAGS := $(LIB_DIR)/$(LIB_STLIBNAME)
+else
+EEPROG_LDFLAGS := -L$(LIB_DIR) -li2c
+endif
+
+EEPROG_TARGETS := eeprog
+
+#
+# Programs
+#
+
+$(EEPROG_DIR)/eeprog: $(EEPROG_DIR)/eeprog.o $(EEPROG_DIR)/24cXX.o
+ $(CC) $(LDFLAGS) -o $@ $^ $(EEPROG_LDFLAGS)
+
+#
+# Objects
+#
+
+$(EEPROG_DIR)/eeprog.o: $(EEPROG_DIR)/eeprog.c $(EEPROG_DIR)/24cXX.h
+ $(CC) $(CFLAGS) $(EEPROG_CFLAGS) -c $< -o $@
+
+$(EEPROG_DIR)/24cXX.o: $(EEPROG_DIR)/24cXX.c $(EEPROG_DIR)/24cXX.h $(INCLUDE_DIR)/i2c/smbus.h
+ $(CC) $(CFLAGS) $(EEPROG_CFLAGS) -c $< -o $@
+
+#
+# Commands
+#
+
+all-eeprog: $(addprefix $(EEPROG_DIR)/,$(EEPROG_TARGETS))
+
+strip-eeprog: $(addprefix $(EEPROG_DIR)/,$(EEPROG_TARGETS))
+ strip $(addprefix $(EEPROG_DIR)/,$(EEPROG_TARGETS))
+
+clean-eeprog:
+ $(RM) $(addprefix $(EEPROG_DIR)/,*.o $(EEPROG_TARGETS))
+
+install-eeprog: $(addprefix $(EEPROG_DIR)/,$(EEPROG_TARGETS))
+ $(INSTALL_DIR) $(DESTDIR)$(sbindir) $(DESTDIR)$(man8dir)
+ for program in $(EEPROG_TARGETS) ; do \
+ $(INSTALL_PROGRAM) $(EEPROG_DIR)/$$program $(DESTDIR)$(sbindir) ; \
+ $(INSTALL_DATA) $(EEPROG_DIR)/$$program.8 $(DESTDIR)$(man8dir) ; done
+
+uninstall-eeprog:
+ for program in $(EEPROG_TARGETS) ; do \
+ $(RM) $(DESTDIR)$(sbindir)/$$program ; \
+ $(RM) $(DESTDIR)$(man8dir)/$$program.8 ; done
+
+all: all-eeprog
+
+strip: strip-eeprog
+
+clean: clean-eeprog
+
+install: install-eeprog
+
+uninstall: uninstall-eeprog
diff --git a/eeprog/README.eeprog b/eeprog/README.eeprog
new file mode 100644
index 0000000..7514c73
--- /dev/null
+++ b/eeprog/README.eeprog
@@ -0,0 +1,22 @@
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!!! !
+!!! This program should only be used on external busses unless you REALLY !
+!!! know what you are doing. !
+!!! !
+!!! Your computer probably contains eeproms for saving data vital to its !
+!!! operation. If you are not careful you might overwrite this data with !
+!!! this program and your computer may no longer boot! !
+!!! !
+!!! An example are the EEPROMS on your SDRAM DIMMs, your computer may no !
+!!! longer detect the RAM module rendering it essentially USELESS! !
+!!! !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+eeprog reads and writes 24Cxx EEPROMs connected to I2C serial bus.
+
+It uses the SMBus protocol used by most of the recent chipsets. Don't forget to load
+your i2c chipset and the i2c-dev drivers.
+
+Use -16 switch for EEPROM larger then 24C16 (16 bit addressing mode).
+
+Use "make EXTRA=eeprog" to build this program.
diff --git a/eeprog/eeprog.8 b/eeprog/eeprog.8
new file mode 100644
index 0000000..6a61102
--- /dev/null
+++ b/eeprog/eeprog.8
@@ -0,0 +1,100 @@
+.\"
+.\" eeprog.8 - manpage for the i2c-tools/eeprog utility
+.\" Copyright (C) 2013 Jaromir Capik
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+.\"
+.TH eeprog "8" "Jul 2013" "i2c-tools" "System Administration"
+.SH NAME
+eeprog \- reads and writes 24Cxx EEPROMs connected to I2C serial bus
+.SH SYNOPSIS
+.B eeprog
+[-fqxdh] [-16|-8] [-r addr[:count]|-w addr] <device> <i2c-addr>
+.SH DESCRIPTION
+.B eeprog
+uses the SMBus protocol used by most of the recent chipsets.
+.SH NOTE
+Don't forget to load your i2c chipset and the i2c-dev drivers.
+.P
+The following environment variables could be set instead of the command line arguments:
+.P
+ EEPROG_DEV device
+.br
+ EEPROG_I2C_ADDR i2c-addr
+.SH PARAMETERS
+.I Address modes
+.TP
+.B \-8
+Use 8bit address mode for 24c0x...24C16 [default]
+.TP
+.B \-16
+Use 16bit address mode for 24c32...24C256
+.TP
+.I Actions
+.TP
+.B \-r addr[:count]
+Read
+.B count
+(1 if omitted) bytes from
+.B addr
+and print them to the standard output
+.TP
+.B \-w addr
+Write input (stdin) at address
+.B addr
+of the EEPROM
+.TP
+.B \-h
+Print this help
+.TP
+.I Options
+.TP
+.B \-x
+Set hex output mode
+.TP
+.B \-d
+Dummy mode, display what *would* have been done
+.TP
+.B \-f
+Disable warnings and don't ask confirmation
+.TP
+.B \-q
+Quiet mode
+.TP
+.I Bus
+.TP
+.B device
+Device file representing the I2C bus (eg. /dev/i2c-0)
+.TP
+.B i2c-addr
+I2C bus address of the EEPROM (eg. 0x3A)
+.SH EXAMPLES
+Read 64 bytes from the EEPROM at address 0x54 on bus 0 starting at address 123 (decimal)
+.P
+.B eeprog
+/dev/i2c-0 0x54 -r 123:64
+.P
+Print the hex codes of the first 32 bytes read from bus 1 at address 0x22
+.P
+.B eeprog
+/dev/i2c-1 0x51 -x -r 0x22:0x20
+.P
+Write the current timestamp at address 0x200 of the EEPROM on bus 0 at address 0x33
+.P
+ date |
+.B eeprog
+/dev/i2c-0 0x33 -w 0x200
+.SH AUTHOR
+Stefano Barbato
diff --git a/eeprog/eeprog.c b/eeprog/eeprog.c
new file mode 100644
index 0000000..9978241
--- /dev/null
+++ b/eeprog/eeprog.c
@@ -0,0 +1,281 @@
+/***************************************************************************
+ copyright : (C) by 2002-2003 Stefano Barbato
+ email : stefano@codesink.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+#include <stdio.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "24cXX.h"
+
+#define VERSION "0.7.5"
+
+#define ENV_DEV "EEPROG_DEV"
+#define ENV_I2C_ADDR "EEPROG_I2C_ADDR"
+
+int g_quiet;
+
+#define usage_if(a) do { do_usage_if( a , __LINE__); } while(0);
+void do_usage_if(int b, int line)
+{
+ static const char *eeprog_usage =
+"eeprog " VERSION ", a 24Cxx EEPROM reader/writer\n"
+"Copyright (c) 2003 by Stefano Barbato - All rights reserved.\n"
+"Usage: eeprog [-fqxdh] [-16|-8] [ -r addr[:count] | -w addr ] /dev/i2c-N i2c-address\n"
+"\n"
+" Address modes:\n"
+" -8 Use 8bit address mode for 24c0x...24C16 [default]\n"
+" -16 Use 16bit address mode for 24c32...24C256\n"
+" Actions:\n"
+" -r addr[:count] Read [count] (1 if omitted) bytes from [addr]\n"
+" and print them to the standard output\n"
+" -w addr Write input (stdin) at address [addr] of the EEPROM\n"
+" -h Print this help\n"
+" Options:\n"
+" -x Set hex output mode\n"
+" -d Dummy mode, display what *would* have been done\n"
+" -f Disable warnings and don't ask confirmation\n"
+" -q Quiet mode\n"
+"\n"
+"The following environment variables could be set instead of the command\n"
+"line arguments:\n"
+" EEPROG_DEV device name(/dev/i2c-N)\n"
+" EEPROG_I2C_ADDR i2c-address\n"
+"\n"
+" Examples\n"
+" 1- read 64 bytes from the EEPROM at address 0x54 on bus 0 starting\n"
+" at address 123 (decimal)\n"
+" eeprog /dev/i2c-0 0x54 -r 123:64\n"
+" 2- prints the hex codes of the first 32 bytes read from bus 1\n"
+" at address 0x22\n"
+" eeprog /dev/i2c-1 0x51 -x -r 0x22:0x20\n"
+" 3- write the current timestamp at address 0x200 of the EEPROM on\n"
+" bus 0 at address 0x33\n"
+" date | eeprog /dev/i2c-0 0x33 -w 0x200\n";
+
+ if(!b)
+ return;
+ fprintf(stderr, "%s\n[line %d]\n", eeprog_usage, line);
+ exit(1);
+}
+
+
+#define die_if(a, msg) do { do_die_if( a , msg, __LINE__); } while(0);
+void do_die_if(int b, char* msg, int line)
+{
+ if(!b)
+ return;
+ fprintf(stderr, "Error at line %d: %s\n", line, msg);
+ //fprintf(stderr, " sysmsg: %s\n", strerror(errno));
+ exit(1);
+}
+
+#define print_info(args...) do { if(!g_quiet) fprintf(stderr, args); } while(0);
+
+void parse_arg(char *arg, int* paddr, int *psize)
+{
+ char *end;
+ *paddr = strtoul(arg, &end, 0);
+ if(*end == ':')
+ *psize = strtoul(++end, 0, 0);
+}
+
+int confirm_action()
+{
+ fprintf(stderr,
+ "\n"
+ "____________________________WARNING____________________________\n"
+ "Erroneously writing to a system EEPROM (like DIMM SPD modules)\n"
+ "can break your system. It will NOT boot anymore so you'll not\n"
+ "be able to fix it.\n"
+ "\n"
+ "Reading from 8bit EEPROMs (like that in your DIMM) without using\n"
+ "the -8 switch can also UNEXPECTEDLY write to them, so be sure to\n"
+ "use the -8 command param when required.\n"
+ "\n"
+ "Use -f to disable this warning message\n"
+ "\n"
+ "Press ENTER to continue or hit CTRL-C to exit\n"
+ "\n"
+ );
+ getchar();
+ return 1;
+}
+
+
+int read_from_eeprom(struct eeprom *e, int addr, int size, int hex)
+{
+
+ int ch, i;
+ // hex print out
+ die_if((ch = eeprom_read_byte(e, addr)) < 0, "read error");
+ i = 1;
+ if(hex)
+ printf("\n %.4x| %.2x ", addr, ch);
+ else
+ putchar(ch);
+ while(--size)
+ {
+ die_if((ch = eeprom_read_current_byte(e)) < 0, "read error");
+ if(hex)
+ {
+ addr++;
+ if( (i % 16) == 0 )
+ printf("\n %.4x| ", addr);
+ else if( (i % 8) == 0 )
+ printf(" ");
+ i++;
+ printf("%.2x ", ch);
+ } else {
+ putchar(ch);
+ }
+ }
+ if(hex)
+ printf("\n\n");
+ fflush(stdout);
+ return 0;
+}
+
+int write_to_eeprom(struct eeprom *e, int addr)
+{
+ int c;
+ while((c = getchar()) != EOF)
+ {
+ print_info(".");
+ fflush(stdout);
+ die_if(eeprom_write_byte(e, addr++, c), "write error");
+ }
+ print_info("\n\n");
+ return 0;
+}
+
+int main(int argc, char** argv)
+{
+ struct eeprom e;
+ int ret, op, i2c_addr, memaddr, size, want_hex, dummy, force, sixteen;
+ char *device, *arg = 0, *i2c_addr_s;
+ struct stat st;
+ int eeprom_type = 0;
+
+ op = want_hex = dummy = force = sixteen = 0;
+ g_quiet = 0;
+
+ while((ret = getopt(argc, argv, "1:8fr:qhw:xd")) != -1)
+ {
+ switch(ret)
+ {
+ case '1':
+ usage_if(*optarg != '6' || strlen(optarg) != 1);
+ die_if(eeprom_type, "EEPROM type switch (-8 or -16) used twice");
+ eeprom_type = EEPROM_TYPE_16BIT_ADDR;
+ break;
+ case 'x':
+ want_hex++;
+ break;
+ case 'd':
+ dummy++;
+ break;
+ case '8':
+ die_if(eeprom_type, "EEPROM type switch (-8 or -16) used twice");
+ eeprom_type = EEPROM_TYPE_8BIT_ADDR;
+ break;
+ case 'f':
+ force++;
+ break;
+ case 'q':
+ g_quiet++;
+ break;
+ case 'h':
+ usage_if(1);
+ break;
+ default:
+ die_if(op != 0, "Both read and write requested");
+ arg = optarg;
+ op = ret;
+ }
+ }
+ if(!eeprom_type)
+ eeprom_type = EEPROM_TYPE_8BIT_ADDR; // default
+
+ usage_if(op == 0); // no switches
+ // set device and i2c_addr reading from cmdline or env
+ device = i2c_addr_s = 0;
+ switch(argc - optind)
+ {
+ case 0:
+ device = getenv(ENV_DEV);
+ i2c_addr_s = getenv(ENV_I2C_ADDR);
+ break;
+ case 1:
+ if(stat(argv[optind], &st) != -1)
+ {
+ device = argv[optind];
+ i2c_addr_s = getenv(ENV_I2C_ADDR);
+ } else {
+ device = getenv(ENV_DEV);
+ i2c_addr_s = argv[optind];
+ }
+ break;
+ case 2:
+ device = argv[optind++];
+ i2c_addr_s = argv[optind];
+ break;
+ default:
+ usage_if(1);
+ }
+ usage_if(!device || !i2c_addr_s);
+ i2c_addr = strtoul(i2c_addr_s, 0, 0);
+
+ print_info("eeprog %s, a 24Cxx EEPROM reader/writer\n", VERSION);
+ print_info("Copyright (c) 2003 by Stefano Barbato - All rights reserved.\n");
+ print_info(" Bus: %s, Address: 0x%x, Mode: %dbit\n",
+ device, i2c_addr,
+ (eeprom_type == EEPROM_TYPE_8BIT_ADDR ? 8 : 16) );
+ if(dummy)
+ {
+ fprintf(stderr, "Dummy mode selected, nothing done.\n");
+ return 0;
+ }
+ die_if(eeprom_open(device, i2c_addr, eeprom_type, &e) < 0,
+ "unable to open eeprom device file (check that the file exists and that it's readable)");
+ switch(op)
+ {
+ case 'r':
+ if(force == 0)
+ confirm_action();
+ size = 1; // default
+ parse_arg(arg, &memaddr, &size);
+ print_info(" Reading %d bytes from 0x%x\n", size, memaddr);
+ read_from_eeprom(&e, memaddr, size, want_hex);
+ break;
+ case 'w':
+ if(force == 0)
+ confirm_action();
+ parse_arg(arg, &memaddr, &size);
+ print_info(" Writing stdin starting at address 0x%x\n",
+ memaddr);
+ write_to_eeprom(&e, memaddr);
+ break;
+ default:
+ usage_if(1);
+ exit(1);
+ }
+ eeprom_close(&e);
+
+ return 0;
+}
+
diff --git a/eeprom/Module.mk b/eeprom/Module.mk
new file mode 100644
index 0000000..402f2f5
--- /dev/null
+++ b/eeprom/Module.mk
@@ -0,0 +1,34 @@
+# EEPROM decoding scripts for the Linux eeprom driver
+#
+# Copyright (C) 2007-2013 Jean Delvare <jdelvare@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+EEPROM_DIR := eeprom
+
+EEPROM_TARGETS := decode-dimms decode-vaio ddcmon decode-edid
+EEPROM_MANPAGES := decode-dimms.1 decode-vaio.1
+
+#
+# Commands
+#
+
+install-eeprom: $(addprefix $(EEPROM_DIR)/,$(EEPROM_TARGETS))
+ $(INSTALL_DIR) $(DESTDIR)$(bindir) $(DESTDIR)$(mandir)/man1
+ for program in $(EEPROM_TARGETS) ; do \
+ $(INSTALL_PROGRAM) $(EEPROM_DIR)/$$program $(DESTDIR)$(bindir) ; done
+ for manual in $(EEPROM_MANPAGES) ; do \
+ $(INSTALL_DATA) $(EEPROM_DIR)/$$manual $(DESTDIR)$(mandir)/man1 ; done
+
+uninstall-eeprom:
+ for program in $(EEPROM_TARGETS) ; do \
+ $(RM) $(DESTDIR)$(bindir)/$$program ; done
+ for manual in $(EEPROM_MANPAGES) ; do \
+ $(RM) $(DESTDIR)$(mandir)/$$manual ; done
+
+install: install-eeprom
+
+uninstall: uninstall-eeprom
diff --git a/eeprom/README b/eeprom/README
new file mode 100644
index 0000000..325a8dc
--- /dev/null
+++ b/eeprom/README
@@ -0,0 +1,18 @@
+This directory contains scripts to decode the data exposed by the eeprom
+Linux kernel driver.
+
+* decode-dimms (perl script)
+ Decode the information found in memory module SPD EEPROMs. The SPD
+ data is read either from the running system or from dump files.
+
+* decode-vaio (perl script)
+ Decode the information found in Sony Vaio laptop identification EEPROMs.
+
+* ddcmon (perl script)
+ decode-edid (perl script)
+ Decode the information found in monitor EEPROMs. Both scripts require
+ an access to the DDC channel of the monitor. This is typically provided
+ by framebuffer drivers. decode-edid additionally requires parse-edid,
+ which is part of the read-edid package. ddcmon prints general
+ information, while decode-edid prints timing information for
+ inclusion into your X11 configuration file.
diff --git a/eeprom/ddcmon b/eeprom/ddcmon
new file mode 100755
index 0000000..2d31e22
--- /dev/null
+++ b/eeprom/ddcmon
@@ -0,0 +1,565 @@
+#!/usr/bin/perl -w
+#
+# Copyright (C) 2004-2008 Jean Delvare <jdelvare@suse.de>
+#
+# Parts inspired from decode-edid.
+# Copyright (C) 2003-2004 Jean Delvare
+#
+# Parts inspired from the ddcmon driver and sensors' print_ddcmon function.
+# Copyright (C) 1998-2004 Mark D. Studebaker
+#
+# Parts inspired from the fbmon driver (Linux 2.6.10).
+# Copyright (C) 2002 James Simmons <jsimmons@users.sf.net>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA.
+#
+# This script is a replacement for the now deprecated ddcmon kernel driver.
+# Instead of having a dedicated driver, it is better to reuse the standard
+# eeprom driver and implement the EDID-specific code in user-space.
+#
+# EDID (Extended Display Identification Data) is a VESA standard which
+# allows storing (on manufacturer's side) and retrieving (on user's side)
+# of configuration information about displays, such as manufacturer,
+# serial number, physical dimensions and allowed horizontal and vertical
+# refresh rates.
+#
+# Syntax: ddcmon [bus [address]]
+# Address can be given in decimal or hexadecimal (with a 0x prefix).
+# If no address is given, default is 0x50.
+# Bus number must be given in decimal. If no bus number is given,
+# try them all.
+
+use strict;
+use Fcntl qw(:DEFAULT :seek);
+use vars qw(@standard_scales);
+
+@standard_scales = ([1, 1], [3, 4], [4, 5], [16, 9]);
+
+# Make sure the eeprom module is loaded.
+# For non-modular kernels, we can't help.
+if (-r '/proc/modules')
+{
+ my $found = 0;
+ open(MODULES, '/proc/modules');
+ while (!$found && defined ($_ = <MODULES>))
+ {
+ $found++ if m/^eeprom\s+/;
+ }
+ close(MODULES);
+
+ unless ($found)
+ {
+ print STDERR
+ "This script requires the eeprom module to be loaded.\n";
+ exit 1;
+ }
+}
+
+# Only used for sysfs
+sub rawread
+{
+ my $filename = shift;
+ my $length = shift;
+ my $offset = shift || 0;
+ my $bytes = '';
+
+ sysopen(FH, $filename, O_RDONLY)
+ or die "Can't open $filename";
+ if ($offset)
+ {
+ sysseek(FH, $offset, SEEK_SET)
+ or die "Can't seek in $filename";
+ }
+
+ $offset = 0;
+ while ($length)
+ {
+ my $r = sysread(FH, $bytes, $length, $offset);
+ die "Can't read $filename"
+ unless defined($r);
+ die "Unexpected EOF in $filename"
+ unless $r;
+ $offset += $r;
+ $length -= $r;
+ }
+ close(FH);
+
+ return $bytes;
+}
+
+sub get_edid_sysfs
+{
+ my ($bus, $addr) = @_;
+
+ my @bytes = unpack("C*", rawread("/sys/bus/i2c/devices/$bus-00$addr/eeprom", 128));
+
+ return \@bytes;
+}
+
+sub get_edid_procfs
+{
+ my ($bus, $addr) = @_;
+
+ my @bytes;
+
+ for (my $i = 0 ; $i < 0x80; $i += 0x10)
+ {
+ my $filename = sprintf("/proc/sys/dev/sensors/eeprom-i2c-\%s-\%s/\%02x",
+ $bus, $addr, $i);
+ open(EEDATA, $filename)
+ or die "Can't read $filename";
+ push @bytes, split(/\s+/, <EEDATA>);
+ close(EEDATA);
+ }
+
+ return \@bytes;
+}
+
+sub print_line
+{
+ my $label = shift;
+ my $pattern = shift;
+
+ printf("\%-24s$pattern\n", $label.':', @_);
+}
+
+sub extract_byte
+{
+ my ($bytes, $offset) = @_;
+
+ return $bytes->[$offset];
+}
+
+sub extract_word
+{
+ my ($bytes, $offset) = @_;
+
+ return ($bytes->[$offset]
+ | ($bytes->[$offset+1] << 8));
+}
+
+sub extract_manufacturer
+{
+ my ($bytes, $offset) = @_;
+ my $i = ($bytes->[$offset+1] | ($bytes->[$offset] << 8));
+
+ return sprintf('%c%c%c',
+ (($i >> 10) & 0x1f) + ord('A') - 1,
+ (($i >> 5) & 0x1f) + ord('A') - 1,
+ ($i & 0x1f) + ord('A') - 1);
+}
+
+sub extract_sesquiword
+{
+ my ($bytes, $offset) = @_;
+
+ return ($bytes->[$offset]
+ | ($bytes->[$offset+1] << 8)
+ | ($bytes->[$offset+2] << 16));
+}
+
+sub extract_dword
+{
+ my ($bytes, $offset) = @_;
+
+ return ($bytes->[$offset]
+ | ($bytes->[$offset+1] << 8)
+ | ($bytes->[$offset+2] << 16)
+ | ($bytes->[$offset+3] << 24));
+}
+
+sub extract_display_input
+{
+ my ($bytes, $offset) = @_;
+
+ my @voltage = ('0.700V/0.300V', '0.714V/0.286V',
+ '1.000V/0.400V', '0.700V/0.000V');
+
+ return 'Digital'
+ if ($bytes->[$offset] & 0x80);
+
+ return 'Analog ('.$voltage[($bytes->[$offset] & 0x60) >> 5].')';
+}
+
+sub extract_dpms
+{
+ my ($bytes, $offset) = @_;
+
+ my @supported;
+
+ push @supported, 'Active Off' if ($bytes->[$offset] & 0x20);
+ push @supported, 'Suspend' if ($bytes->[$offset] & 0x40);
+ push @supported, 'Standby' if ($bytes->[$offset] & 0x80);
+
+ return join(', ', @supported)
+ if (@supported);
+
+ return 'None supported';
+}
+
+sub extract_color_mode
+{
+ my ($bytes, $offset) = @_;
+
+ my @mode = ('Monochrome', 'RGB Multicolor', 'Non-RGB Multicolor');
+
+ return $mode[($bytes->[$offset] >> 3) & 0x03];
+}
+
+sub good_signature
+{
+ my $bytes = shift;
+
+ return $bytes->[0] == 0x00
+ && $bytes->[1] == 0xff
+ && $bytes->[2] == 0xff
+ && $bytes->[3] == 0xff
+ && $bytes->[4] == 0xff
+ && $bytes->[5] == 0xff
+ && $bytes->[6] == 0xff
+ && $bytes->[7] == 0x00;
+}
+
+sub verify_checksum
+{
+ my $bytes = shift;
+ my $cs;
+
+ for (my $i = 0, $cs = 0; $i < 0x80; $i++)
+ {
+ $cs += $bytes->[$i];
+ }
+
+ return (($cs & 0xff) == 0 ? 'OK' : 'Not OK');
+}
+
+sub add_timing
+{
+ my ($timings, $new) = @_;
+
+ my $mode = sprintf('%ux%u@%u%s', $new->[0], $new->[1],
+ $new->[2], defined ($new->[3]) &&
+ $new->[3] eq 'interlaced' ? 'i' : '');
+
+ $timings->{$mode} = $new;
+}
+
+sub add_standard_timing
+{
+ my ($timings, $byte0, $byte1) = @_;
+
+ # Unused slot
+ return if ($byte0 == $byte1)
+ && ($byte0 == 0x01 || $byte0 == 0x00 || $byte0 == 0x20);
+
+ my $width = ($byte0 + 31) * 8;
+ my $height = $width * $standard_scales[$byte1 >> 6]->[0]
+ / $standard_scales[$byte1 >> 6]->[1];
+ my $refresh = 60 + ($byte1 & 0x3f);
+
+ add_timing($timings, [$width, $height, $refresh]);
+}
+
+sub sort_timings
+{
+ # First order by width
+ return -1 if $a->[0] < $b->[0];
+ return 1 if $a->[0] > $b->[0];
+
+ # Second by height
+ return -1 if $a->[1] < $b->[1];
+ return 1 if $a->[1] > $b->[1];
+
+ # Third by frequency
+ # Interlaced modes count for half their frequency
+ my $freq_a = $a->[2];
+ my $freq_b = $b->[2];
+ $freq_a /= 2 if defined $a->[3] && $a->[3] eq 'interlaced';
+ $freq_b /= 2 if defined $b->[3] && $b->[3] eq 'interlaced';
+ return -1 if $freq_a < $freq_b;
+ return 1 if $freq_a > $freq_b;
+
+ return 0;
+}
+
+sub print_timings
+{
+ my ($bytes, $timings) = @_;
+
+ # Established Timings
+ my @established =
+ (
+ [720, 400, 70],
+ [720, 400, 88],
+ [640, 480, 60],
+ [640, 480, 67],
+ [640, 480, 72],
+ [640, 480, 75],
+ [800, 600, 56],
+ [800, 600, 60],
+ [800, 600, 72],
+ [800, 600, 75],
+ [832, 624, 75],
+ [1024, 768, 87, 'interlaced'],
+ [1024, 768, 60],
+ [1024, 768, 70],
+ [1024, 768, 75],
+ [1280, 1024, 75],
+ undef, undef, undef,
+ [1152, 870, 75],
+ );
+ my $temp = extract_sesquiword($bytes, 0x23);
+ for (my $i = 0; $i < 24; $i++)
+ {
+ next unless defined($established[$i]);
+ add_timing($timings, $established[$i])
+ if ($temp & (1 << $i));
+ }
+
+ # Standard Timings
+ for (my $i = 0x26; $i < 0x36; $i += 2)
+ {
+ add_standard_timing($timings, $bytes->[$i], $bytes->[$i+1]);
+ }
+
+ foreach my $v (sort sort_timings values(%{$timings}))
+ {
+ print_line("Timing", '%ux%u @ %u Hz%s',
+ $v->[0], $v->[1], $v->[2],
+ defined($v->[3]) ? ' ('.$v->[3].')' : '');
+ }
+}
+
+sub extract_string
+{
+ my ($bytes, $offset) = @_;
+ my $string = '';
+
+ for (my $i = 5; $i < 18; $i++)
+ {
+ last if $bytes->[$offset+$i] == 0x0a
+ || $bytes->[$offset+$i] == 0x00;
+ $string .= chr($bytes->[$offset+$i])
+ if ($bytes->[$offset+$i] >= 32
+ && $bytes->[$offset+$i] < 127);
+ }
+ $string =~ s/\s+$//;
+
+ return $string;
+}
+
+# Some blocks contain different information:
+# 0x00, 0x00, 0x00, 0xfa: Additional standard timings block
+# 0x00, 0x00, 0x00, 0xfc: Monitor block
+# 0x00, 0x00, 0x00, 0xfd: Limits block
+# 0x00, 0x00, 0x00, 0xfe: Ascii block
+# 0x00, 0x00, 0x00, 0xff: Serial block
+# Return a reference to a hash containing all information.
+sub extract_detailed_timings
+{
+ my ($bytes) = @_;
+
+ my %info = ('timings' => {});
+
+ for (my $offset = 0x36; $offset < 0x7e; $offset += 18)
+ {
+ if ($bytes->[$offset] == 0x00
+ && $bytes->[$offset+1] == 0x00
+ && $bytes->[$offset+2] == 0x00
+ && $bytes->[$offset+4] == 0x00)
+ {
+ if ($bytes->[$offset+3] == 0xfa)
+ {
+ for (my $i = $offset + 5; $i < $offset + 17; $i += 2)
+ {
+ add_standard_timing($info{'timings'},
+ $bytes->[$i],
+ $bytes->[$i+1]);
+ }
+ }
+
+ elsif ($bytes->[$offset+3] == 0xfc)
+ {
+ $info{'monitor'} .= extract_string($bytes, $offset);
+ }
+
+ elsif ($bytes->[$offset+3] == 0xfd)
+ {
+ $info{'limits'}{'vsync_min'} = $bytes->[$offset+5];
+ $info{'limits'}{'vsync_max'} = $bytes->[$offset+6];
+ $info{'limits'}{'hsync_min'} = $bytes->[$offset+7];
+ $info{'limits'}{'hsync_max'} = $bytes->[$offset+8];
+ $info{'limits'}{'clock_max'} = $bytes->[$offset+9];
+ }
+
+ elsif ($bytes->[$offset+3] == 0xfe)
+ {
+ $info{'ascii'} .= extract_string($bytes, $offset);
+ }
+
+ elsif ($bytes->[$offset+3] == 0xff)
+ {
+ $info{'serial'} .= extract_string($bytes, $offset);
+ }
+
+ next;
+ }
+
+ # Detailed Timing
+ my $width = $bytes->[$offset+2] + (($bytes->[$offset+4] & 0xf0) << 4);
+ my $height = $bytes->[$offset+5] + (($bytes->[$offset+7] & 0xf0) << 4);
+ my $clock = extract_word($bytes, $offset) * 10000;
+ my $hblank = $bytes->[$offset+3] + (($bytes->[$offset+4] & 0x0f) << 8);
+ my $vblank = $bytes->[$offset+6] + (($bytes->[$offset+7] & 0x0f) << 8);
+ my $area = ($width + $hblank) * ($height + $vblank);
+ next unless $area; # Should not happen, but...
+ my $refresh = ($clock + $area / 2) / $area; # Proper rounding
+ add_timing($info{'timings'}, [$width, $height, $refresh]);
+ }
+
+ return \%info;
+}
+
+sub print_edid
+{
+ my ($bus, $address) = @_;
+ my $bytes;
+
+ if (-r "/sys/bus/i2c/devices/$bus-00$address/eeprom")
+ {
+ $bytes = get_edid_sysfs($bus, $address);
+ }
+ elsif (-r "/proc/sys/dev/sensors/eeprom-i2c-$bus-$address/00")
+ {
+ $bytes = get_edid_procfs($bus, $address);
+ }
+
+ return 1 unless defined $bytes;
+ return 2 unless good_signature($bytes);
+
+ print_line('Checksum', '%s', verify_checksum($bytes));
+ my $edid_version = extract_byte($bytes, 0x12);
+ my $edid_revision = extract_byte($bytes, 0x13);
+ print_line('EDID Version', '%u.%u', $edid_version,
+ $edid_revision);
+ if ($edid_version > 1 || $edid_revision > 2)
+ {
+ $standard_scales[0][0] = 16;
+ $standard_scales[0][1] = 10;
+ }
+ else
+ {
+ $standard_scales[0][0] = 1;
+ $standard_scales[0][1] = 1;
+ }
+
+ my $info = extract_detailed_timings($bytes);
+
+ print_line('Manufacturer ID', '%s', extract_manufacturer($bytes, 0x08));
+ print_line('Model Number', '0x%04X', extract_word($bytes, 0x0A));
+ print_line('Model Name', '%s', $info->{'monitor'})
+ if defined $info->{'monitor'};
+
+ if ($info->{'serial'})
+ {
+ print_line('Serial Number', '%s', $info->{'serial'})
+ }
+ elsif ((my $temp = extract_dword($bytes, 0x0C)))
+ {
+ print_line('Serial Number', '%u', $temp)
+ }
+
+ print_line('Manufacture Time', '%u-W%02u',
+ 1990 + extract_byte($bytes, 0x11),
+ extract_byte($bytes, 0x10));
+ print_line('Display Input', '%s', extract_display_input($bytes, 0x14));
+ print_line('Monitor Size (cm)', '%ux%u', extract_byte($bytes, 0x15),
+ extract_byte($bytes, 0x16));
+ print_line('Gamma Factor', '%.2f',
+ 1 + extract_byte($bytes, 0x17) / 100.0);
+ print_line('DPMS Modes', '%s', extract_dpms($bytes, 0x18));
+ print_line('Color Mode', '%s', extract_color_mode($bytes, 0x18))
+ if (($bytes->[0x18] & 0x18) != 0x18);
+ print_line('Additional Info', '%s', $info->{'ascii'})
+ if $info->{'ascii'};
+
+ if (defined($info->{'limits'}))
+ {
+ print_line('Vertical Sync (Hz)', '%u-%u',
+ $info->{'limits'}{'vsync_min'},
+ $info->{'limits'}{'vsync_max'});
+ print_line('Horizontal Sync (kHz)', '%u-%u',
+ $info->{'limits'}{'hsync_min'},
+ $info->{'limits'}{'hsync_max'});
+ print_line('Max Pixel Clock (MHz)', '%u',
+ $info->{'limits'}{'clock_max'} * 10)
+ unless $info->{'limits'}{'clock_max'} == 0xff;
+ }
+
+ print_timings($bytes, $info->{'timings'});
+ print("\n");
+ return 0;
+}
+
+# Get the address. Default to 0x50 if not given.
+my $address;
+if (defined($ARGV[1]))
+{
+ $address = $ARGV[1];
+ # Convert to decimal, whatever the value.
+ $address = oct $address if $address =~ m/^0/;
+ # Convert to an hexadecimal string.
+ $address = sprintf '%02x', $address;
+}
+else
+{
+ $address = '50';
+}
+
+if (defined($ARGV[0]))
+{
+ my $error = print_edid($ARGV[0], $address);
+
+ if ($error == 1)
+ {
+ print STDERR
+ "No EEPROM found at 0x$address on bus $ARGV[0].\n";
+ exit 1;
+ }
+ elsif ($error == 2)
+ {
+ print STDERR
+ "EEPROM found at 0x$address on bus $ARGV[0], but is not an EDID EEPROM.\n";
+ exit 1;
+ }
+}
+# If no bus is given, try them all.
+else
+{
+ my $total = 0;
+
+ for (my $i = 0; $i < 16; $i++)
+ {
+ $total++ unless print_edid($i, $address);
+ }
+
+ unless ($total)
+ {
+ print STDERR
+ "No EDID EEPROM found.\n";
+ exit 1;
+ }
+}
diff --git a/eeprom/decode-dimms b/eeprom/decode-dimms
new file mode 100755
index 0000000..1877ae1
--- /dev/null
+++ b/eeprom/decode-dimms
@@ -0,0 +1,2433 @@
+#!/usr/bin/perl -w
+#
+# EEPROM data decoder for SDRAM DIMM modules
+#
+# Copyright 1998, 1999 Philip Edelbrock <phil@netroedge.com>
+# modified by Christian Zuckschwerdt <zany@triq.net>
+# modified by Burkart Lingner <burkart@bollchen.de>
+# Copyright (C) 2005-2013 Jean Delvare <jdelvare@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA.
+#
+#
+# The eeprom driver must be loaded (unless option -x is used). For kernels
+# older than 2.6.0, the eeprom driver can be found in the lm-sensors package.
+#
+# References:
+# PC SDRAM Serial Presence
+# Detect (SPD) Specification, Intel,
+# 1997,1999, Rev 1.2B
+#
+# Jedec Standards 4.1.x & 4.5.x
+# http://www.jedec.org
+#
+
+require 5.004;
+
+use strict;
+use POSIX qw(ceil);
+use Fcntl qw(:DEFAULT :seek);
+use File::Basename;
+use vars qw($opt_html $opt_bodyonly $opt_side_by_side $opt_merge
+ $opt_igncheck $use_sysfs $use_hexdump $sbs_col_width
+ @vendors %decode_callback $revision @dimm $current %hexdump_cache);
+
+use constant LITTLEENDIAN => "little-endian";
+use constant BIGENDIAN => "big-endian";
+
+$revision = '$Revision$ ($Date$)';
+$revision =~ s/\$\w+: (.*?) \$/$1/g;
+$revision =~ s/ \([^()]*\)//;
+
+@vendors = (
+["AMD", "AMI", "Fairchild", "Fujitsu",
+ "GTE", "Harris", "Hitachi", "Inmos",
+ "Intel", "I.T.T.", "Intersil", "Monolithic Memories",
+ "Mostek", "Freescale (former Motorola)", "National", "NEC",
+ "RCA", "Raytheon", "Conexant (Rockwell)", "Seeq",
+ "NXP (former Signetics, Philips Semi.)", "Synertek", "Texas Instruments", "Toshiba",
+ "Xicor", "Zilog", "Eurotechnique", "Mitsubishi",
+ "Lucent (AT&T)", "Exel", "Atmel", "STMicroelectronics (former SGS/Thomson)",
+ "Lattice Semi.", "NCR", "Wafer Scale Integration", "IBM",
+ "Tristar", "Visic", "Intl. CMOS Technology", "SSSI",
+ "MicrochipTechnology", "Ricoh Ltd.", "VLSI", "Micron Technology",
+ "SK Hynix (former Hyundai Electronics)", "OKI Semiconductor", "ACTEL", "Sharp",
+ "Catalyst", "Panasonic", "IDT", "Cypress",
+ "DEC", "LSI Logic", "Zarlink (former Plessey)", "UTMC",
+ "Thinking Machine", "Thomson CSF", "Integrated CMOS (Vertex)", "Honeywell",
+ "Tektronix", "Oracle Corporation (former Sun Microsystems)", "Silicon Storage Technology", "ProMos/Mosel Vitelic",
+ "Infineon (former Siemens)", "Macronix", "Xerox", "Plus Logic",
+ "SunDisk", "Elan Circuit Tech.", "European Silicon Str.", "Apple Computer",
+ "Xilinx", "Compaq", "Protocol Engines", "SCI",
+ "Seiko Instruments", "Samsung", "I3 Design System", "Klic",
+ "Crosspoint Solutions", "Alliance Semiconductor", "Tandem", "Hewlett-Packard",
+ "Integrated Silicon Solutions", "Brooktree", "New Media", "MHS Electronic",
+ "Performance Semi.", "Winbond Electronic", "Kawasaki Steel", "Bright Micro",
+ "TECMAR", "Exar", "PCMCIA", "LG Semi (former Goldstar)",
+ "Northern Telecom", "Sanyo", "Array Microsystems", "Crystal Semiconductor",
+ "Analog Devices", "PMC-Sierra", "Asparix", "Convex Computer",
+ "Quality Semiconductor", "Nimbus Technology", "Transwitch", "Micronas (ITT Intermetall)",
+ "Cannon", "Altera", "NEXCOM", "QUALCOMM",
+ "Sony", "Cray Research", "AMS(Austria Micro)", "Vitesse",
+ "Aster Electronics", "Bay Networks (Synoptic)", "Zentrum or ZMD", "TRW",
+ "Thesys", "Solbourne Computer", "Allied-Signal", "Dialog",
+ "Media Vision", "Numonyx Corporation (former Level One Communication)"],
+["Cirrus Logic", "National Instruments", "ILC Data Device", "Alcatel Mietec",
+ "Micro Linear", "Univ. of NC", "JTAG Technologies", "BAE Systems",
+ "Nchip", "Galileo Tech", "Bestlink Systems", "Graychip",
+ "GENNUM", "VideoLogic", "Robert Bosch", "Chip Express",
+ "DATARAM", "United Microelec Corp.", "TCSI", "Smart Modular",
+ "Hughes Aircraft", "Lanstar Semiconductor", "Qlogic", "Kingston",
+ "Music Semi", "Ericsson Components", "SpaSE", "Eon Silicon Devices",
+ "Programmable Micro Corp", "DoD", "Integ. Memories Tech.", "Corollary Inc.",
+ "Dallas Semiconductor", "Omnivision", "EIV(Switzerland)", "Novatel Wireless",
+ "Zarlink (former Mitel)", "Clearpoint", "Cabletron", "STEC (former Silicon Technology)",
+ "Vanguard", "Hagiwara Sys-Com", "Vantis", "Celestica",
+ "Century", "Hal Computers", "Rohm Company Ltd.", "Juniper Networks",
+ "Libit Signal Processing", "Mushkin Enhanced Memory", "Tundra Semiconductor", "Adaptec Inc.",
+ "LightSpeed Semi.", "ZSP Corp.", "AMIC Technology", "Adobe Systems",
+ "Dynachip", "PNY Technologies Inc. (former PNY Electronics)", "Newport Digital", "MMC Networks",
+ "T Square", "Seiko Epson", "Broadcom", "Viking Components",
+ "V3 Semiconductor", "Flextronics (former Orbit)", "Suwa Electronics", "Transmeta",
+ "Micron CMS", "American Computer & Digital Components Inc", "Enhance 3000 Inc", "Tower Semiconductor",
+ "CPU Design", "Price Point", "Maxim Integrated Product", "Tellabs",
+ "Centaur Technology", "Unigen Corporation", "Transcend Information", "Memory Card Technology",
+ "CKD Corporation Ltd.", "Capital Instruments, Inc.", "Aica Kogyo, Ltd.", "Linvex Technology",
+ "MSC Vertriebs GmbH", "AKM Company, Ltd.", "Dynamem, Inc.", "NERA ASA",
+ "GSI Technology", "Dane-Elec (C Memory)", "Acorn Computers", "Lara Technology",
+ "Oak Technology, Inc.", "Itec Memory", "Tanisys Technology", "Truevision",
+ "Wintec Industries", "Super PC Memory", "MGV Memory", "Galvantech",
+ "Gadzoox Nteworks", "Multi Dimensional Cons.", "GateField", "Integrated Memory System",
+ "Triscend", "XaQti", "Goldenram", "Clear Logic",
+ "Cimaron Communications", "Nippon Steel Semi. Corp.", "Advantage Memory", "AMCC",
+ "LeCroy", "Yamaha Corporation", "Digital Microwave", "NetLogic Microsystems",
+ "MIMOS Semiconductor", "Advanced Fibre", "BF Goodrich Data.", "Epigram",
+ "Acbel Polytech Inc.", "Apacer Technology", "Admor Memory", "FOXCONN",
+ "Quadratics Superconductor", "3COM"],
+["Camintonn Corporation", "ISOA Incorporated", "Agate Semiconductor", "ADMtek Incorporated",
+ "HYPERTEC", "Adhoc Technologies", "MOSAID Technologies", "Ardent Technologies",
+ "Switchcore", "Cisco Systems, Inc.", "Allayer Technologies", "WorkX AG (Wichman)",
+ "Oasis Semiconductor", "Novanet Semiconductor", "E-M Solutions", "Power General",
+ "Advanced Hardware Arch.", "Inova Semiconductors GmbH", "Telocity", "Delkin Devices",
+ "Symagery Microsystems", "C-Port Corporation", "SiberCore Technologies", "Southland Microsystems",
+ "Malleable Technologies", "Kendin Communications", "Great Technology Microcomputer", "Sanmina Corporation",
+ "HADCO Corporation", "Corsair", "Actrans System Inc.", "ALPHA Technologies",
+ "Silicon Laboratories, Inc. (Cygnal)", "Artesyn Technologies", "Align Manufacturing", "Peregrine Semiconductor",
+ "Chameleon Systems", "Aplus Flash Technology", "MIPS Technologies", "Chrysalis ITS",
+ "ADTEC Corporation", "Kentron Technologies", "Win Technologies", "Tezzaron Semiconductor (former Tachyon Semiconductor)",
+ "Extreme Packet Devices", "RF Micro Devices", "Siemens AG", "Sarnoff Corporation",
+ "Itautec SA (former Itautec Philco SA)", "Radiata Inc.", "Benchmark Elect. (AVEX)", "Legend",
+ "SpecTek Incorporated", "Hi/fn", "Enikia Incorporated", "SwitchOn Networks",
+ "AANetcom Incorporated", "Micro Memory Bank", "ESS Technology", "Virata Corporation",
+ "Excess Bandwidth", "West Bay Semiconductor", "DSP Group", "Newport Communications",
+ "Chip2Chip Incorporated", "Phobos Corporation", "Intellitech Corporation", "Nordic VLSI ASA",
+ "Ishoni Networks", "Silicon Spice", "Alchemy Semiconductor", "Agilent Technologies",
+ "Centillium Communications", "W.L. Gore", "HanBit Electronics", "GlobeSpan",
+ "Element 14", "Pycon", "Saifun Semiconductors", "Sibyte, Incorporated",
+ "MetaLink Technologies", "Feiya Technology", "I & C Technology", "Shikatronics",
+ "Elektrobit", "Megic", "Com-Tier", "Malaysia Micro Solutions",
+ "Hyperchip", "Gemstone Communications", "Anadigm (former Anadyne)", "3ParData",
+ "Mellanox Technologies", "Tenx Technologies", "Helix AG", "Domosys",
+ "Skyup Technology", "HiNT Corporation", "Chiaro", "MDT Technologies GmbH (former MCI Computer GMBH)",
+ "Exbit Technology A/S", "Integrated Technology Express", "AVED Memory", "Legerity",
+ "Jasmine Networks", "Caspian Networks", "nCUBE", "Silicon Access Networks",
+ "FDK Corporation", "High Bandwidth Access", "MultiLink Technology", "BRECIS",
+ "World Wide Packets", "APW", "Chicory Systems", "Xstream Logic",
+ "Fast-Chip", "Zucotto Wireless", "Realchip", "Galaxy Power",
+ "eSilicon", "Morphics Technology", "Accelerant Networks", "Silicon Wave",
+ "SandCraft", "Elpida"],
+["Solectron", "Optosys Technologies", "Buffalo (former Melco)", "TriMedia Technologies",
+ "Cyan Technologies", "Global Locate", "Optillion", "Terago Communications",
+ "Ikanos Communications", "Princeton Technology", "Nanya Technology", "Elite Flash Storage",
+ "Mysticom", "LightSand Communications", "ATI Technologies", "Agere Systems",
+ "NeoMagic", "AuroraNetics", "Golden Empire", "Mushkin",
+ "Tioga Technologies", "Netlist", "TeraLogic", "Cicada Semiconductor",
+ "Centon Electronics", "Tyco Electronics", "Magis Works", "Zettacom",
+ "Cogency Semiconductor", "Chipcon AS", "Aspex Technology", "F5 Networks",
+ "Programmable Silicon Solutions", "ChipWrights", "Acorn Networks", "Quicklogic",
+ "Kingmax Semiconductor", "BOPS", "Flasys", "BitBlitz Communications",
+ "eMemory Technology", "Procket Networks", "Purple Ray", "Trebia Networks",
+ "Delta Electronics", "Onex Communications", "Ample Communications", "Memory Experts Intl",
+ "Astute Networks", "Azanda Network Devices", "Dibcom", "Tekmos",
+ "API NetWorks", "Bay Microsystems", "Firecron Ltd", "Resonext Communications",
+ "Tachys Technologies", "Equator Technology", "Concept Computer", "SILCOM",
+ "3Dlabs", "c't Magazine", "Sanera Systems", "Silicon Packets",
+ "Viasystems Group", "Simtek", "Semicon Devices Singapore", "Satron Handelsges",
+ "Improv Systems", "INDUSYS GmbH", "Corrent", "Infrant Technologies",
+ "Ritek Corp", "empowerTel Networks", "Hypertec", "Cavium Networks",
+ "PLX Technology", "Massana Design", "Intrinsity", "Valence Semiconductor",
+ "Terawave Communications", "IceFyre Semiconductor", "Primarion", "Picochip Designs Ltd",
+ "Silverback Systems", "Jade Star Technologies", "Pijnenburg Securealink",
+ "takeMS - Ultron AG (former Memorysolution GmbH)", "Cambridge Silicon Radio",
+ "Swissbit", "Nazomi Communications", "eWave System",
+ "Rockwell Collins", "Picocel Co., Ltd.", "Alphamosaic Ltd", "Sandburst",
+ "SiCon Video", "NanoAmp Solutions", "Ericsson Technology", "PrairieComm",
+ "Mitac International", "Layer N Networks", "MtekVision", "Allegro Networks",
+ "Marvell Semiconductors", "Netergy Microelectronic", "NVIDIA", "Internet Machines",
+ "Memorysolution GmbH (former Peak Electronics)", "Litchfield Communication", "Accton Technology", "Teradiant Networks",
+ "Scaleo Chip (former Europe Technologies)", "Cortina Systems", "RAM Components", "Raqia Networks",
+ "ClearSpeed", "Matsushita Battery", "Xelerated", "SimpleTech",
+ "Utron Technology", "Astec International", "AVM gmbH", "Redux Communications",
+ "Dot Hill Systems", "TeraChip"],
+["T-RAM Incorporated", "Innovics Wireless", "Teknovus", "KeyEye Communications",
+ "Runcom Technologies", "RedSwitch", "Dotcast", "Silicon Mountain Memory",
+ "Signia Technologies", "Pixim", "Galazar Networks", "White Electronic Designs",
+ "Patriot Scientific", "Neoaxiom Corporation", "3Y Power Technology", "Scaleo Chip (former Europe Technologies)",
+ "Potentia Power Systems", "C-guys Incorporated", "Digital Communications Technology Incorporated", "Silicon-Based Technology",
+ "Fulcrum Microsystems", "Positivo Informatica Ltd", "XIOtech Corporation", "PortalPlayer",
+ "Zhiying Software", "Parker Vision, Inc. (former Direct2Data)", "Phonex Broadband", "Skyworks Solutions",
+ "Entropic Communications", "I'M Intelligent Memory Ltd (former Pacific Force Technology)", "Zensys A/S", "Legend Silicon Corp.",
+ "sci-worx GmbH", "SMSC (former Oasis Silicon Systems)", "Renesas Electronics (former Renesas Technology)", "Raza Microelectronics",
+ "Phyworks", "MediaTek", "Non-cents Productions", "US Modular",
+ "Wintegra Ltd", "Mathstar", "StarCore", "Oplus Technologies",
+ "Mindspeed", "Just Young Computer", "Radia Communications", "OCZ",
+ "Emuzed", "LOGIC Devices", "Inphi Corporation", "Quake Technologies",
+ "Vixel", "SolusTek", "Kongsberg Maritime", "Faraday Technology",
+ "Altium Ltd.", "Insyte", "ARM Ltd.", "DigiVision",
+ "Vativ Technologies", "Endicott Interconnect Technologies", "Pericom", "Bandspeed",
+ "LeWiz Communications", "CPU Technology", "Ramaxel Technology", "DSP Group",
+ "Axis Communications", "Legacy Electronics", "Chrontel", "Powerchip Semiconductor",
+ "MobilEye Technologies", "Excel Semiconductor", "A-DATA Technology", "VirtualDigm",
+ "G Skill Intl", "Quanta Computer", "Yield Microelectronics", "Afa Technologies",
+ "KINGBOX Technology Co. Ltd.", "Ceva", "iStor Networks", "Advance Modules",
+ "Microsoft", "Open-Silicon", "Goal Semiconductor", "ARC International",
+ "Simmtec", "Metanoia", "Key Stream", "Lowrance Electronics",
+ "Adimos", "SiGe Semiconductor", "Fodus Communications", "Credence Systems Corp.",
+ "Genesis Microchip Inc.", "Vihana, Inc.", "WIS Technologies", "GateChange Technologies",
+ "High Density Devices AS", "Synopsys", "Gigaram", "Enigma Semiconductor Inc.",
+ "Century Micro Inc.", "Icera Semiconductor", "Mediaworks Integrated Systems", "O'Neil Product Development",
+ "Supreme Top Technology Ltd.", "MicroDisplay Corporation", "Team Group Inc.", "Sinett Corporation",
+ "Toshiba Corporation", "Tensilica", "SiRF Technology", "Bacoc Inc.",
+ "SMaL Camera Technologies", "Thomson SC", "Airgo Networks", "Wisair Ltd.",
+ "SigmaTel", "Arkados", "Compete IT gmbH Co. KG", "Eudar Technology Inc.",
+ "Focus Enhancements", "Xyratex"],
+["Specular Networks", "Patriot Memory", "U-Chip Technology Corp.", "Silicon Optix",
+ "Greenfield Networks", "CompuRAM GmbH", "Stargen, Inc.", "NetCell Corporation",
+ "Excalibrus Technologies Ltd", "SCM Microsystems", "Xsigo Systems, Inc.", "CHIPS & Systems Inc",
+ "Tier 1 Multichip Solutions", "CWRL Labs", "Teradici", "Gigaram, Inc.",
+ "g2 Microsystems", "PowerFlash Semiconductor", "P.A. Semi, Inc.", "NovaTech Solutions, S.A.",
+ "c2 Microsystems, Inc.", "Level5 Networks", "COS Memory AG", "Innovasic Semiconductor",
+ "02IC Co. Ltd", "Tabula, Inc.", "Crucial Technology", "Chelsio Communications",
+ "Solarflare Communications", "Xambala Inc.", "EADS Astrium", "Terra Semiconductor Inc. (former ATO Semicon Co. Ltd.)",
+ "Imaging Works, Inc.", "Astute Networks, Inc.", "Tzero", "Emulex",
+ "Power-One", "Pulse~LINK Inc.", "Hon Hai Precision Industry", "White Rock Networks Inc.",
+ "Telegent Systems USA, Inc.", "Atrua Technologies, Inc.", "Acbel Polytech Inc.",
+ "eRide Inc.","ULi Electronics Inc.", "Magnum Semiconductor Inc.", "neoOne Technology, Inc.",
+ "Connex Technology, Inc.", "Stream Processors, Inc.", "Focus Enhancements", "Telecis Wireless, Inc.",
+ "uNav Microelectronics", "Tarari, Inc.", "Ambric, Inc.", "Newport Media, Inc.", "VMTS",
+ "Enuclia Semiconductor, Inc.", "Virtium Technology Inc.", "Solid State System Co., Ltd.", "Kian Tech LLC",
+ "Artimi", "Power Quotient International", "Avago Technologies", "ADTechnology", "Sigma Designs",
+ "SiCortex, Inc.", "Ventura Technology Group", "eASIC", "M.H.S. SAS", "Micro Star International",
+ "Rapport Inc.", "Makway International", "Broad Reach Engineering Co.",
+ "Semiconductor Mfg Intl Corp", "SiConnect", "FCI USA Inc.", "Validity Sensors",
+ "Coney Technology Co. Ltd.", "Spans Logic", "Neterion Inc.", "Qimonda",
+ "New Japan Radio Co. Ltd.", "Velogix", "Montalvo Systems", "iVivity Inc.", "Walton Chaintech",
+ "AENEON", "Lorom Industrial Co. Ltd.", "Radiospire Networks", "Sensio Technologies, Inc.",
+ "Nethra Imaging", "Hexon Technology Pte Ltd", "CompuStocx (CSX)", "Methode Electronics, Inc.",
+ "Connect One Ltd.", "Opulan Technologies", "Septentrio NV", "Goldenmars Technology Inc.",
+ "Kreton Corporation", "Cochlear Ltd.", "Altair Semiconductor", "NetEffect, Inc.",
+ "Spansion, Inc.", "Taiwan Semiconductor Mfg", "Emphany Systems Inc.",
+ "ApaceWave Technologies", "Mobilygen Corporation", "Tego", "Cswitch Corporation",
+ "Haier (Beijing) IC Design Co.", "MetaRAM", "Axel Electronics Co. Ltd.", "Tilera Corporation",
+ "Aquantia", "Vivace Semiconductor", "Redpine Signals", "Octalica", "InterDigital Communications",
+ "Avant Technology", "Asrock, Inc.", "Availink", "Quartics, Inc.", "Element CXI",
+ "Innovaciones Microelectronicas", "VeriSilicon Microelectronics", "W5 Networks"],
+["MOVEKING", "Mavrix Technology, Inc.", "CellGuide Ltd.", "Faraday Technology",
+ "Diablo Technologies, Inc.", "Jennic", "Octasic", "Molex Incorporated", "3Leaf Networks",
+ "Bright Micron Technology", "Netxen", "NextWave Broadband Inc.", "DisplayLink", "ZMOS Technology",
+ "Tec-Hill", "Multigig, Inc.", "Amimon", "Euphonic Technologies, Inc.", "BRN Phoenix",
+ "InSilica", "Ember Corporation", "Avexir Technologies Corporation", "Echelon Corporation",
+ "Edgewater Computer Systems", "XMOS Semiconductor Ltd.", "GENUSION, Inc.", "Memory Corp NV",
+ "SiliconBlue Technologies", "Rambus Inc.", "Andes Technology Corporation", "Coronis Systems",
+ "Achronix Semiconductor", "Siano Mobile Silicon Ltd.", "Semtech Corporation", "Pixelworks Inc.",
+ "Gaisler Research AB", "Teranetics", "Toppan Printing Co. Ltd.", "Kingxcon",
+ "Silicon Integrated Systems", "I-O Data Device, Inc.", "NDS Americas Inc.", "Solomon Systech Limited",
+ "On Demand Microelectronics", "Amicus Wireless Inc.", "SMARDTV SNC", "Comsys Communication Ltd.",
+ "Movidia Ltd.", "Javad GNSS, Inc.", "Montage Technology Group", "Trident Microsystems", "Super Talent",
+ "Optichron, Inc.", "Future Waves UK Ltd.", "SiBEAM, Inc.", "Inicore, Inc.", "Virident Systems",
+ "M2000, Inc.", "ZeroG Wireless, Inc.", "Gingle Technology Co. Ltd.", "Space Micro Inc.", "Wilocity",
+ "Novafora, Inc.", "iKoa Corporation", "ASint Technology", "Ramtron", "Plato Networks Inc.",
+ "IPtronics AS", "Infinite-Memories", "Parade Technologies Inc.", "Dune Networks",
+ "GigaDevice Semiconductor", "Modu Ltd.", "CEITEC", "Northrop Grumman", "XRONET Corporation",
+ "Sicon Semiconductor AB", "Atla Electronics Co. Ltd.", "TOPRAM Technology", "Silego Technology Inc.",
+ "Kinglife", "Ability Industries Ltd.", "Silicon Power Computer & Communications",
+ "Augusta Technology, Inc.", "Nantronics Semiconductors", "Hilscher Gesellschaft", "Quixant Ltd.",
+ "Percello Ltd.", "NextIO Inc.", "Scanimetrics Inc.", "FS-Semi Company Ltd.", "Infinera Corporation",
+ "SandForce Inc.", "Lexar Media", "Teradyne Inc.", "Memory Exchange Corp.", "Suzhou Smartek Electronics",
+ "Avantium Corporation", "ATP Electronics Inc.", "Valens Semiconductor Ltd", "Agate Logic, Inc.",
+ "Netronome", "Zenverge, Inc.", "N-trig Ltd", "SanMax Technologies Inc.", "Contour Semiconductor Inc.",
+ "TwinMOS", "Silicon Systems, Inc.", "V-Color Technology Inc.", "Certicom Corporation", "JSC ICC Milandr",
+ "PhotoFast Global Inc.", "InnoDisk Corporation", "Muscle Power", "Energy Micro", "Innofidei",
+ "CopperGate Communications", "Holtek Semiconductor Inc.", "Myson Century, Inc.", "FIDELIX",
+ "Red Digital Cinema", "Densbits Technology", "Zempro", "MoSys", "Provigent", "Triad Semiconductor, Inc."],
+["Siklu Communication Ltd.", "A Force Manufacturing Ltd.", "Strontium", "Abilis Systems", "Siglead, Inc.",
+ "Ubicom, Inc.", "Unifosa Corporation", "Stretch, Inc.", "Lantiq Deutschland GmbH", "Visipro",
+ "EKMemory", "Microelectronics Institute ZTE", "Cognovo Ltd.", "Carry Technology Co. Ltd.", "Nokia",
+ "King Tiger Technology", "Sierra Wireless", "HT Micron", "Albatron Technology Co. Ltd.",
+ "Leica Geosystems AG", "BroadLight", "AEXEA", "ClariPhy Communications, Inc.", "Green Plug",
+ "Design Art Networks", "Mach Xtreme Technology Ltd.", "ATO Solutions Co. Ltd.", "Ramsta",
+ "Greenliant Systems, Ltd.", "Teikon", "Antec Hadron", "NavCom Technology, Inc.",
+ "Shanghai Fudan Microelectronics", "Calxeda, Inc.", "JSC EDC Electronics", "Kandit Technology Co. Ltd.",
+ "Ramos Technology", "Goldenmars Technology", "XeL Technology Inc.", "Newzone Corporation",
+ "ShenZhen MercyPower Tech", "Nanjing Yihuo Technology", "Nethra Imaging Inc.", "SiTel Semiconductor BV",
+ "SolidGear Corporation", "Topower Computer Ind Co Ltd.", "Wilocity", "Profichip GmbH",
+ "Gerad Technologies", "Ritek Corporation", "Gomos Technology Limited", "Memoright Corporation",
+ "D-Broad, Inc.", "HiSilicon Technologies", "Syndiant Inc.", "Enverv Inc.", "Cognex",
+ "Xinnova Technology Inc.", "Ultron AG", "Concord Idea Corporation", "AIM Corporation",
+ "Lifetime Memory Products", "Ramsway", "Recore Systems BV", "Haotian Jinshibo Science Tech",
+ "Being Advanced Memory", "Adesto Technologies", "Giantec Semiconductor, Inc.", "HMD Electronics AG",
+ "Gloway International (HK)", "Kingcore", "Anucell Technology Holding",
+ "Accord Software & Systems Pvt. Ltd.", "Active-Semi Inc.", "Denso Corporation", "TLSI Inc.",
+ "Shenzhen Daling Electronic Co. Ltd.", "Mustang", "Orca Systems", "Passif Semiconductor",
+ "GigaDevice Semiconductor (Beijing) Inc.", "Memphis Electronic", "Beckhoff Automation GmbH",
+ "Harmony Semiconductor Corp (former ProPlus Design Solutions)", "Air Computers SRL", "TMT Memory",
+ "Eorex Corporation", "Xingtera", "Netsol", "Bestdon Technology Co. Ltd.", "Baysand Inc.",
+ "Uroad Technology Co. Ltd. (former Triple Grow Industrial Ltd.)", "Wilk Elektronik S.A.",
+ "AAI", "Harman", "Berg Microelectronics Inc.", "ASSIA, Inc.", "Visiontek Products LLC",
+ "OCMEMORY", "Welink Solution Inc.", "Shark Gaming", "Avalanche Technology",
+ "R&D Center ELVEES OJSC", "KingboMars Technology Co. Ltd.",
+ "High Bridge Solutions Industria Eletronica", "Transcend Technology Co. Ltd.",
+ "Everspin Technologies", "Hon-Hai Precision", "Smart Storage Systems", "Toumaz Group",
+ "Zentel Electronics Corporation", "Panram International Corporation",
+ "Silicon Space Technology", "LITE-ON IT Corporation", "Inuitive", "HMicro",
+ "BittWare Inc.", "GLOBALFOUNDRIES", "ACPI Digital Co. Ltd", "Annapurna Labs",
+ "AcSiP Technology Corporation", "Idea! Electronic Systems", "Gowe Technology Co. Ltd",
+ "Hermes Testing Solutions Inc.", "Positivo BGH", "Intelligence Silicon Technology"],
+["3D PLUS", "Diehl Aerospace", "Fairchild", "Mercury Systems",
+ "Sonics Inc.", "GE Intelligent Platforms GmbH & Co.", "Shenzhen Jinge Information Co. Ltd",
+ "SCWW", "Silicon Motion Inc.", "Anurag", "King Kong",
+ "FROM30 Co. Ltd", "Gowin Semiconductor Corp", "Fremont Micro Devices Ltd",
+ "Ericsson Modems", "Exelis", "Satixfy Ltd", "Galaxy Microsystems Ltd",
+ "Gloway International Co. Ltd", "Lab", "Smart Energy Instruments",
+ "Approved Memory Corporation", "Axell Corporation", "ISD Technology Limited",
+ "Phytium", "Xi'an SinoChip Semiconductor", "Ambiq Micro", "eveRAM Technology Inc.",
+ "Infomax", "Butterfly Network Inc.", "Shenzhen City Gcai Electronics",
+ "Stack Devices Corporation", "ADK Media Group", "TSP Global Co. Ltd",
+ "HighX", "Shenzhen Elicks Technology", "ISSI/Chingis", "Google Inc.",
+ "Dasima International Development", "Leahkinn Technology Limited",
+ "HIMA Paul Hildebrandt GmbH Co KG", "Keysight Technologies",
+ "Techcomp International (Fastable)", "Ancore Technology Corporation",
+ "Nuvoton", "Korea Uhbele International Group Ltd", "Ikegami Tsushinki Co. Ltd",
+ "RelChip Inc.", "Baikal Electronics", "Nemostech Inc.",
+ "Memorysolution GmbH", "Silicon Integrated Systems Corporation",
+ "Xiede", "Multilaser Components", "Flash Chi", "Jone",
+ "GCT Semiconductor Inc.", "Hong Kong Zetta Device Technology",
+ "Unimemory Technology(s) Pte Ltd", "Cuso", "Kuso",
+ "Uniquify Inc.", "Skymedi Corporation", "Core Chance Co. Ltd",
+ "Tekism Co. Ltd", "Seagate Technology PLC", "Hong Kong Gaia Group Co. Limited",
+ "Gigacom Semiconductor LLC", "V2 Technologies", "TLi", "Neotion",
+ "Lenovo", "Shenzhen Zhongteng Electronic Corp. Ltd", "Compound Photonics",
+ "Cognimem Technologies Inc.", "Shenzhen Pango Microsystems Co. Ltd",
+ "Vasekey", "Cal-Comp Industria de Semicondutores", "Eyenix Co. Ltd",
+ "Heoriady", "Accelerated Memory Production Inc.", "INVECAS Inc.",
+ "AP Memory", "Douqi Technology", "Etron Technology Inc.",
+ "Indie Semiconductor", "Socionext Inc.", "HGST", "EVGA",
+ "Audience Inc.", "EpicGear", "Vitesse Enterprise Co.",
+ "Foxtronn International Corporation", "Bretelon Inc.",
+ "Zbit Semiconductor Inc."]
+);
+
+$use_sysfs = -d '/sys/bus';
+
+# We consider that no data was written to this area of the SPD EEPROM if
+# all bytes read 0x00 or all bytes read 0xff
+sub spd_written(@)
+{
+ my $all_00 = 1;
+ my $all_ff = 1;
+
+ foreach my $b (@_) {
+ $all_00 = 0 unless $b == 0x00;
+ $all_ff = 0 unless $b == 0xff;
+ return 1 unless $all_00 or $all_ff;
+ }
+
+ return 0;
+}
+
+sub parity($)
+{
+ my $n = shift;
+ my $parity = 0;
+
+ while ($n) {
+ $parity++ if ($n & 1);
+ $n >>= 1;
+ }
+
+ return ($parity & 1);
+}
+
+# The code byte includes parity, the count byte does not.
+sub manufacturer_common($$)
+{
+ my ($count, $code) = @_;
+ my $manufacturer;
+
+ return "Invalid" if parity($code) != 1
+ or ($code &= 0x7F) == 0;
+ return "Unknown" if $count >= @vendors
+ or $code - 1 >= @{$vendors[$count]};
+ $manufacturer = $vendors[$count][$code - 1];
+ $manufacturer =~ s/ \(former .*\)$// if $opt_side_by_side;
+ return $manufacturer;
+}
+
+# New encoding format (as of DDR3) for manufacturer just has a count of
+# leading 0x7F rather than all the individual bytes. The count bytes includes
+# parity!
+sub manufacturer_ddr3($$)
+{
+ my ($count, $code) = @_;
+ my $manufacturer;
+
+ return "Undefined" unless spd_written($count, $code);
+
+ $manufacturer = manufacturer_common($count & 0x7F, $code);
+ $manufacturer .= "? (Invalid parity)" if parity($count) != 1;
+ return $manufacturer;
+}
+
+sub manufacturer(@)
+{
+ my @bytes = @_;
+ my $ai = 0;
+ my $first;
+
+ return ("Undefined", []) unless spd_written(@bytes);
+
+ while (defined($first = shift(@bytes)) && $first == 0x7F) {
+ $ai++;
+ }
+
+ return ("Invalid", []) unless defined $first;
+ return (manufacturer_common($ai, $first), \@bytes);
+}
+
+sub manufacturer_data(@)
+{
+ my $hex = "";
+ my $asc = "";
+
+ return unless spd_written(@_);
+
+ foreach my $byte (@_) {
+ $hex .= sprintf("\%02X ", $byte);
+ $asc .= ($byte >= 32 && $byte < 127) ? chr($byte) : '?';
+ }
+
+ return "$hex(\"$asc\")";
+}
+
+sub part_number(@)
+{
+ my $asc = "";
+ my $byte;
+
+ while (defined ($byte = shift) && $byte >= 32 && $byte < 127) {
+ $asc .= chr($byte);
+ }
+
+ return ($asc eq "") ? "Undefined" : $asc;
+}
+
+sub cas_latencies(@)
+{
+ return "None" unless @_;
+ return join ', ', map("${_}T", sort { $b <=> $a } @_);
+}
+
+# Real printing functions
+
+sub html_encode($)
+{
+ my $text = shift;
+ $text =~ s/</\</sg;
+ $text =~ s/>/\>/sg;
+ $text =~ s/ degrees C/\°C/sg;
+ $text =~ s/\n/<br\/>\n/sg;
+ return $text;
+}
+
+sub same_values(@)
+{
+ my $value = shift;
+ while (@_) {
+ return 0 unless $value eq shift;
+ }
+ return 1;
+}
+
+sub real_printl($$) # print a line w/ label and values
+{
+ my ($label, @values) = @_;
+ local $_;
+ my $same_values = same_values(@values);
+
+ # If all values are N/A, don't bother printing
+ return if $values[0] eq "N/A" and $same_values;
+
+ if ($opt_html) {
+ $label = html_encode($label);
+ @values = map { html_encode($_) } @values;
+ print "<tr><td style=\"vertical-align: top;\">$label</td>";
+ if (!$opt_merge) {
+ print "<td>$_</td>" foreach @values;
+ } elsif ($same_values) {
+ print "<td colspan=\"".(scalar @values)."\">$values[0]</td>";
+ } else {
+ # For HTML output, merge adjacent cells even if
+ # the whole line cannot be merged.
+ my $colcnt = 0;
+ while (@values) {
+ $colcnt++;
+ my $value = shift @values;
+ next if (@values && $value eq $values[0]);
+ print "<td" . ($colcnt > 1 ? " colspan=\"$colcnt\"" : "") .">$value</td>";
+ $colcnt = 0;
+ }
+ }
+ print "</tr>\n";
+ } else {
+ if ($opt_merge && $same_values) {
+ splice(@values, 1);
+ }
+
+ my $format = "%-47s".((" %-".$sbs_col_width."s") x (scalar @values - 1))." %s\n";
+ my $maxl = 0; # Keep track of the max number of lines
+
+ # It's a bit tricky because each value may span over more than
+ # one line. We can easily extract the values per column, but
+ # we need them per line at printing time. So we have to
+ # prepare a 2D array with all the individual string fragments.
+ my ($col, @lines);
+ for ($col = 0; $col < @values; $col++) {
+ my @cells = split /\n/, $values[$col];
+ $maxl = @cells if @cells > $maxl;
+ for (my $l = 0; $l < @cells; $l++) {
+ $lines[$l]->[$col] = $cells[$l];
+ }
+ }
+
+ # Also make sure there are no holes in the array
+ for (my $l = 0; $l < $maxl; $l++) {
+ for ($col = 0; $col < @values; $col++) {
+ $lines[$l]->[$col] = ""
+ if not defined $lines[$l]->[$col];
+ }
+ }
+
+ printf $format, $label, @{shift @lines};
+ printf $format, "", @{$_} foreach (@lines);
+ }
+}
+
+sub printl2 # print a line w/ label and value (outside a table)
+{
+ my ($label, $value, $style) = @_;
+ if ($opt_html) {
+ $label = html_encode($label);
+ $value = html_encode($value);
+ print "<p", (defined $style ? " style=\"$style\"" : ""), ">";
+ }
+ print "$label: $value\n";
+ print "</p>\n" if $opt_html;
+}
+
+sub real_prints($) # print separator w/ given text
+{
+ my ($label, $ncol) = @_;
+ $ncol = 1 unless $ncol;
+ if ($opt_html) {
+ $label = html_encode($label);
+ print "<tr><td style=\"font-weight: bold; text-align: center;\" colspan=\"".(1+$ncol)."\">$label</td></tr>\n";
+ } else {
+ print "\n---=== $label ===---\n";
+ }
+}
+
+sub printh($$) # print header w/ given text
+{
+ my ($header, $sub) = @_;
+ if ($opt_html) {
+ $header = html_encode($header);
+ $sub = html_encode($sub);
+ print "<h1>$header</h1>\n";
+ print "<p>$sub</p>\n";
+ } else {
+ print "\n$header\n$sub\n";
+ }
+}
+
+sub printc($) # print comment
+{
+ my ($comment) = @_;
+ if ($opt_html) {
+ $comment = html_encode($comment);
+ print "<!-- $comment -->\n";
+ } else {
+ print "# $comment\n";
+ }
+}
+
+# Fake printing functions
+# These don't actually print anything, instead they store the desired
+# output for later processing.
+
+sub printl($$) # print a line w/ label and value
+{
+ my @output = (\&real_printl, @_);
+ push @{$dimm[$current]->{output}}, \@output;
+}
+
+sub printl_cond($$$) # same as printl but conditional
+{
+ my ($cond, $label, $value) = @_;
+ return unless $cond || $opt_side_by_side;
+ printl($label, $cond ? $value : "N/A");
+}
+
+sub prints($) # print separator w/ given text
+{
+ my @output = (\&real_prints, @_);
+ push @{$dimm[$current]->{output}}, \@output;
+}
+
+# Helper functions
+
+sub tns1($) # print a time in ns, with 1 decimal digit
+{
+ return sprintf("%.1f ns", $_[0]);
+}
+
+sub tns($) # print a time in ns, with 2 decimal digits
+{
+ return sprintf("%3.2f ns", $_[0]);
+}
+
+sub tns3($) # print a time in ns, with 3 decimal digits
+{
+ return sprintf("%.3f ns", $_[0]);
+}
+
+sub value_or_undefined
+{
+ my ($value, $unit) = @_;
+ return "Undefined!" unless $value;
+ $value .= " $unit" if defined $unit;
+ return $value;
+}
+
+# Common to SDR, DDR and DDR2 SDRAM
+sub sdram_voltage_interface_level($)
+{
+ my @levels = (
+ "TTL (5V tolerant)", # 0
+ "LVTTL (not 5V tolerant)", # 1
+ "HSTL 1.5V", # 2
+ "SSTL 3.3V", # 3
+ "SSTL 2.5V", # 4
+ "SSTL 1.8V", # 5
+ );
+
+ return ($_[0] < @levels) ? $levels[$_[0]] : "Undefined!";
+}
+
+# Common to SDR, DDR and DDR2 SDRAM
+sub sdram_module_configuration_type($)
+{
+ my $byte = $_[0] & 0x07;
+ my @edc;
+
+ return "No Parity" if $byte == 0;
+
+ # Data ECC includes Data Parity so don't print both
+ push @edc, "Data Parity" if ($byte & 0x03) == 0x01;
+ push @edc, "Data ECC" if ($byte & 0x02);
+ # New in DDR2 specification
+ push @edc, "Address/Command Parity" if ($byte & 0x04);
+
+ return join ", ", @edc;
+}
+
+# Parameter: EEPROM bytes 0-127 (using 3-62)
+sub decode_sdr_sdram($)
+{
+ my $bytes = shift;
+ my $temp;
+ my ($ctime, $ctime1, $ctime2, $ctime_min);
+
+# SPD revision
+ # Starting with SPD revision 1.2, this byte is encoded in BCD
+ printl("SPD Revision", $bytes->[62] < 0x12 ? $bytes->[62] :
+ ($bytes->[62] >> 4) . "." . ($bytes->[62] & 0xf));
+
+#size computation
+
+ prints("Memory Characteristics");
+
+ my $k = 0;
+ my $ii = 0;
+
+ $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
+ if (($bytes->[5] <= 8) && ($bytes->[17] <= 8)) {
+ $k = $bytes->[5] * $bytes->[17];
+ }
+
+ if ($ii > 0 && $ii <= 12 && $k > 0) {
+ printl("Size", ((1 << $ii) * $k) . " MB");
+ } else {
+ printl("Size", "INVALID: " . $bytes->[3] . "," . $bytes->[4] . "," .
+ $bytes->[5] . "," . $bytes->[17]);
+ }
+
+ my @cas;
+ for ($ii = 0; $ii < 7; $ii++) {
+ push(@cas, $ii + 1) if ($bytes->[18] & (1 << $ii));
+ }
+
+ my $trcd;
+ my $trp;
+ my $tras;
+ $ctime_min = $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
+
+ $trcd = $bytes->[29];
+ $trp = $bytes->[27];
+ $tras = $bytes->[30];
+
+ printl("tCL-tRCD-tRP-tRAS",
+ $cas[$#cas] . "-" .
+ ceil($trcd/$ctime) . "-" .
+ ceil($trp/$ctime) . "-" .
+ ceil($tras/$ctime));
+
+ if ($bytes->[3] == 0) { $temp = "Undefined!"; }
+ elsif ($bytes->[3] == 1) { $temp = "1/16"; }
+ elsif ($bytes->[3] == 2) { $temp = "2/17"; }
+ elsif ($bytes->[3] == 3) { $temp = "3/18"; }
+ else { $temp = $bytes->[3]; }
+ printl("Number of Row Address Bits", $temp);
+
+ if ($bytes->[4] == 0) { $temp = "Undefined!"; }
+ elsif ($bytes->[4] == 1) { $temp = "1/16"; }
+ elsif ($bytes->[4] == 2) { $temp = "2/17"; }
+ elsif ($bytes->[4] == 3) { $temp = "3/18"; }
+ else { $temp = $bytes->[4]; }
+ printl("Number of Col Address Bits", $temp);
+
+ printl("Number of Module Rows", value_or_undefined($bytes->[5]));
+
+ if ($bytes->[7] > 1) { $temp = "Undefined!"; }
+ else { $temp = ($bytes->[7] * 256) + $bytes->[6]; }
+ printl("Data Width", $temp);
+
+ printl("Voltage Interface Level",
+ sdram_voltage_interface_level($bytes->[8]));
+
+ printl("Module Configuration Type",
+ sdram_module_configuration_type($bytes->[11]));
+
+ printl("Refresh Rate", ddr2_refresh_rate($bytes->[12]));
+
+ if ($bytes->[13] & 0x80) { $temp = "Bank2 = 2 x Bank1"; }
+ else { $temp = "No Bank2 OR Bank2 = Bank1 width"; }
+ printl("Primary SDRAM Component Bank Config", $temp);
+ printl("Primary SDRAM Component Widths",
+ value_or_undefined($bytes->[13] & 0x7f));
+
+ if ($bytes->[14] & 0x80) { $temp = "Bank2 = 2 x Bank1"; }
+ else { $temp = "No Bank2 OR Bank2 = Bank1 width"; }
+ printl("Error Checking SDRAM Component Bank Config", $temp);
+ printl("Error Checking SDRAM Component Widths",
+ value_or_undefined($bytes->[14] & 0x7f));
+
+ printl("Min Clock Delay for Back to Back Random Access",
+ value_or_undefined($bytes->[15]));
+
+ my @array;
+ for ($ii = 0; $ii < 4; $ii++) {
+ push(@array, 1 << $ii) if ($bytes->[16] & (1 << $ii));
+ }
+ push(@array, "Page") if ($bytes->[16] & 128);
+ if (@array) { $temp = join ', ', @array; }
+ else { $temp = "None"; }
+ printl("Supported Burst Lengths", $temp);
+
+ printl("Number of Device Banks",
+ value_or_undefined($bytes->[17]));
+
+ printl("Supported CAS Latencies", cas_latencies(@cas));
+
+ @array = ();
+ for ($ii = 0; $ii < 7; $ii++) {
+ push(@array, $ii) if ($bytes->[19] & (1 << $ii));
+ }
+ if (@array) { $temp = join ', ', @array; }
+ else { $temp = "None"; }
+ printl("Supported CS Latencies", $temp);
+
+ @array = ();
+ for ($ii = 0; $ii < 7; $ii++) {
+ push(@array, $ii) if ($bytes->[20] & (1 << $ii));
+ }
+ if (@array) { $temp = join ', ', @array; }
+ else { $temp = "None"; }
+ printl("Supported WE Latencies", $temp);
+
+ my ($cycle_time, $access_time);
+
+ if (@cas >= 1) {
+ $cycle_time = "$ctime ns at CAS ".$cas[$#cas];
+
+ $temp = ($bytes->[10] >> 4) + ($bytes->[10] & 0xf) * 0.1;
+ $access_time = "$temp ns at CAS ".$cas[$#cas];
+ }
+
+ if (@cas >= 2 && spd_written(@$bytes[23..24])) {
+ $temp = $bytes->[23] >> 4;
+ if ($temp == 0) { $temp = "Undefined!"; }
+ else {
+ $temp += 15 if $temp < 4;
+ $temp += ($bytes->[23] & 0xf) * 0.1;
+ $ctime1 = $temp;
+ }
+ $cycle_time .= "\n$temp ns at CAS ".$cas[$#cas-1];
+
+ $temp = $bytes->[24] >> 4;
+ if ($temp == 0) { $temp = "Undefined!"; }
+ else {
+ $temp += 15 if $temp < 4;
+ $temp += ($bytes->[24] & 0xf) * 0.1;
+ }
+ $access_time .= "\n$temp ns at CAS ".$cas[$#cas-1];
+ }
+
+ if (@cas >= 3 && spd_written(@$bytes[25..26])) {
+ $temp = $bytes->[25] >> 2;
+ if ($temp == 0) { $temp = "Undefined!"; }
+ else {
+ $temp += ($bytes->[25] & 0x3) * 0.25;
+ $ctime2 = $temp;
+ }
+ $cycle_time .= "\n$temp ns at CAS ".$cas[$#cas-2];
+
+ $temp = $bytes->[26] >> 2;
+ if ($temp == 0) { $temp = "Undefined!"; }
+ else {
+ $temp += ($bytes->[26] & 0x3) * 0.25;
+ }
+ $access_time .= "\n$temp ns at CAS ".$cas[$#cas-2];
+ }
+
+ printl_cond(defined $cycle_time, "Cycle Time", $cycle_time);
+ printl_cond(defined $access_time, "Access Time", $access_time);
+
+ prints("Attributes");
+ $temp = "";
+ if ($bytes->[21] & 1) { $temp .= "Buffered Address/Control Inputs\n"; }
+ if ($bytes->[21] & 2) { $temp .= "Registered Address/Control Inputs\n"; }
+ if ($bytes->[21] & 4) { $temp .= "On card PLL (clock)\n"; }
+ if ($bytes->[21] & 8) { $temp .= "Buffered DQMB Inputs\n"; }
+ if ($bytes->[21] & 16) { $temp .= "Registered DQMB Inputs\n"; }
+ if ($bytes->[21] & 32) { $temp .= "Differential Clock Input\n"; }
+ if ($bytes->[21] & 64) { $temp .= "Redundant Row Address\n"; }
+ if ($bytes->[21] & 128) { $temp .= "Undefined (bit 7)\n"; }
+ printl_cond($bytes->[21], "SDRAM Module Attributes", $temp);
+
+# standard DDR speeds
+ prints("Timings at Standard Speeds");
+ foreach $ctime (7.5, 10, 15) {
+ my $best_cas;
+
+ # Find min CAS latency at this speed
+ if (defined $ctime2 && $ctime >= $ctime2) {
+ $best_cas = $cas[$#cas-2];
+ } elsif (defined $ctime1 && $ctime >= $ctime1) {
+ $best_cas = $cas[$#cas-1];
+ } else {
+ $best_cas = $cas[$#cas];
+ }
+
+ printl_cond($ctime >= $ctime_min,
+ "tCL-tRCD-tRP-tRAS as PC" . int(1000 / $ctime),
+ ddr_core_timings($best_cas, $ctime,
+ $trcd, $trp, $tras));
+ }
+
+ $temp = "";
+ if ($bytes->[22] & 1) { $temp .= "Supports Early RAS# Recharge\n"; }
+ if ($bytes->[22] & 2) { $temp .= "Supports Auto-Precharge\n"; }
+ if ($bytes->[22] & 4) { $temp .= "Supports Precharge All\n"; }
+ if ($bytes->[22] & 8) { $temp .= "Supports Write1/Read Burst\n"; }
+ if ($bytes->[22] & 16) { $temp .= "Lower VCC Tolerance: 5%\n"; }
+ else { $temp .= "Lower VCC Tolerance: 10%\n"; }
+ if ($bytes->[22] & 32) { $temp .= "Upper VCC Tolerance: 5%\n"; }
+ else { $temp .= "Upper VCC Tolerance: 10%\n"; }
+ if ($bytes->[22] & 64) { $temp .= "Undefined (bit 6)\n"; }
+ if ($bytes->[22] & 128) { $temp .= "Undefined (bit 7)\n"; }
+ printl("SDRAM Device Attributes (General)", $temp);
+
+ prints("Timing Parameters");
+ printl("Minimum Row Precharge Time",
+ value_or_undefined($bytes->[27], "ns"));
+
+ printl("Row Active to Row Active Min",
+ value_or_undefined($bytes->[28], "ns"));
+
+ printl("RAS to CAS Delay",
+ value_or_undefined($bytes->[29], "ns"));
+
+ printl("Min RAS Pulse Width",
+ value_or_undefined($bytes->[30], "ns"));
+
+ $temp = "";
+ if ($bytes->[31] & 1) { $temp .= "4 MByte\n"; }
+ if ($bytes->[31] & 2) { $temp .= "8 MByte\n"; }
+ if ($bytes->[31] & 4) { $temp .= "16 MByte\n"; }
+ if ($bytes->[31] & 8) { $temp .= "32 MByte\n"; }
+ if ($bytes->[31] & 16) { $temp .= "64 MByte\n"; }
+ if ($bytes->[31] & 32) { $temp .= "128 MByte\n"; }
+ if ($bytes->[31] & 64) { $temp .= "256 MByte\n"; }
+ if ($bytes->[31] & 128) { $temp .= "512 MByte\n"; }
+ if ($bytes->[31] == 0) { $temp .= "(Undefined! -- None Reported!)\n"; }
+ printl("Row Densities", $temp);
+
+ $temp = (($bytes->[32] & 0x7f) >> 4) + ($bytes->[32] & 0xf) * 0.1;
+ printl_cond(($bytes->[32] & 0xf) <= 9,
+ "Command and Address Signal Setup Time",
+ (($bytes->[32] >> 7) ? -$temp : $temp) . " ns");
+
+ $temp = (($bytes->[33] & 0x7f) >> 4) + ($bytes->[33] & 0xf) * 0.1;
+ printl_cond(($bytes->[33] & 0xf) <= 9,
+ "Command and Address Signal Hold Time",
+ (($bytes->[33] >> 7) ? -$temp : $temp) . " ns");
+
+ $temp = (($bytes->[34] & 0x7f) >> 4) + ($bytes->[34] & 0xf) * 0.1;
+ printl_cond(($bytes->[34] & 0xf) <= 9, "Data Signal Setup Time",
+ (($bytes->[34] >> 7) ? -$temp : $temp) . " ns");
+
+ $temp = (($bytes->[35] & 0x7f) >> 4) + ($bytes->[35] & 0xf) * 0.1;
+ printl_cond(($bytes->[35] & 0xf) <= 9, "Data Signal Hold Time",
+ (($bytes->[35] >> 7) ? -$temp : $temp) . " ns");
+}
+
+sub as_ddr($$)
+{
+ my ($gen, $ctime) = @_;
+
+ return " as DDR" . ($gen == 1 ? "" : $gen) . "-" .
+ int(2000 / $ctime);
+}
+
+sub ddr_core_timings($$$$$)
+{
+ my ($cas, $ctime, $trcd, $trp, $tras) = @_;
+
+ return $cas . "-" . ceil($trcd/$ctime) . "-" . ceil($trp/$ctime) .
+ "-" . ceil($tras/$ctime);
+}
+
+# Parameter: EEPROM bytes 0-127 (using 3-62)
+sub decode_ddr_sdram($)
+{
+ my $bytes = shift;
+ my $temp;
+ my ($ctime, $ctime1, $ctime2, $ctime_min, $ctime_max);
+
+# SPD revision
+ printl_cond($bytes->[62] != 0xff, "SPD Revision",
+ ($bytes->[62] >> 4) . "." . ($bytes->[62] & 0xf));
+
+# speed
+ prints("Memory Characteristics");
+
+ $ctime_min = $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
+ my $ddrclk = 2 * (1000 / $ctime);
+ my $tbits = ($bytes->[7] * 256) + $bytes->[6];
+ if (($bytes->[11] == 2) || ($bytes->[11] == 1)) { $tbits = $tbits - 8; }
+ my $pcclk = int ($ddrclk * $tbits / 8);
+ $pcclk += 100 if ($pcclk % 100) >= 50; # Round properly
+ $pcclk = $pcclk - ($pcclk % 100);
+ $ddrclk = int ($ddrclk);
+ printl("Maximum module speed", "$ddrclk MHz (PC${pcclk})");
+
+#size computation
+ my $k = 0;
+ my $ii = 0;
+
+ $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
+ if (($bytes->[5] <= 8) && ($bytes->[17] <= 8)) {
+ $k = $bytes->[5] * $bytes->[17];
+ }
+
+ if ($ii > 0 && $ii <= 12 && $k > 0) {
+ printl("Size", ((1 << $ii) * $k) . " MB");
+ } else {
+ printl("Size", "INVALID: " . $bytes->[3] . ", " . $bytes->[4] . ", " .
+ $bytes->[5] . ", " . $bytes->[17]);
+ }
+
+ printl("Banks x Rows x Columns x Bits",
+ join(' x ', $bytes->[17], $bytes->[3], $bytes->[4], $bytes->[6]));
+ printl("Ranks", $bytes->[5]);
+
+ printl("Voltage Interface Level",
+ sdram_voltage_interface_level($bytes->[8]));
+
+ printl("Module Configuration Type",
+ sdram_module_configuration_type($bytes->[11]));
+
+ printl("Refresh Rate", ddr2_refresh_rate($bytes->[12]));
+
+ my $highestCAS = 0;
+ my %cas;
+ for ($ii = 0; $ii < 7; $ii++) {
+ if ($bytes->[18] & (1 << $ii)) {
+ $highestCAS = 1+$ii*0.5;
+ $cas{$highestCAS}++;
+ }
+ }
+
+ my $trcd;
+ my $trp;
+ my $tras;
+
+ $trcd = ($bytes->[29] >> 2) + (($bytes->[29] & 3) * 0.25);
+ $trp = ($bytes->[27] >> 2) + (($bytes->[27] & 3) * 0.25);
+ $tras = $bytes->[30];
+
+# latencies
+ printl("Supported CAS Latencies", cas_latencies(keys %cas));
+
+ my @array;
+ for ($ii = 0; $ii < 7; $ii++) {
+ push(@array, $ii) if ($bytes->[19] & (1 << $ii));
+ }
+ if (@array) { $temp = join ', ', @array; }
+ else { $temp = "None"; }
+ printl("Supported CS Latencies", $temp);
+
+ @array = ();
+ for ($ii = 0; $ii < 7; $ii++) {
+ push(@array, $ii) if ($bytes->[20] & (1 << $ii));
+ }
+ if (@array) { $temp = join ', ', @array; }
+ else { $temp = "None"; }
+ printl("Supported WE Latencies", $temp);
+
+# timings
+ my ($cycle_time, $access_time, $core_timings);
+
+ if (exists $cas{$highestCAS}) {
+ $core_timings = ddr_core_timings($highestCAS, $ctime,
+ $trcd, $trp, $tras) . as_ddr(1, $ctime);
+
+ $cycle_time = "$ctime ns at CAS $highestCAS";
+ $access_time = (($bytes->[10] >> 4) * 0.1 + ($bytes->[10] & 0xf) * 0.01)
+ . " ns at CAS $highestCAS";
+ }
+
+ if (exists $cas{$highestCAS-0.5} && spd_written(@$bytes[23..24])) {
+ $ctime1 = ($bytes->[23] >> 4) + ($bytes->[23] & 0xf) * 0.1;
+ $core_timings .= "\n".ddr_core_timings($highestCAS-0.5, $ctime1,
+ $trcd, $trp, $tras) . as_ddr(1, $ctime1);
+
+ $cycle_time .= "\n$ctime1 ns at CAS ".($highestCAS-0.5);
+ $access_time .= "\n".(($bytes->[24] >> 4) * 0.1 + ($bytes->[24] & 0xf) * 0.01)
+ . " ns at CAS ".($highestCAS-0.5);
+ }
+
+ if (exists $cas{$highestCAS-1} && spd_written(@$bytes[25..26])) {
+ $ctime2 = ($bytes->[25] >> 4) + ($bytes->[25] & 0xf) * 0.1,
+ $core_timings .= "\n".ddr_core_timings($highestCAS-1, $ctime2,
+ $trcd, $trp, $tras) . as_ddr(1, $ctime2);
+
+ $cycle_time .= "\n$ctime2 ns at CAS ".($highestCAS-1);
+ $access_time .= "\n".(($bytes->[26] >> 4) * 0.1 + ($bytes->[26] & 0xf) * 0.01)
+ . " ns at CAS ".($highestCAS-1);
+ }
+
+ $ctime_max = $bytes->[43] == 0xff ? 0 : $bytes->[43]/4;
+
+ printl_cond(defined $core_timings, "tCL-tRCD-tRP-tRAS", $core_timings);
+ printl_cond(defined $cycle_time, "Minimum Cycle Time", $cycle_time);
+ printl_cond(defined $access_time, "Maximum Access Time", $access_time);
+ printl_cond($bytes->[43] & 0xfc,
+ "Maximum Cycle Time (tCK max)",
+ $bytes->[43] == 0xff ? "No minimum frequency" :
+ $bytes->[43] == 0 ? "" : # Wouldn't be displayed, prevent div by 0
+ tns1($ctime_max)." (DDR-".int(8000 / $bytes->[43]).")");
+
+# standard DDR speeds
+ prints("Timings at Standard Speeds");
+ foreach $ctime (5, 6, 7.5, 10) {
+ my $best_cas;
+
+ # Find min CAS latency at this speed
+ if (defined $ctime2 && $ctime >= $ctime2) {
+ $best_cas = $highestCAS-1;
+ } elsif (defined $ctime1 && $ctime >= $ctime1) {
+ $best_cas = $highestCAS-0.5;
+ } else {
+ $best_cas = $highestCAS;
+ }
+
+ printl_cond($ctime >= $ctime_min && ($ctime_max < 1 || $ctime <= $ctime_max),
+ "tCL-tRCD-tRP-tRAS" . as_ddr(1, $ctime),
+ ddr_core_timings($best_cas, $ctime,
+ $trcd, $trp, $tras));
+ }
+
+# more timing information
+ prints("Timing Parameters");
+ printl_cond($bytes->[32] != 0xff,
+ "Address/Command Setup Time Before Clock",
+ tns(ddr2_sdram_atime($bytes->[32])));
+ printl_cond($bytes->[33] != 0xff,
+ "Address/Command Hold Time After Clock",
+ tns(ddr2_sdram_atime($bytes->[33])));
+ printl_cond($bytes->[34] != 0xff,
+ "Data Input Setup Time Before Clock",
+ tns(ddr2_sdram_atime($bytes->[34])));
+ printl_cond($bytes->[35] != 0xff,
+ "Data Input Hold Time After Clock",
+ tns(ddr2_sdram_atime($bytes->[35])));
+ printl("Minimum Row Precharge Delay (tRP)", tns($trp));
+ printl_cond($bytes->[28] & 0xfc,
+ "Minimum Row Active to Row Active Delay (tRRD)",
+ tns($bytes->[28]/4));
+ printl("Minimum RAS# to CAS# Delay (tRCD)", tns($trcd));
+ printl("Minimum RAS# Pulse Width (tRAS)", tns($tras));
+ printl_cond($bytes->[41] && $bytes->[41] != 0xff,
+ "Minimum Active to Active/AR Time (tRC)",
+ tns($bytes->[41]));
+ printl_cond($bytes->[42],
+ "Minimum AR to Active/AR Command Period (tRFC)",
+ tns($bytes->[42]));
+ printl_cond($bytes->[44],
+ "Maximum DQS to DQ Skew (tDQSQ)",
+ tns($bytes->[44]/100));
+ printl_cond(($bytes->[45] & 0xf0) && $bytes->[45] != 0xff,
+ "Maximum Read Data Hold Skew (tQHS)",
+ tns(ddr2_sdram_atime($bytes->[45])));
+
+# module attributes
+ prints("Module Attributes");
+ if (($bytes->[47] & 0x03) == 0x01) { $temp = "1.125\" to 1.25\""; }
+ elsif (($bytes->[47] & 0x03) == 0x02) { $temp = "1.7\""; }
+ else { $temp = "Other"; }
+ printl_cond($bytes->[47] & 0x03, "Module Height", $temp);
+}
+
+sub ddr2_sdram_ctime($)
+{
+ my $byte = shift;
+ my $ctime;
+
+ $ctime = $byte >> 4;
+ if (($byte & 0xf) <= 9) { $ctime += ($byte & 0xf) * 0.1; }
+ elsif (($byte & 0xf) == 10) { $ctime += 0.25; }
+ elsif (($byte & 0xf) == 11) { $ctime += 0.33; }
+ elsif (($byte & 0xf) == 12) { $ctime += 0.66; }
+ elsif (($byte & 0xf) == 13) { $ctime += 0.75; }
+
+ return $ctime;
+}
+
+sub ddr2_sdram_atime($)
+{
+ my $byte = shift;
+ my $atime;
+
+ $atime = ($byte >> 4) * 0.1 + ($byte & 0xf) * 0.01;
+
+ return $atime;
+}
+
+# Base, high-bit, 3-bit fraction code
+sub ddr2_sdram_rtime($$$)
+{
+ my ($rtime, $msb, $ext) = @_;
+ my @table = (0, .25, .33, .50, .66, .75);
+
+ return $rtime + $msb * 256 + $table[$ext];
+}
+
+sub ddr2_module_types($)
+{
+ my $byte = shift;
+ my @types = qw(RDIMM UDIMM SO-DIMM Micro-DIMM Mini-RDIMM Mini-UDIMM);
+ my @widths = (133.35, 133.25, 67.6, 45.5, 82.0, 82.0);
+ my @suptypes;
+ local $_;
+
+ foreach (0..5) {
+ push @suptypes, "$types[$_] ($widths[$_] mm)"
+ if ($byte & (1 << $_));
+ }
+
+ return @suptypes;
+}
+
+# Common to SDR, DDR and DDR2 SDRAM
+sub ddr2_refresh_rate($)
+{
+ my $byte = shift;
+ my @refresh = qw(Normal Reduced Reduced Extended Extended Extended);
+ my @refresht = (15.625, 3.9, 7.8, 31.3, 62.5, 125);
+
+ return "$refresh[$byte & 0x7f] ($refresht[$byte & 0x7f] us)".
+ ($byte & 0x80 ? " - Self Refresh" : "");
+}
+
+# Parameter: EEPROM bytes 0-127 (using 3-62)
+sub decode_ddr2_sdram($)
+{
+ my $bytes = shift;
+ my $temp;
+ my ($ctime, $ctime1, $ctime2, $ctime_min, $ctime_max);
+
+# SPD revision
+ printl_cond($bytes->[62] != 0xff, "SPD Revision",
+ ($bytes->[62] >> 4) . "." . ($bytes->[62] & 0xf));
+
+# speed
+ prints("Memory Characteristics");
+
+ $ctime_min = $ctime = ddr2_sdram_ctime($bytes->[9]);
+ my $ddrclk = 2 * (1000 / $ctime);
+ my $tbits = ($bytes->[7] * 256) + $bytes->[6];
+ if ($bytes->[11] & 0x03) { $tbits = $tbits - 8; }
+ my $pcclk = int ($ddrclk * $tbits / 8);
+ # Round down to comply with Jedec
+ $pcclk = $pcclk - ($pcclk % 100);
+ $ddrclk = int ($ddrclk);
+ printl("Maximum module speed", "$ddrclk MHz (PC2-${pcclk})");
+
+#size computation
+ my $k = 0;
+ my $ii = 0;
+
+ $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
+ $k = (($bytes->[5] & 0x7) + 1) * $bytes->[17];
+
+ if($ii > 0 && $ii <= 12 && $k > 0) {
+ printl("Size", ((1 << $ii) * $k) . " MB");
+ } else {
+ printl("Size", "INVALID: " . $bytes->[3] . "," . $bytes->[4] . "," .
+ $bytes->[5] . "," . $bytes->[17]);
+ }
+
+ printl("Banks x Rows x Columns x Bits",
+ join(' x ', $bytes->[17], $bytes->[3], $bytes->[4], $bytes->[6]));
+ printl("Ranks", ($bytes->[5] & 7) + 1);
+
+ printl("SDRAM Device Width", $bytes->[13]." bits");
+
+ my @heights = ('< 25.4', '25.4', '25.4 - 30.0', '30.0', '30.5', '> 30.5');
+ printl("Module Height", $heights[$bytes->[5] >> 5]." mm");
+
+ my @suptypes = ddr2_module_types($bytes->[20]);
+ printl("Module Type".(@suptypes > 1 ? 's' : ''), join(', ', @suptypes));
+
+ printl("DRAM Package", $bytes->[5] & 0x10 ? "Stack" : "Planar");
+
+ printl("Voltage Interface Level",
+ sdram_voltage_interface_level($bytes->[8]));
+
+ printl("Module Configuration Type",
+ sdram_module_configuration_type($bytes->[11]));
+
+ printl("Refresh Rate", ddr2_refresh_rate($bytes->[12]));
+
+ my @burst;
+ push @burst, 4 if ($bytes->[16] & 4);
+ push @burst, 8 if ($bytes->[16] & 8);
+ $burst[0] = 'None' if !@burst;
+ printl("Supported Burst Lengths", join(', ', @burst));
+
+ my $highestCAS = 0;
+ my %cas;
+ for ($ii = 2; $ii < 7; $ii++) {
+ if ($bytes->[18] & (1 << $ii)) {
+ $highestCAS = $ii;
+ $cas{$highestCAS}++;
+ }
+ }
+
+ my $trcd;
+ my $trp;
+ my $tras;
+
+ $trcd = ($bytes->[29] >> 2) + (($bytes->[29] & 3) * 0.25);
+ $trp = ($bytes->[27] >> 2) + (($bytes->[27] & 3) * 0.25);
+ $tras = $bytes->[30];
+
+# latencies
+ printl("Supported CAS Latencies (tCL)", cas_latencies(keys %cas));
+
+# timings
+ my ($cycle_time, $access_time, $core_timings);
+
+ if (exists $cas{$highestCAS}) {
+ $core_timings = ddr_core_timings($highestCAS, $ctime,
+ $trcd, $trp, $tras) . as_ddr(2, $ctime);
+
+ $cycle_time = tns($ctime) . " at CAS $highestCAS (tCK min)";
+ $access_time = tns(ddr2_sdram_atime($bytes->[10]))
+ . " at CAS $highestCAS (tAC)";
+ }
+
+ if (exists $cas{$highestCAS-1} && spd_written(@$bytes[23..24])) {
+ $ctime1 = ddr2_sdram_ctime($bytes->[23]);
+ $core_timings .= "\n".ddr_core_timings($highestCAS-1, $ctime1,
+ $trcd, $trp, $tras) . as_ddr(2, $ctime1);
+
+ $cycle_time .= "\n".tns($ctime1)
+ . " at CAS ".($highestCAS-1);
+ $access_time .= "\n".tns(ddr2_sdram_atime($bytes->[24]))
+ . " at CAS ".($highestCAS-1);
+ }
+
+ if (exists $cas{$highestCAS-2} && spd_written(@$bytes[25..26])) {
+ $ctime2 = ddr2_sdram_ctime($bytes->[25]);
+ $core_timings .= "\n".ddr_core_timings($highestCAS-2, $ctime2,
+ $trcd, $trp, $tras) . as_ddr(2, $ctime2);
+
+ $cycle_time .= "\n".tns($ctime2)
+ . " at CAS ".($highestCAS-2);
+ $access_time .= "\n".tns(ddr2_sdram_atime($bytes->[26]))
+ . " at CAS ".($highestCAS-2);
+ }
+
+ $ctime_max = ddr2_sdram_ctime($bytes->[43]);
+
+ printl_cond(defined $core_timings, "tCL-tRCD-tRP-tRAS", $core_timings);
+ printl_cond(defined $cycle_time, "Minimum Cycle Time", $cycle_time);
+ printl_cond(defined $access_time, "Maximum Access Time", $access_time);
+ printl_cond(($bytes->[43] & 0xf0) && $bytes->[43] != 0xff,
+ "Maximum Cycle Time (tCK max)",
+ $ctime_max == 0 ? "" : # Wouldn't be displayed, prevent div by 0
+ tns($ctime_max)." (DDR2-".int(2000 / $ctime_max).")");
+
+# standard DDR2 speeds
+ prints("Timings at Standard Speeds");
+ foreach $ctime (1.875, 2.5, 3, 3.75, 5) {
+ my $best_cas;
+
+ # Find min CAS latency at this speed
+ if (defined $ctime2 && $ctime >= $ctime2) {
+ $best_cas = $highestCAS-2;
+ } elsif (defined $ctime1 && $ctime >= $ctime1) {
+ $best_cas = $highestCAS-1;
+ } else {
+ $best_cas = $highestCAS;
+ }
+
+ printl_cond($ctime >= $ctime_min && $ctime <= $ctime_max,
+ "tCL-tRCD-tRP-tRAS" . as_ddr(2,$ctime),
+ ddr_core_timings($best_cas, $ctime,
+ $trcd, $trp, $tras));
+ }
+
+# more timing information
+ prints("Timing Parameters");
+ # According to the JEDEC standard, the four timings below can't be less
+ # than 0.1 ns, however we've seen memory modules code such values so
+ # handle them properly.
+ printl_cond($bytes->[32] && $bytes->[32] != 0xff,
+ "Address/Command Setup Time Before Clock (tIS)",
+ tns(ddr2_sdram_atime($bytes->[32])));
+ printl_cond($bytes->[33] && $bytes->[33] != 0xff,
+ "Address/Command Hold Time After Clock (tIH)",
+ tns(ddr2_sdram_atime($bytes->[33])));
+ printl_cond($bytes->[34] && $bytes->[34] != 0xff,
+ "Data Input Setup Time Before Strobe (tDS)",
+ tns(ddr2_sdram_atime($bytes->[34])));
+ printl_cond($bytes->[35] && $bytes->[35] != 0xff,
+ "Data Input Hold Time After Strobe (tDH)",
+ tns(ddr2_sdram_atime($bytes->[35])));
+
+ printl("Minimum Row Precharge Delay (tRP)", tns($trp));
+ printl_cond($bytes->[28] & 0xfc,
+ "Minimum Row Active to Row Active Delay (tRRD)",
+ tns($bytes->[28]/4));
+ printl("Minimum RAS# to CAS# Delay (tRCD)", tns($trcd));
+ printl("Minimum RAS# Pulse Width (tRAS)", tns($tras));
+ printl_cond($bytes->[36] & 0xfc,
+ "Write Recovery Time (tWR)",
+ tns($bytes->[36]/4));
+ printl_cond($bytes->[37] & 0xfc,
+ "Minimum Write to Read CMD Delay (tWTR)",
+ tns($bytes->[37]/4));
+ printl_cond($bytes->[38] & 0xfc,
+ "Minimum Read to Pre-charge CMD Delay (tRTP)",
+ tns($bytes->[38]/4));
+
+ printl_cond($bytes->[41] && $bytes->[41] != 0xff,
+ "Minimum Active to Auto-refresh Delay (tRC)",
+ tns(ddr2_sdram_rtime($bytes->[41], 0,
+ ($bytes->[40] >> 4) & 7)));
+ printl_cond($bytes->[42],
+ "Minimum Recovery Delay (tRFC)",
+ tns(ddr2_sdram_rtime($bytes->[42], $bytes->[40] & 1,
+ ($bytes->[40] >> 1) & 7)));
+
+ printl_cond($bytes->[44], "Maximum DQS to DQ Skew (tDQSQ)",
+ tns($bytes->[44]/100));
+ printl_cond($bytes->[45], "Maximum Read Data Hold Skew (tQHS)",
+ tns($bytes->[45]/100));
+ printl_cond($bytes->[46], "PLL Relock Time", $bytes->[46] . " us");
+}
+
+# Return combined time in ns
+sub ddr3_mtb_ftb($$$$)
+{
+ my ($byte1, $byte2, $mtb, $ftb) = @_;
+
+ # byte1 is unsigned in ns, but byte2 is signed in ps
+ $byte2 -= 0x100 if $byte2 & 0x80;
+
+ return $byte1 * $mtb + $byte2 * $ftb / 1000;
+}
+
+sub ddr3_reference_card($$)
+{
+ my ($rrc, $ext) = @_;
+ my $alphabet = "ABCDEFGHJKLMNPRTUVWY";
+ my $ref = $rrc & 0x1f;
+ my $revision = $ext >> 5;
+ my $ref_card;
+
+ return "ZZ" if $ref == 0x1f;
+ $ref += 0x1f if $rrc & 0x80;
+ $revision = (($rrc >> 5) & 0x03) if $revision == 0;
+
+ if ($ref < length($alphabet)) {
+ # One letter reference card
+ $ref_card = substr($alphabet, $ref, 1);
+ } else {
+ # Two letter reference card
+ my $ref1 = int($ref / (length($alphabet)));
+ $ref -= length($alphabet) * $ref1;
+ $ref_card = substr($alphabet, $ref1, 1) .
+ substr($alphabet, $ref, 1);
+ }
+
+ return "$ref_card revision $revision";
+}
+
+sub ddr3_revision_number($)
+{
+ my $h = $_[0] >> 4;
+ my $l = $_[0] & 0x0f;
+
+ # Decode as suggested by JEDEC Standard 21-C
+ return sprintf("%d", $l) if $h == 0;
+ return sprintf("%d.%d", $h, $l) if $h < 0xa;
+ return sprintf("%c%d", ord('A') + $h - 0xa, $l);
+}
+
+sub ddr3_device_type($)
+{
+ my $byte = shift;
+ my $type = $byte & 0x80 ? "Non-Standard" : "Standard Monolithic";
+ my $die_count = ($byte >> 4) & 0x07;
+ my $loading = ($byte >> 2) & 0x03;
+
+ if ($die_count == 1) {
+ $type .= "\nSingle die";
+ } elsif ($die_count == 2) {
+ $type .= "\n2 die";
+ } elsif ($die_count == 3) {
+ $type .= "\n4 die";
+ } elsif ($die_count == 4) {
+ $type .= "\n8 die";
+ }
+
+ if ($loading == 1) {
+ $type .= "\nMulti load stack";
+ } elsif ($loading == 2) {
+ $type .= "\nSingle load stack";
+ }
+
+ return $type;
+}
+
+use constant DDR3_UNBUFFERED => 1;
+use constant DDR3_REGISTERED => 2;
+use constant DDR3_CLOCKED => 3;
+use constant DDR3_LOAD_REDUCED => 4;
+
+# Parameter: EEPROM bytes 0-127 (using 3-76)
+sub decode_ddr3_sdram($)
+{
+ my $bytes = shift;
+ my $temp;
+ my $ctime;
+ my ($ftb, $mtb);
+ my $ii;
+
+ my @module_types = (
+ { type => "Undefined", width => "Unknown" },
+ { type => "RDIMM", width => "133.35 mm", family => DDR3_REGISTERED },
+ { type => "UDIMM", width => "133.35 mm", family => DDR3_UNBUFFERED },
+ { type => "SO-DIMM", width => "67.6 mm", family => DDR3_UNBUFFERED },
+ { type => "Micro-DIMM", width => "TBD", family => DDR3_UNBUFFERED },
+ { type => "Mini-RDIMM", width => "82.0 mm", family => DDR3_REGISTERED },
+ { type => "Mini-UDIMM", width => "82.0 mm", family => DDR3_UNBUFFERED },
+ { type => "Mini-CDIMM", width => "67.6 mm", family => DDR3_CLOCKED },
+ { type => "72b-SO-UDIMM", width => "67.6 mm", family => DDR3_UNBUFFERED },
+ { type => "72b-SO-RDIMM", width => "67.6 mm", family => DDR3_REGISTERED },
+ { type => "72b-SO-CDIMM", width => "67.6 mm", family => DDR3_CLOCKED },
+ { type => "LRDIMM", width => "133.35 mm", family => DDR3_LOAD_REDUCED },
+ { type => "16b-SO-DIMM", width => "67.6 mm", family => DDR3_UNBUFFERED },
+ { type => "32b-SO-DIMM", width => "67.6 mm", family => DDR3_UNBUFFERED },
+ );
+
+ printl("Module Type", ($bytes->[3] <= $#module_types) ?
+ $module_types[$bytes->[3]]->{type} :
+ sprintf("Reserved (0x%.2X)", $bytes->[3]));
+
+# time bases
+ if (($bytes->[9] & 0x0f) == 0 || $bytes->[11] == 0) {
+ print STDERR "Invalid time base divisor, can't decode\n";
+ return;
+ }
+ $ftb = ($bytes->[9] >> 4) / ($bytes->[9] & 0x0f);
+ $mtb = $bytes->[10] / $bytes->[11];
+
+# speed
+ prints("Memory Characteristics");
+
+ $ctime = ddr3_mtb_ftb($bytes->[12], $bytes->[34], $mtb, $ftb);
+ # Starting with DDR3-1866, vendors may start approximating the
+ # minimum cycle time. Try to guess what they really meant so
+ # that the reported speed matches the standard.
+ for ($ii = 7; $ii < 15; $ii++) {
+ if ($ctime > 7.5/$ii - $ftb/1000 && $ctime < 7.5/$ii + $ftb/1000) {
+ $ctime = 7.5/$ii;
+ last;
+ }
+ }
+
+ my $ddrclk = 2 * (1000 / $ctime);
+ my $tbits = 1 << (($bytes->[8] & 7) + 3);
+ my $pcclk = int ($ddrclk * $tbits / 8);
+ # Round down to comply with Jedec
+ $pcclk = $pcclk - ($pcclk % 100);
+ $ddrclk = int ($ddrclk);
+ printl("Maximum module speed", "$ddrclk MHz (PC3-${pcclk})");
+
+# Size computation
+
+ my $cap = ($bytes->[4] & 15) + 28;
+ $cap += ($bytes->[8] & 7) + 3;
+ $cap -= ($bytes->[7] & 7) + 2;
+ $cap -= 20 + 3;
+ my $k = (($bytes->[7] >> 3) & 31) + 1;
+ printl("Size", ((1 << $cap) * $k) . " MB");
+
+ printl("Banks x Rows x Columns x Bits",
+ join(' x ', 1 << ((($bytes->[4] >> 4) & 7) + 3),
+ ((($bytes->[5] >> 3) & 31) + 12),
+ ( ($bytes->[5] & 7) + 9),
+ ( 1 << (($bytes->[8] & 7) + 3)) ));
+ printl("Ranks", $k);
+
+ printl("SDRAM Device Width", (1 << (($bytes->[7] & 7) + 2))." bits");
+
+ printl("Bus Width Extension", ($bytes->[8] & 24)." bits");
+
+ my $taa;
+ my $trcd;
+ my $trp;
+ my $tras;
+
+ $taa = ddr3_mtb_ftb($bytes->[16], $bytes->[35], $mtb, $ftb);
+ $trcd = ddr3_mtb_ftb($bytes->[18], $bytes->[36], $mtb, $ftb);
+ $trp = ddr3_mtb_ftb($bytes->[20], $bytes->[37], $mtb, $ftb);
+ $tras = ((($bytes->[21] & 0x0f) << 8) + $bytes->[22]) * $mtb;
+
+ printl("tCL-tRCD-tRP-tRAS", ddr_core_timings(ceil($taa / $ctime), $ctime, $trcd, $trp, $tras));
+
+# latencies
+ my $highestCAS = 0;
+ my %cas;
+ my $cas_sup = ($bytes->[15] << 8) + $bytes->[14];
+ for ($ii = 0; $ii < 15; $ii++) {
+ if ($cas_sup & (1 << $ii)) {
+ $highestCAS = $ii + 4;
+ $cas{$highestCAS}++;
+ }
+ }
+ printl("Supported CAS Latencies (tCL)", cas_latencies(keys %cas));
+
+# standard DDR3 speeds
+ prints("Timings at Standard Speeds");
+ foreach my $ctime_at_speed (7.5/8, 7.5/7, 1.25, 1.5, 1.875, 2.5) {
+ my $best_cas = 0;
+
+ # Find min CAS latency at this speed
+ for ($ii = 14; $ii >= 0; $ii--) {
+ next unless ($cas_sup & (1 << $ii));
+ if (ceil($taa / $ctime_at_speed) <= $ii + 4) {
+ $best_cas = $ii + 4;
+ }
+ }
+
+ printl_cond($best_cas && $ctime_at_speed >= $ctime,
+ "tCL-tRCD-tRP-tRAS" . as_ddr(3, $ctime_at_speed),
+ ddr_core_timings($best_cas, $ctime_at_speed,
+ $trcd, $trp, $tras));
+ }
+
+# more timing information
+ prints("Timing Parameters");
+
+ printl("Minimum Cycle Time (tCK)", tns3($ctime));
+ printl("Minimum CAS Latency Time (tAA)", tns3($taa));
+ printl("Minimum Write Recovery time (tWR)", tns3($bytes->[17] * $mtb));
+ printl("Minimum RAS# to CAS# Delay (tRCD)", tns3($trcd));
+ printl("Minimum Row Active to Row Active Delay (tRRD)",
+ tns3($bytes->[19] * $mtb));
+ printl("Minimum Row Precharge Delay (tRP)", tns3($trp));
+ printl("Minimum Active to Precharge Delay (tRAS)", tns3($tras));
+ printl("Minimum Active to Auto-Refresh Delay (tRC)",
+ tns3(ddr3_mtb_ftb((($bytes->[21] & 0xf0) << 4) + $bytes->[23], $bytes->[38], $mtb, $ftb)));
+ printl("Minimum Recovery Delay (tRFC)",
+ tns3((($bytes->[25] << 8) + $bytes->[24]) * $mtb));
+ printl("Minimum Write to Read CMD Delay (tWTR)",
+ tns3($bytes->[26] * $mtb));
+ printl("Minimum Read to Pre-charge CMD Delay (tRTP)",
+ tns3($bytes->[27] * $mtb));
+ printl("Minimum Four Activate Window Delay (tFAW)",
+ tns3(((($bytes->[28] & 15) << 8) + $bytes->[29]) * $mtb));
+
+# miscellaneous stuff
+ prints("Optional Features");
+
+ my $volts = "1.5V";
+ if ($bytes->[6] & 1) {
+ $volts .= " tolerant";
+ }
+ if ($bytes->[6] & 2) {
+ $volts .= ", 1.35V ";
+ }
+ if ($bytes->[6] & 4) {
+ $volts .= ", 1.2X V";
+ }
+ printl("Operable voltages", $volts);
+ printl("RZQ/6 supported?", ($bytes->[30] & 1) ? "Yes" : "No");
+ printl("RZQ/7 supported?", ($bytes->[30] & 2) ? "Yes" : "No");
+ printl("DLL-Off Mode supported?", ($bytes->[30] & 128) ? "Yes" : "No");
+ printl("Operating temperature range", sprintf "0-%d degrees C",
+ ($bytes->[31] & 1) ? 95 : 85);
+ printl_cond($bytes->[31] & 1,
+ "Refresh Rate in extended temp range",
+ ($bytes->[31] & 2) ? "1X" : "2X");
+ printl("Auto Self-Refresh?", ($bytes->[31] & 4) ? "Yes" : "No");
+ printl("On-Die Thermal Sensor readout?",
+ ($bytes->[31] & 8) ? "Yes" : "No");
+ printl("Partial Array Self-Refresh?",
+ ($bytes->[31] & 128) ? "Yes" : "No");
+ printl("Module Thermal Sensor",
+ ($bytes->[32] & 128) ? "Yes" : "No");
+ printl("SDRAM Device Type", ddr3_device_type($bytes->[33]));
+
+ # Following bytes are type-specific, so don't continue if type
+ # isn't known.
+ return if $bytes->[3] == 0 || $bytes->[3] > $#module_types;
+
+ if ($module_types[$bytes->[3]]->{family} == DDR3_UNBUFFERED ||
+ $module_types[$bytes->[3]]->{family} == DDR3_REGISTERED ||
+ $module_types[$bytes->[3]]->{family} == DDR3_CLOCKED ||
+ $module_types[$bytes->[3]]->{family} == DDR3_LOAD_REDUCED) {
+ prints("Physical Characteristics");
+ printl("Module Height", (($bytes->[60] & 31) + 15) . " mm");
+ printl("Module Thickness", sprintf("%d mm front, %d mm back",
+ ($bytes->[61] & 15) + 1,
+ (($bytes->[61] >> 4) & 15) +1));
+ printl("Module Width", $module_types[$bytes->[3]]->{width});
+ printl("Module Reference Card", ddr3_reference_card($bytes->[62], $bytes->[60]));
+
+ printl_cond($module_types[$bytes->[3]]->{family} == DDR3_UNBUFFERED,
+ "Rank 1 Mapping", $bytes->[63] & 0x01 ? "Mirrored" : "Standard");
+ }
+
+ if ($module_types[$bytes->[3]]->{family} == DDR3_REGISTERED) {
+ prints("Registered DIMM");
+
+ my @rows = ("Undefined", 1, 2, 4);
+ printl("# DRAM Rows", $rows[($bytes->[63] >> 2) & 3]);
+ printl("# Registers", $rows[$bytes->[63] & 3]);
+ printl("Register manufacturer",
+ manufacturer_ddr3($bytes->[65], $bytes->[66]));
+ printl("Register device type",
+ (($bytes->[68] & 7) == 0) ? "SSTE32882" :
+ "Undefined");
+ printl_cond($bytes->[67] != 0xff,
+ "Register revision", ddr3_revision_number($bytes->[67]));
+ printl("Heat spreader", $bytes->[64] & 0x80 ? "Yes" : "No");
+ }
+
+ if ($module_types[$bytes->[3]]->{family} == DDR3_LOAD_REDUCED) {
+ prints("Load Reduced DIMM");
+
+ my @rows = ("Undefined", 1, 2, "Reserved");
+ printl("# DRAM Rows", $rows[($bytes->[63] >> 2) & 3]);
+ my @mirroring = ("None", "Odd ranks", "Reserved", "Reserved");
+ printl("Mirroring", $mirroring[$bytes->[63] & 3]);
+ printl("Rank Numbering", $bytes->[63] & 0x20 ? "Even only" : "Contiguous");
+ printl("Buffer Orientation", $bytes->[63] & 0x10 ? "Horizontal" : "Vertical");
+ printl("Register manufacturer",
+ manufacturer_ddr3($bytes->[65], $bytes->[66]));
+ printl_cond($bytes->[64] != 0xff,
+ "Buffer Revision", ddr3_revision_number($bytes->[64]));
+ printl("Heat spreader", $bytes->[63] & 0x80 ? "Yes" : "No");
+ }
+
+}
+
+# Parameter: EEPROM bytes 0-127 (using 4-5)
+sub decode_direct_rambus($)
+{
+ my $bytes = shift;
+
+#size computation
+ prints("Memory Characteristics");
+
+ my $ii;
+
+ $ii = ($bytes->[4] & 0x0f) + ($bytes->[4] >> 4) + ($bytes->[5] & 0x07) - 13;
+
+ if ($ii > 0 && $ii < 16) {
+ printl("Size", (1 << $ii) . " MB");
+ } else {
+ printl("Size", sprintf("INVALID: 0x%02x, 0x%02x",
+ $bytes->[4], $bytes->[5]));
+ }
+}
+
+# Parameter: EEPROM bytes 0-127 (using 3-5)
+sub decode_rambus($)
+{
+ my $bytes = shift;
+
+#size computation
+ prints("Memory Characteristics");
+
+ my $ii;
+
+ $ii = ($bytes->[3] & 0x0f) + ($bytes->[3] >> 4) + ($bytes->[5] & 0x07) - 13;
+
+ if ($ii > 0 && $ii < 16) {
+ printl("Size", (1 << $ii) . " MB");
+ } else {
+ printl("Size", "INVALID: " . sprintf("0x%02x, 0x%02x",
+ $bytes->[3], $bytes->[5]));
+ }
+}
+
+%decode_callback = (
+ "SDR SDRAM" => \&decode_sdr_sdram,
+ "DDR SDRAM" => \&decode_ddr_sdram,
+ "DDR2 SDRAM" => \&decode_ddr2_sdram,
+ "DDR3 SDRAM" => \&decode_ddr3_sdram,
+ "Direct Rambus" => \&decode_direct_rambus,
+ "Rambus" => \&decode_rambus,
+);
+
+# Parameter: Manufacturing year/week bytes
+sub manufacture_date($$)
+{
+ my ($year, $week) = @_;
+
+ # In theory the year and week are in BCD format, but
+ # this is not always true in practice :(
+ if (($year & 0xf0) <= 0x90 && ($year & 0x0f) <= 0x09
+ && ($week & 0xf0) <= 0x90 && ($week & 0x0f) <= 0x09) {
+ # Note that this heuristic will break in year 2080
+ return sprintf("%d%02X-W%02X",
+ $year >= 0x80 ? 19 : 20, $year, $week);
+ # Fallback to binary format if it seems to make sense
+ } elsif ($year <= 99 && $week >= 1 && $week <= 53) {
+ return sprintf("%d%02d-W%02d",
+ $year >= 80 ? 19 : 20, $year, $week);
+ } else {
+ return sprintf("0x%02X%02X", $year, $week);
+ }
+}
+
+sub printl_mfg_location_code($)
+{
+ my $code = shift;
+ my $letter = chr($code);
+
+ # Try the location code as ASCII first, as earlier specifications
+ # suggested this. As newer specifications don't mention it anymore,
+ # we still fall back to binary.
+ printl_cond(spd_written($code), "Manufacturing Location Code",
+ $letter =~ m/^[\w\d]$/ ? $letter : sprintf("0x%.2X", $code));
+}
+
+sub printl_mfg_assembly_serial(@)
+{
+ printl_cond(spd_written(@_), "Assembly Serial Number",
+ sprintf("0x%02X%02X%02X%02X", @_));
+}
+
+# Parameter: EEPROM bytes 0-175 (using 117-149)
+sub decode_ddr3_mfg_data($)
+{
+ my $bytes = shift;
+
+ prints("Manufacturer Data");
+
+ printl("Module Manufacturer",
+ manufacturer_ddr3($bytes->[117], $bytes->[118]));
+
+ printl_cond(spd_written(@{$bytes}[148..149]),
+ "DRAM Manufacturer",
+ manufacturer_ddr3($bytes->[148], $bytes->[149]));
+
+ printl_mfg_location_code($bytes->[119]);
+
+ printl_cond(spd_written(@{$bytes}[120..121]),
+ "Manufacturing Date",
+ manufacture_date($bytes->[120], $bytes->[121]));
+
+ printl_mfg_assembly_serial(@{$bytes}[122..125]);
+
+ printl("Part Number", part_number(@{$bytes}[128..145]));
+
+ printl_cond(spd_written(@{$bytes}[146..147]),
+ "Revision Code",
+ sprintf("0x%02X%02X", $bytes->[146], $bytes->[147]));
+}
+
+# Parameter: EEPROM bytes 0-127 (using 64-98)
+sub decode_manufacturing_information($)
+{
+ my $bytes = shift;
+ my ($temp, $extra);
+
+ prints("Manufacturing Information");
+
+ # $extra is a reference to an array containing up to
+ # 7 extra bytes from the Manufacturer field. Sometimes
+ # these bytes are filled with interesting data.
+ ($temp, $extra) = manufacturer(@{$bytes}[64..71]);
+ printl("Manufacturer", $temp);
+ $temp = manufacturer_data(@{$extra});
+ printl_cond(defined $temp, "Custom Manufacturer Data", $temp);
+
+ printl_mfg_location_code($bytes->[72]);
+
+ printl("Part Number", part_number(@{$bytes}[73..90]));
+
+ printl_cond(spd_written(@{$bytes}[91..92]), "Revision Code",
+ sprintf("0x%02X%02X", @{$bytes}[91..92]));
+
+ printl_cond(spd_written(@{$bytes}[93..94]), "Manufacturing Date",
+ manufacture_date($bytes->[93], $bytes->[94]));
+
+ printl_mfg_assembly_serial(@{$bytes}[95..98]);
+}
+
+# Parameter: EEPROM bytes 0-127 (using 126-127)
+sub decode_intel_spec_freq($)
+{
+ my $bytes = shift;
+ my $temp;
+
+ prints("Intel Specification");
+
+ if ($bytes->[126] == 0x66) { $temp = "66 MHz"; }
+ elsif ($bytes->[126] == 100) { $temp = "100 MHz or 133 MHz"; }
+ elsif ($bytes->[126] == 133) { $temp = "133 MHz"; }
+ else { $temp = "Undefined!"; }
+ printl("Frequency", $temp);
+
+ $temp = "";
+ if ($bytes->[127] & 1) { $temp .= "Intel Concurrent Auto-precharge\n"; }
+ if ($bytes->[127] & 2) { $temp .= "CAS Latency = 2\n"; }
+ if ($bytes->[127] & 4) { $temp .= "CAS Latency = 3\n"; }
+ if ($bytes->[127] & 8) { $temp .= "Junction Temp A (100 degrees C)\n"; }
+ else { $temp .= "Junction Temp B (90 degrees C)\n"; }
+ if ($bytes->[127] & 16) { $temp .= "CLK 3 Connected\n"; }
+ if ($bytes->[127] & 32) { $temp .= "CLK 2 Connected\n"; }
+ if ($bytes->[127] & 64) { $temp .= "CLK 1 Connected\n"; }
+ if ($bytes->[127] & 128) { $temp .= "CLK 0 Connected\n"; }
+ if (($bytes->[127] & 192) == 192) { $temp .= "Double-sided DIMM\n"; }
+ elsif (($bytes->[127] & 192) != 0) { $temp .= "Single-sided DIMM\n"; }
+ printl("Details for 100 MHz Support", $temp);
+}
+
+# Read various hex dump style formats: hexdump, hexdump -C, i2cdump, eeprog
+# note that normal 'hexdump' format on a little-endian system byte-swaps
+# words, using hexdump -C is better.
+sub read_hexdump($)
+{
+ my $addr = 0;
+ my $repstart = 0;
+ my @bytes;
+ my $header = 1;
+ my $word = 0;
+
+ # Look in the cache first
+ return @{$hexdump_cache{$_[0]}} if exists $hexdump_cache{$_[0]};
+
+ open F, '<', $_[0] or die "Unable to open: $_[0]";
+ while (<F>) {
+ chomp;
+ if (/^\*$/) {
+ $repstart = $addr;
+ next;
+ }
+ /^(?:0000 )?([a-f\d]{2,8}):?\s+((:?[a-f\d]{4}\s*){8}|(:?[a-f\d]{2}\s*){16})/i ||
+ /^(?:0000 )?([a-f\d]{2,8}):?\s*$/i;
+ next if (!defined $1 && $header); # skip leading unparsed lines
+
+ defined $1 or die "Unable to parse input";
+ $header = 0;
+
+ $addr = hex $1;
+ if ($repstart) {
+ @bytes[$repstart .. ($addr-1)] =
+ (@bytes[($repstart-16)..($repstart-1)]) x (($addr-$repstart)/16);
+ $repstart = 0;
+ }
+ last unless defined $2;
+ foreach (split(/\s+/, $2)) {
+ if (/^(..)(..)$/) {
+ $word |= 1;
+ if ($use_hexdump eq LITTLEENDIAN) {
+ $bytes[$addr++] = hex($2);
+ $bytes[$addr++] = hex($1);
+ } else {
+ $bytes[$addr++] = hex($1);
+ $bytes[$addr++] = hex($2);
+ }
+ } else {
+ $bytes[$addr++] = hex($_);
+ }
+ }
+ }
+ close F;
+ $header and die "Unable to parse any data from hexdump '$_[0]'";
+ $word and printc("Using $use_hexdump 16-bit hex dump");
+
+ # Cache the data for later use
+ $hexdump_cache{$_[0]} = \@bytes;
+ return @bytes;
+}
+
+# Returns the (total, used) number of bytes in the EEPROM,
+# assuming it is a non-Rambus SPD EEPROM.
+sub spd_sizes($)
+{
+ my $bytes = shift;
+
+ if ($bytes->[2] >= 9) {
+ # For FB-DIMM and newer, decode number of bytes written
+ my $spd_len = ($bytes->[0] >> 4) & 7;
+ my $size = 64 << ($bytes->[0] & 15);
+ if ($spd_len == 0) {
+ return ($size, 128);
+ } elsif ($spd_len == 1) {
+ return ($size, 176);
+ } elsif ($spd_len == 2) {
+ return ($size, 256);
+ } else {
+ return (64, 64);
+ }
+ } else {
+ my $size;
+ if ($bytes->[1] <= 14) {
+ $size = 1 << $bytes->[1];
+ } elsif ($bytes->[1] == 0) {
+ $size = "RFU";
+ } else { $size = "ERROR!" }
+
+ return ($size, ($bytes->[0] < 64) ? 64 : $bytes->[0]);
+ }
+}
+
+# Read bytes from SPD-EEPROM
+# Note: offset must be a multiple of 16!
+sub readspd($$$)
+{
+ my ($offset, $size, $dimm_i) = @_;
+ my @bytes;
+ if ($use_hexdump) {
+ @bytes = read_hexdump($dimm_i);
+ return @bytes[$offset..($offset + $size - 1)];
+ } elsif ($use_sysfs) {
+ # Kernel 2.6 with sysfs
+ sysopen(HANDLE, "$dimm_i/eeprom", O_RDONLY)
+ or die "Cannot open $dimm_i/eeprom";
+ binmode HANDLE;
+ sysseek(HANDLE, $offset, SEEK_SET)
+ or die "Cannot seek $dimm_i/eeprom";
+ sysread(HANDLE, my $eeprom, $size)
+ or die "Cannot read $dimm_i/eeprom";
+ close HANDLE;
+ @bytes = unpack("C*", $eeprom);
+ } else {
+ # Kernel 2.4 with procfs
+ for my $i (0 .. ($size-1)/16) {
+ my $hexoff = sprintf('%02x', $offset + $i * 16);
+ push @bytes, split(" ", `cat $dimm_i/$hexoff`);
+ }
+ }
+ return @bytes;
+}
+
+# Calculate and verify checksum of first 63 bytes
+sub checksum($)
+{
+ my $bytes = shift;
+ my $dimm_checksum = 0;
+ local $_;
+
+ $dimm_checksum += $bytes->[$_] foreach (0 .. 62);
+ $dimm_checksum &= 0xff;
+
+ return ("EEPROM Checksum of bytes 0-62",
+ ($bytes->[63] == $dimm_checksum) ? 1 : 0,
+ sprintf('0x%02X', $bytes->[63]),
+ sprintf('0x%02X', $dimm_checksum));
+}
+
+# Calculate and verify CRC
+sub check_crc($)
+{
+ my $bytes = shift;
+ my $crc = 0;
+ my $crc_cover = $bytes->[0] & 0x80 ? 116 : 125;
+ my $crc_ptr = 0;
+ my $crc_bit;
+
+ while ($crc_ptr <= $crc_cover) {
+ $crc = $crc ^ ($bytes->[$crc_ptr] << 8);
+ for ($crc_bit = 0; $crc_bit < 8; $crc_bit++) {
+ if ($crc & 0x8000) {
+ $crc = ($crc << 1) ^ 0x1021;
+ } else {
+ $crc = $crc << 1
+ }
+ }
+ $crc_ptr++;
+ }
+ $crc &= 0xffff;
+
+ my $dimm_crc = ($bytes->[127] << 8) | $bytes->[126];
+ return ("EEPROM CRC of bytes 0-$crc_cover",
+ ($dimm_crc == $crc) ? 1 : 0,
+ sprintf("0x%04X", $dimm_crc),
+ sprintf("0x%04X", $crc));
+}
+
+# Parse command-line
+foreach (@ARGV) {
+ if ($_ eq '-h' || $_ eq '--help') {
+ print "Usage: $0 [-c] [-f [-b]] [-x|-X file [files..]]\n",
+ " $0 -h\n\n",
+ " -f, --format Print nice html output\n",
+ " -b, --bodyonly Don't print html header\n",
+ " (useful for postprocessing the output)\n",
+ " --side-by-side Display all DIMMs side-by-side if possible\n",
+ " --merge-cells Merge neighbour cells with identical values\n",
+ " (side-by-side output only, default)\n",
+ " --no-merge-cells Don't merge neighbour cells with identical values\n",
+ " (side-by-side output only)\n",
+ " -c, --checksum Decode completely even if checksum fails\n",
+ " -x, Read data from hexdump files\n",
+ " -X, Same as -x except treat multibyte hex\n",
+ " data as little endian\n",
+ " -h, --help Display this usage summary\n";
+ print <<"EOF";
+
+Hexdumps can be the output from hexdump, hexdump -C, i2cdump, eeprog and
+likely many other progams producing hex dumps of one kind or another. Note
+that the default output of "hexdump" will be byte-swapped on little-endian
+systems and you must use -X instead of -x, otherwise the dump will not be
+parsed correctly. It is better to use "hexdump -C", which is not ambiguous.
+EOF
+ exit;
+ }
+
+ if ($_ eq '-f' || $_ eq '--format') {
+ $opt_html = 1;
+ next;
+ }
+ if ($_ eq '-b' || $_ eq '--bodyonly') {
+ $opt_bodyonly = 1;
+ next;
+ }
+ if ($_ eq '--side-by-side') {
+ $opt_side_by_side = 1;
+ next;
+ }
+ if ($_ eq '--merge-cells') {
+ $opt_merge = 1;
+ next;
+ }
+ if ($_ eq '--no-merge-cells') {
+ $opt_merge = 0;
+ next;
+ }
+ if ($_ eq '-c' || $_ eq '--checksum') {
+ $opt_igncheck = 1;
+ next;
+ }
+ if ($_ eq '-x') {
+ $use_hexdump = BIGENDIAN;
+ next;
+ }
+ if ($_ eq '-X') {
+ $use_hexdump = LITTLEENDIAN;
+ next;
+ }
+
+ if (m/^-/) {
+ print STDERR "Unrecognized option $_\n";
+ exit;
+ }
+
+ push @dimm, { eeprom => basename($_), file => $_ } if $use_hexdump;
+}
+
+# Default values
+$opt_merge = 1 unless defined $opt_merge;
+
+# From a sysfs device path and an attribute name, return the attribute
+# value, or undef (stolen from sensors-detect)
+sub sysfs_device_attribute
+{
+ my ($device, $attr) = @_;
+ my $value;
+
+ open(local *FILE, "$device/$attr") or return "";
+ $value = <FILE>;
+ close(FILE);
+ return unless defined $value;
+
+ chomp($value);
+ return $value;
+}
+
+sub get_dimm_list
+{
+ my (@dirs, $dir, $opened, $file, @files);
+
+ if ($use_sysfs) {
+ @dirs = ('/sys/bus/i2c/drivers/eeprom', '/sys/bus/i2c/drivers/at24');
+ } else {
+ @dirs = ('/proc/sys/dev/sensors');
+ }
+
+ foreach $dir (@dirs) {
+ next unless opendir(local *DIR, $dir);
+ $opened++;
+ while (defined($file = readdir(DIR))) {
+ if ($use_sysfs) {
+ # We look for I2C devices like 0-0050 or 2-0051
+ next unless $file =~ /^\d+-[\da-f]+$/i;
+ next unless -d "$dir/$file";
+
+ # Device name must be eeprom (driver eeprom)
+ # or spd (driver at24)
+ my $attr = sysfs_device_attribute("$dir/$file", "name");
+ next unless defined $attr &&
+ ($attr eq "eeprom" || $attr eq "spd");
+ } else {
+ next unless $file =~ /^eeprom-/;
+ }
+ push @files, { eeprom => "$file",
+ file => "$dir/$file" };
+ }
+ close(DIR);
+ }
+
+ if (!$opened) {
+ print STDERR "No EEPROM found, try loading the eeprom or at24 module\n";
+ exit;
+ }
+
+ return sort { $a->{file} cmp $b->{file} } @files;
+}
+
+# @dimm is a list of hashes. There's one hash for each EEPROM we found.
+# Each hash has the following keys:
+# * eeprom: Name of the eeprom data file
+# * file: Full path to the eeprom data file
+# * bytes: The EEPROM data (array)
+# * is_rambus: Whether this is a RAMBUS DIMM or not (boolean)
+# * chk_label: The label to display for the checksum or CRC
+# * chk_valid: Whether the checksum or CRC is valid or not (boolean)
+# * chk_spd: The checksum or CRC value found in the EEPROM
+# * chk_calc: The checksum or CRC computed from the EEPROM data
+# Keys are added over time.
+@dimm = get_dimm_list() unless $use_hexdump;
+
+for my $i (0 .. $#dimm) {
+ my @bytes = readspd(0, 128, $dimm[$i]->{file});
+ $dimm[$i]->{bytes} = \@bytes;
+ $dimm[$i]->{is_rambus} = $bytes[0] < 4; # Simple heuristic
+ if ($dimm[$i]->{is_rambus} || $bytes[2] < 9) {
+ ($dimm[$i]->{chk_label}, $dimm[$i]->{chk_valid},
+ $dimm[$i]->{chk_spd}, $dimm[$i]->{chk_calc}) =
+ checksum(\@bytes);
+ } else {
+ ($dimm[$i]->{chk_label}, $dimm[$i]->{chk_valid},
+ $dimm[$i]->{chk_spd}, $dimm[$i]->{chk_calc}) =
+ check_crc(\@bytes);
+ }
+}
+
+# Checksum or CRC validation
+if (!$opt_igncheck) {
+ for (my $i = 0; $i < @dimm; ) {
+ if ($dimm[$i]->{chk_valid}) {
+ $i++;
+ } else {
+ splice(@dimm, $i, 1);
+ }
+ }
+}
+
+
+if ($opt_html && !$opt_bodyonly) {
+ print "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.1//EN' \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n\n",
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\">\n",
+ "<head>\n",
+ "\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" />\n",
+ "\t<title>PC DIMM Serial Presence Detect Tester/Decoder Output</title>\n",
+ "</head>\n\n",
+ "<body>\n";
+}
+
+printc("decode-dimms version $revision");
+printh('Memory Serial Presence Detect Decoder',
+'By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner,
+Jean Delvare, Trent Piepho and others');
+
+# Process the valid entries
+for $current (0 .. $#dimm) {
+ my @bytes = @{$dimm[$current]->{bytes}};
+
+ if ($opt_side_by_side) {
+ printl("Decoding EEPROM", $dimm[$current]->{eeprom});
+ }
+
+ if (!$use_hexdump) {
+ if ($dimm[$current]->{file} =~ /-([\da-f]+)$/i) {
+ my $dimm_num = hex($1) - 0x50 + 1;
+ if ($dimm_num >= 1 && $dimm_num <= 8) {
+ printl("Guessing DIMM is in", "bank $dimm_num");
+ }
+ }
+ }
+
+# Decode first 3 bytes (0-2)
+ prints("SPD EEPROM Information");
+
+ printl($dimm[$current]->{chk_label}, ($dimm[$current]->{chk_valid} ?
+ sprintf("OK (%s)", $dimm[$current]->{chk_calc}) :
+ sprintf("Bad\n(found %s, calculated %s)",
+ $dimm[$current]->{chk_spd}, $dimm[$current]->{chk_calc})));
+
+ my $temp;
+ if ($dimm[$current]->{is_rambus}) {
+ if ($bytes[0] == 1) { $temp = "0.7"; }
+ elsif ($bytes[0] == 2) { $temp = "1.0"; }
+ elsif ($bytes[0] == 0) { $temp = "Invalid"; }
+ else { $temp = "Reserved"; }
+ printl("SPD Revision", $temp);
+ } else {
+ my ($spd_size, $spd_used) = spd_sizes(\@bytes);
+ printl("# of bytes written to SDRAM EEPROM", $spd_used);
+ printl("Total number of bytes in EEPROM", $spd_size);
+
+ # If there's more data than what we've read, let's
+ # read it now. DDR3 will need this data.
+ if ($spd_used > @bytes) {
+ push (@bytes,
+ readspd(@bytes, $spd_used - @bytes,
+ $dimm[$current]->{file}));
+ }
+ }
+
+ my $type = sprintf("Unknown (0x%02x)", $bytes[2]);
+ if ($dimm[$current]->{is_rambus}) {
+ if ($bytes[2] == 1) { $type = "Direct Rambus"; }
+ elsif ($bytes[2] == 17) { $type = "Rambus"; }
+ } else {
+ my @type_list = (
+ "Reserved", "FPM DRAM", # 0, 1
+ "EDO", "Pipelined Nibble", # 2, 3
+ "SDR SDRAM", "Multiplexed ROM", # 4, 5
+ "DDR SGRAM", "DDR SDRAM", # 6, 7
+ "DDR2 SDRAM", "FB-DIMM", # 8, 9
+ "FB-DIMM Probe", "DDR3 SDRAM", # 10, 11
+ );
+ if ($bytes[2] < @type_list) {
+ $type = $type_list[$bytes[2]];
+ }
+ }
+ printl("Fundamental Memory type", $type);
+
+# Decode next 61 bytes (3-63, depend on memory type)
+ $decode_callback{$type}->(\@bytes)
+ if exists $decode_callback{$type};
+
+ if ($type eq "DDR3 SDRAM") {
+ # Decode DDR3-specific manufacturing data in bytes
+ # 117-149
+ decode_ddr3_mfg_data(\@bytes)
+ } else {
+ # Decode next 35 bytes (64-98, common to most
+ # memory types)
+ decode_manufacturing_information(\@bytes);
+ }
+
+# Next 27 bytes (99-125) are manufacturer specific, can't decode
+
+# Last 2 bytes (126-127) are reserved, Intel used them as an extension
+ if ($type eq "SDR SDRAM") {
+ decode_intel_spec_freq(\@bytes);
+ }
+}
+
+# Side-by-side output format is only possible if all DIMMs have a similar
+# output structure
+if ($opt_side_by_side) {
+ for $current (1 .. $#dimm) {
+ my @ref_output = @{$dimm[0]->{output}};
+ my @test_output = @{$dimm[$current]->{output}};
+ my $line;
+
+ if (scalar @ref_output != scalar @test_output) {
+ $opt_side_by_side = 0;
+ last;
+ }
+
+ for ($line = 0; $line < @ref_output; $line++) {
+ my ($ref_func, $ref_label, @ref_dummy) = @{$ref_output[$line]};
+ my ($test_func, $test_label, @test_dummy) = @{$test_output[$line]};
+
+ if ($ref_func != $test_func || $ref_label ne $test_label) {
+ $opt_side_by_side = 0;
+ last;
+ }
+ }
+ }
+
+ if (!$opt_side_by_side) {
+ printc("Side-by-side output only possible if all DIMMS are similar\n");
+
+ # Discard "Decoding EEPROM" entry from all outputs
+ for $current (0 .. $#dimm) {
+ shift(@{$dimm[$current]->{output}});
+ }
+ }
+}
+
+# Check if all dimms have the same value for a given line
+sub line_has_same_values($)
+{
+ my $line = shift;
+ my $value = $dimm[0]->{output}->[$line]->[2];
+
+ # Skip lines with no values (headers)
+ return 1 unless defined $value;
+
+ for my $other (1 .. $#dimm) {
+ return 0 unless $value eq $dimm[$other]->{output}->[$line]->[2];
+ }
+
+ return 1;
+}
+
+# Find out the longest value string to adjust the column width
+sub find_col_width($)
+{
+ my $width = shift;
+
+ return $width unless $opt_side_by_side && !$opt_html;
+
+ my $line;
+ my $line_nr = @{$dimm[0]->{output}};
+
+ for ($line = 0; $line < $line_nr; $line++) {
+ next if $opt_merge && line_has_same_values($line);
+
+ my @strings;
+
+ for my $current (0 .. $#dimm) {
+ my $value = $dimm[$current]->{output}->[$line]->[2];
+ push @strings, split("\n", $value) if defined $value;
+ }
+
+ foreach my $line2 (@strings) {
+ my $len = length($line2);
+ $width = $len if $len > $width;
+ }
+ }
+
+ return $width;
+}
+
+$sbs_col_width = find_col_width(15);
+
+# Print the decoded information for all DIMMs
+for $current (0 .. $#dimm) {
+ if ($opt_side_by_side) {
+ print "\n\n";
+ } else {
+ printl2("\n\nDecoding EEPROM", $dimm[$current]->{file},
+ "text-decoration: underline; font-weight: bold;");
+ }
+ print "<table border=\"1\">\n" if $opt_html;
+
+ my @output = @{$dimm[$current]->{output}};
+ for (my $line = 0; $line < @output; $line++) {
+ my ($func, @param) = @{$output[$line]};
+
+ if ($opt_side_by_side) {
+ foreach ($current+1 .. $#dimm) {
+ my @xoutput = @{$dimm[$_]->{output}};
+ if (@{$xoutput[$line]} == 3) {
+ # Line with data, stack all values
+ push @param, @{$xoutput[$line]}[2];
+ } else {
+ # Separator, make it span
+ push @param, scalar @dimm;
+ }
+ }
+ }
+
+ $func->(@param);
+ }
+
+ print "</table>\n" if $opt_html;
+ last if $opt_side_by_side;
+}
+printl2("\n\nNumber of SDRAM DIMMs detected and decoded", scalar @dimm);
+
+print "</body></html>\n" if ($opt_html && !$opt_bodyonly);
diff --git a/eeprom/decode-dimms.1 b/eeprom/decode-dimms.1
new file mode 100644
index 0000000..e8c6eea
--- /dev/null
+++ b/eeprom/decode-dimms.1
@@ -0,0 +1,67 @@
+.\"
+.\" decode-dimms.1 - manpage for the i2c-tools/decode-dimms utility
+.\" Copyright (C) 2013 Jaromir Capik
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+.\"
+.TH decode-dimms 1 "Oct 2013" "i2c-tools" "User Commands"
+.SH NAME
+decode-dimms \- decode the information found in memory module SPD EEPROMs
+.SH SYNOPSIS
+.B decode-dimms
+[-c] [-f [-b]] [-x|-X file [files..]]
+.br
+.B decode-dimms
+-h
+.SH DESCRIPTION
+
+The purpose of the
+.B decode-dimms
+tool is to decode the information found in memory module SPD EEPROMs.
+The SPD data is read either from the running system or dump files.
+In the former case, the tool requires either the eeprom kernel module
+or the at24 kernel module to be loaded.
+.SH PARAMETERS
+.TP
+.B \-f, --format
+Print nice html output
+.TP
+.B \-b, --bodyonly
+Don't print html header (useful for postprocessing the output)
+.TP
+.B \--side-by-side
+Display all DIMMs side-by-side if possible
+.TP
+.B \--merge-cells
+Merge neighbour cells with identical values (side-by-side output only, default)
+.TP
+.B \--no-merge-cells
+Don't merge neighbour cells with identical values (side-by-side output only)
+.TP
+.B \-c, --checksum
+Decode completely even if checksum fails
+.TP
+.B \-x
+Read data from hexdump files
+.TP
+.B \-X
+Same as -x except treat multibyte hex data as little endian
+.TP
+.B \-h, --help
+Display the usage summary
+.SH SEE ALSO
+.BR decode-vaio (1)
+.SH AUTHORS
+Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner, Jean Delvare
diff --git a/eeprom/decode-edid b/eeprom/decode-edid
new file mode 100755
index 0000000..a669985
--- /dev/null
+++ b/eeprom/decode-edid
@@ -0,0 +1,215 @@
+#!/usr/bin/perl -w
+#
+# Copyright (C) 2003-2008 Jean Delvare <jdelvare@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA.
+#
+# EEPROM data decoding for EDID. EDID (Extended Display Identification
+# Data) is a VESA standard which allows storing (on manufacturer's side)
+# and retrieving (on user's side) of configuration information about
+# displays, such as manufacturer, serial number, physical dimensions and
+# allowed horizontal and vertical refresh rates.
+#
+# Using the eeprom kernel driver, you have two possibilities to
+# make use of these data:
+# 1* The ddcmon script.
+# 2* This script.
+# Both solutions will return a different kind of information. The first
+# method will report user-interesting information, such as the model number
+# or the year of manufacturing. The second method will report video-card-
+# interesting information, such as video modes and refresh rates.
+#
+# Note that this script does almost nothing by itself. It simply converts
+# what it finds in /proc to binary data to feed the parse-edid program.
+# The parse-edid program was written by John Fremlin and is available at
+# the following address:
+# http://john.fremlin.de/programs/linux/read-edid/
+
+use strict;
+use Fcntl qw(:DEFAULT :seek);
+use vars qw($bus $address);
+use constant PROCFS => 1;
+use constant SYSFS => 2;
+
+# parse-edid will typically be installed in /usr/sbin or /usr/local/sbin
+# even though regular users can run it
+$ENV{PATH} .= ':/usr/local/sbin'
+ if $ENV{PATH} !~ m,(^|:)/usr/local/sbin/?(:|$),
+ && -x '/usr/local/sbin/parse-edid';
+$ENV{PATH} .= ':/usr/sbin'
+ if $ENV{PATH} !~ m,(^|:)/usr/sbin/?(:|$),
+ && -x '/usr/sbin/parse-edid';
+
+sub edid_valid_procfs
+{
+ my ($bus, $addr) = @_;
+
+ open EEDATA, "/proc/sys/dev/sensors/eeprom-i2c-$bus-$addr/00";
+ my $line = <EEDATA>;
+ close EEDATA;
+ return 1
+ if $line =~ m/^0 255 255 255 255 255 255 0 /;
+ return 0;
+}
+
+# Only used for sysfs
+sub rawread
+{
+ my ($filename, $length, $offset) = @_;
+ my $bytes = '';
+
+ sysopen(FH, $filename, O_RDONLY)
+ or die "Can't open $filename";
+ if ($offset)
+ {
+ sysseek(FH, $offset, SEEK_SET)
+ or die "Can't seek in $filename";
+ }
+
+ $offset = 0;
+ while ($length)
+ {
+ my $r = sysread(FH, $bytes, $length, $offset);
+ die "Can't read $filename"
+ unless defined($r);
+ die "Unexpected EOF in $filename"
+ unless $r;
+ $offset += $r;
+ $length -= $r;
+ }
+ close(FH);
+
+ return $bytes;
+}
+
+sub edid_valid_sysfs
+{
+ my ($bus, $addr) = @_;
+ my $bytes = rawread("/sys/bus/i2c/devices/$bus-00$addr/eeprom", 8, 0);
+
+ return 1
+ if $bytes eq "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00";
+ return 0;
+}
+
+sub bus_detect
+{
+ my $max = shift;
+
+ for (my $i=0; $i<$max; $i++)
+ {
+ if (-r "/proc/sys/dev/sensors/eeprom-i2c-$i-50/00")
+ {
+ if (edid_valid_procfs($i, '50'))
+ {
+ print STDERR
+ "decode-edid: using bus $i (autodetected)\n";
+ return $i;
+ }
+ }
+ elsif (-r "/sys/bus/i2c/devices/$i-0050/eeprom")
+ {
+ if (edid_valid_sysfs($i, '50'))
+ {
+ print STDERR
+ "decode-edid: using bus $i (autodetected)\n";
+ return $i;
+ }
+ }
+ }
+
+ return; # default
+}
+
+sub edid_decode
+{
+ my ($bus, $addr, $mode) = @_;
+
+ # Make sure it is an EDID EEPROM.
+
+ unless (($mode == PROCFS && edid_valid_procfs ($bus, $addr))
+ || ($mode == SYSFS && edid_valid_sysfs ($bus, $addr)))
+ {
+ print STDERR
+ "decode-edid: not an EDID EEPROM at $bus-$addr\n";
+ return;
+ }
+
+ $SIG{__WARN__} = sub { };
+ open PIPE, "| parse-edid"
+ or die "Can't open parse-edid. Please install read-edid.\n";
+ delete $SIG{__WARN__};
+ binmode PIPE;
+
+ if ($mode == PROCFS)
+ {
+ for (my $i=0; $i<=0x70; $i+=0x10)
+ {
+ my $file = sprintf '%02x', $i;
+ my $output = '';
+ open EEDATA, "/proc/sys/dev/sensors/eeprom-i2c-$bus-$addr/$file"
+ or die "Can't read /proc/sys/dev/sensors/eeprom-i2c-$bus-$addr/$file";
+ while(<EEDATA>)
+ {
+ foreach my $item (split)
+ {
+ $output .= pack "C", $item;
+ }
+ }
+ close EEDATA;
+ print PIPE $output;
+ }
+ }
+ elsif ($mode == SYSFS)
+ {
+ print PIPE rawread("/sys/bus/i2c/devices/$bus-00$address/eeprom", 128, 0);
+ }
+
+ close PIPE;
+}
+
+# Get the address. Default to 0x50 if not given.
+$address = $ARGV[1] || 0x50;
+# Convert to decimal, whatever the value.
+$address = oct $address if $address =~ m/^0/;
+# Convert to an hexadecimal string.
+$address = sprintf '%02x', $address;
+
+# Get the bus. Try to autodetect if not given.
+$bus = $ARGV[0] if defined $ARGV[0];
+$bus = bus_detect(8) unless defined $bus;
+
+if(defined $bus)
+{
+ print STDERR
+ "decode-edid: decode-edid version 1.1\n";
+ if (-r "/proc/sys/dev/sensors/eeprom-i2c-$bus-$address")
+ {
+ edid_decode ($bus, $address, PROCFS);
+ exit 0;
+ }
+ elsif (-r "/sys/bus/i2c/devices/$bus-00$address")
+ {
+ edid_decode ($bus, $address, SYSFS);
+ exit 0;
+ }
+}
+
+print STDERR
+ "EDID EEPROM not found. Please make sure that the eeprom module is loaded.\n";
+print STDERR
+ "Maybe your EDID EEPROM is on another bus. Try \"decode-edid ".($bus+1)."\".\n"
+ if defined $bus;
diff --git a/eeprom/decode-vaio b/eeprom/decode-vaio
new file mode 100755
index 0000000..68729dc
--- /dev/null
+++ b/eeprom/decode-vaio
@@ -0,0 +1,237 @@
+#!/usr/bin/perl -w
+#
+# Copyright (C) 2002-2008 Jean Delvare <jdelvare@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA.
+#
+# EEPROM data decoding for Sony Vaio laptops.
+#
+# The eeprom driver must be loaded. For kernels older than 2.6.0, the
+# eeprom driver can be found in the lm-sensors package.
+#
+# Please note that this is a guess-only work. Sony support refused to help
+# me, so if someone can provide information, please contact me.
+# My knowledge is summarized on this page:
+# http://jdelvare.nerim.net/articles/vaio/eeprom.html
+#
+# It seems that if present, the EEPROM is always at 0x57.
+#
+# Models tested so far:
+# PCG-F403 : No EEPROM
+# PCG-F707 : No EEPROM
+# PCG-GR114EK : OK
+# PCG-GR114SK : OK
+# PCG-GR214EP : OK
+# PCG-GRT955MP : OK
+# PCG-GRX316G : OK
+# PCG-GRX570 : OK
+# PCG-GRX600K : OK
+# PCG-U1 : OK
+# PCG-Z600LEK : No EEPROM
+# PCG-Z600NE : No EEPROM
+# VGN-S260 : OK
+# VGN-S4M/S : OK
+# VGN-TZ11MN/N : OK
+#
+# Thanks to Werner Heuser, Carsten Blume, Christian Gennerat, Joe Wreschnig,
+# Xavier Roche, Sebastien Lefevre, Lars Heer, Steve Dobson, Kent Hunt,
+# Timo Hoenig and others for their precious help.
+
+
+use strict;
+use Fcntl qw(:DEFAULT :seek);
+use vars qw($sysfs $found);
+
+use constant VERSION => "1.6";
+use constant ONLYROOT => "Readable only by root";
+
+sub print_item
+{
+ my ($label,$value) = @_;
+
+ printf("\%16s : \%s\n",$label,$value);
+}
+
+# Abstract reads so that other functions don't have to care wether
+# we need to use procfs or sysfs
+sub read_eeprom_bytes
+{
+ my ($bus, $addr, $offset, $length) = @_;
+ my $filename;
+
+ if ($sysfs)
+ {
+ $filename = "/sys/bus/i2c/devices/$bus-00$addr/eeprom";
+ sysopen(FH, $filename, O_RDONLY)
+ or die "Can't open $filename";
+ sysseek(FH, $offset, SEEK_SET)
+ or die "Can't seek in $filename";
+
+ my ($r, $bytes);
+ $bytes = '';
+ $offset = 0;
+ while($length)
+ {
+ $r = sysread(FH, $bytes, $length, $offset);
+ die "Can't read $filename"
+ unless defined($r);
+ die "Unexpected EOF in $filename"
+ unless $r;
+ $offset += $r;
+ $length -= $r;
+ }
+ close(FH);
+
+ return $bytes;
+ }
+ else
+ {
+ my $base = $offset & 0xf0;
+ $offset -= $base;
+ my $values = '';
+ my $remains = $length + $offset;
+
+ # Get all lines in a single string
+ while ($remains > 0)
+ {
+ $filename = "/proc/sys/dev/sensors/eeprom-i2c-$bus-$addr/"
+ . sprintf('%02x', $base);
+ open(FH, $filename)
+ or die "Can't open $filename";
+ $values .= <FH>;
+ close(FH);
+ $remains -= 16;
+ $base += 16;
+ }
+
+ # Store the useful part in an array
+ my @bytes = split(/[ \n]/, $values);
+ @bytes = @bytes[$offset..$offset+$length-1];
+
+ # Back to a binary string
+ return pack('C*', @bytes);
+ }
+}
+
+sub decode_string
+{
+ my ($bus, $addr, $offset, $length) = @_;
+
+ my $string = read_eeprom_bytes($bus, $addr, $offset, $length);
+ $string =~ s/\x00.*$//;
+
+ return($string);
+}
+
+sub decode_hexa
+{
+ my ($bus, $addr, $offset, $length) = @_;
+
+ my @bytes = unpack('C*', read_eeprom_bytes($bus, $addr, $offset, $length));
+ my $string='';
+
+ for(my $i=0;$i<$length;$i++)
+ {
+ $string.=sprintf('%02X', shift(@bytes));
+ }
+
+ return($string);
+}
+
+sub decode_uuid
+{
+ my ($bus,$addr,$base) = @_;
+
+ my @bytes = unpack('C16', read_eeprom_bytes($bus, $addr, $base, 16));
+ my $string='';
+
+ for(my $i=0;$i<16;$i++)
+ {
+ $string.=sprintf('%02x',shift(@bytes));
+ if(($i==3)||($i==5)||($i==7)||($i==9))
+ {
+ $string.='-';
+ }
+ }
+
+ if ($string eq '00000000-0000-0000-0000-000000000000')
+ {
+ return(ONLYROOT);
+ }
+ else
+ {
+ return($string);
+ }
+}
+
+sub vaio_decode
+{
+ my ($bus,$addr) = @_;
+
+ my $name = decode_string($bus, $addr, 128, 32);
+ # Simple heuristic to skip false positives
+ return 0 unless $name =~ m/^[A-Z-]{4}/;
+
+ print_item('Machine Name', $name);
+ my $serial = decode_string($bus, $addr, 192, 32);
+ print_item('Serial Number', $serial ? $serial : ONLYROOT);
+ print_item('UUID', decode_uuid($bus, $addr, 16));
+ my $revision = decode_string($bus, $addr, 160, 10);
+ print_item(length($revision) > 2 ? 'Service Tag' : 'Revision',
+ $revision);
+ print_item('Asset Tag', decode_string($bus, $addr, 170, 4).
+ decode_hexa($bus, $addr, 174, 12));
+ print_item('OEM Data', decode_string($bus, $addr, 32, 16));
+ print_item('Timestamp', decode_string($bus, $addr, 224, 18));
+ return 1;
+}
+
+BEGIN
+{
+ print("# Sony Vaio EEPROM Decoder version ".VERSION." by Jean Delvare\n\n");
+}
+
+END
+{
+ print("\n");
+}
+
+for (my $i = 0, $found=0; $i <= 4 && !$found; $i++)
+{
+ if (-r "/sys/bus/i2c/devices/$i-0057/eeprom")
+ {
+ $sysfs = 1;
+ $found += vaio_decode($i, '57');
+ }
+ elsif (-r "/proc/sys/dev/sensors/eeprom-i2c-$i-57")
+ {
+ if (-r "/proc/sys/dev/sensors/eeprom-i2c-$i-57/data0-15")
+ {
+ print("Deprecated old interface found. Please upgrade to lm_sensors 2.6.3 or greater.");
+ exit;
+ }
+ else
+ {
+ $sysfs = 0;
+ $found += vaio_decode($i, '57');
+ }
+ }
+}
+
+if (!$found)
+{
+ print("Vaio EEPROM not found. Please make sure that the eeprom module is loaded.\n");
+}
diff --git a/eeprom/decode-vaio.1 b/eeprom/decode-vaio.1
new file mode 100644
index 0000000..125d597
--- /dev/null
+++ b/eeprom/decode-vaio.1
@@ -0,0 +1,35 @@
+.\"
+.\" decode-vaio.1 - manpage for the i2c-tools/decode-vaio utility
+.\" Copyright (C) 2013 Jaromir Capik
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+.\"
+.TH decode-vaio 1 "Oct 2013" "i2c-tools" "User Commands"
+.SH NAME
+decode-vaio \- decode the information found in the Sony Vaio laptop
+identification EEPROMs
+.SH SYNOPSIS
+.B decode-vaio
+.SH DESCRIPTION
+
+The purpose of the
+.B decode-vaio
+tool is to decode the information found in the Sony Vaio laptop
+identification EEPROMs.
+The tool requires the eeprom kernel module to be loaded.
+.SH SEE ALSO
+.BR decode-dimms (1)
+.SH AUTHOR
+Jean Delvare
diff --git a/eepromer/.gitignore b/eepromer/.gitignore
new file mode 100644
index 0000000..5f76592
--- /dev/null
+++ b/eepromer/.gitignore
@@ -0,0 +1,2 @@
+/eeprom
+/eepromer
diff --git a/eepromer/Makefile b/eepromer/Makefile
new file mode 100644
index 0000000..b7d38f4
--- /dev/null
+++ b/eepromer/Makefile
@@ -0,0 +1,12 @@
+#eepromer Makefile
+
+CFLAGS = -O2 -I../include -Wall
+
+all: eepromer eeprom
+
+eepromer: eepromer.o
+
+eeprom: eeprom.o
+
+clean:
+ rm -rf *~ *.o eepromer eeprom
diff --git a/eepromer/README b/eepromer/README
new file mode 100644
index 0000000..392c266
--- /dev/null
+++ b/eepromer/README
@@ -0,0 +1,34 @@
+These programs are used to read and write eeproms.
+
+Use eeprom for small eeproms with one-byte addresses:
+ 24C01, 24C01A, 24C02, 24C04, 24C08, and 24C16
+ It works only on true i2c bus adapters.
+ See README.eeprom for details.
+ This program is deprecated, please use eeprog instead.
+
+Use eepromer for large eeproms with two-byte addresses:
+ 24C32, 24C64, 24C128, 24C256, and 24C512
+ It works only on true i2c bus adapters.
+ See README.eepromer for details.
+ This program is deprecated, please use eeprog instead.
+
+Use eeprog for either small or large eeproms.
+ This program was moved to a separate subdirectory.
+ Use the -16 switch for large eeproms.
+ It works on both i2c and smbus bus adapters.
+ See README.eeprog for details.
+
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!!! !
+!!! These programs should only be used on external busses such as i2c-pport !
+!!! unless you REALLY know what you are doing. !
+!!! !
+!!! Your computer probably contains eeproms for saving data vital to its !
+!!! operation. If you are not careful you might overwrite this data with !
+!!! this program and your computer may no longer boot! !
+!!! !
+!!! An example are the EEPROMS on your SDRAM DIMMs, your computer may no !
+!!! longer detect the RAM module rendering it essentially USELESS! !
+!!! !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
diff --git a/eepromer/README.eeprom b/eepromer/README.eeprom
new file mode 100644
index 0000000..b465d07
--- /dev/null
+++ b/eepromer/README.eeprom
@@ -0,0 +1,87 @@
+You can use this program to read/write to i2c-eeproms
+like the popular 24C16, 24C08, 24C04,.. In contrast to eeprommer
+which supports 24C256-type eeproms 24C16ss use 1-byte addresses!
+
+This program is deprecated, please use eeprog instead.
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!!! !
+!!! This program should only be used on external busses such as i2c-pport. !
+!!! !
+!!! Your computer may contain i2c-eeproms for saving data vital to its !
+!!! operation. If you are not careful you might overwrite this data with !
+!!! this program and your computer may no longer boot! !
+!!! !
+!!! An example are the EEPROMS on your SDRAM DIMMs, your computer may no !
+!!! longer detect the RAM module rendering it essentially USELESS! !
+!!! !
+!!! IBM Thinkpads are said to store their configuration data in a eeprom, !
+!!! if you manage to overwrite this eeprom you will have to send your !
+!!! computer to the manufacturer for a costly repair! !
+!!! !
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+It has several options:
+
+ -d devicenode
+
+ set this to the device-node of the i2c-bus
+ you want to use like /dev/i2c-0.
+ Use /dev/i2c-1 for the second bus, i2c-2 for the third...
+
+ The default /dev/i2c-0 should work most of the time.
+
+ -a address
+
+ set this to the device-address of your
+ eeprom. For a 24C16 the address is hardcoded to
+ 0x50, which is -you guessed it- the default.
+
+ For a 24C08 and smaller types you can choose which
+ addresses they occupy by forcing the address-pins
+ of the chip to High or Low so here the address may differ.
+
+ -p number_of_pages
+
+ set this to the number of pages you want to read
+ from or write to the eeprom. The 24C16 maps it's
+ pages to consecutive addresses on the i2c-bus so
+ we will try to read 256 bytes from every i2c
+ address between 'address' (inclusive) and
+ 'address + number_of_pages' (exclusive)...
+
+ A 24C16 has 8 pages so that's the default for this
+ parameter.
+
+ -f filename
+
+ read data from this file (when writing to eeprom) or
+ write data to this file (when reading from eeprom).
+
+ When reading a file that's smaller than the
+ eeprom's storage size we will pad the eeprom
+ with zeroes.
+
+ If no file is given we will just read the
+ eeprom (while in read-mode) and test it's presence
+ this way. In write-mode we will just write zeroes
+ to the eeprom.
+
+ -w When '-w' is present we will *write* to the eeprom.
+ If you do not specify '-w' we will read the contents
+ of the eeprom.
+
+ -y This flag will suppress the warning when you write to the
+ eeprom. You will not be required to enter 'yes' so be careful
+ when using this switch!
+
+
+I wrote that program to clear a 24C16 eeprom that sit's in my crappy
+satellite receiver because sometimes its Z80 processor likes to
+write garbage to it and then crash....
+
+No further testing besides writing a long series of "The quick brown
+fox jumps over the lazy dog!" and reading it back has been done so
+of course this comes without any warranty.
+
+ Chris <chris@hedonism.cx>
diff --git a/eepromer/README.eepromer b/eepromer/README.eepromer
new file mode 100644
index 0000000..4f0648c
--- /dev/null
+++ b/eepromer/README.eepromer
@@ -0,0 +1,29 @@
+Simple program for storing data to I2C EEPROM.
+
+This program is deprecated, please use eeprog instead.
+
+!!!!!!!!!!!!!!!!!!!!!!!!Warning!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+The EEPROM must be a large EEPROM which uses a 2-byte address
+field (24C32 or larger). It will NOT WORK on small EEPROMs
+(24C01 - 24C16) such as those used on SDRAM DIMMs.
+
+Tested only on 24C256.
+
+!!!!!!!!!!!!!!!!!!!!!!!!Warning!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+This program is intended for use on eeproms using external busses such as
+i2c-pport.
+Do not use this on your SDRAM DIMM EEPROMS, it won't work!!!!!!!!!
+Doing so will render your SDRAM USELESS and leave your system UNBOOTABLE!!!
+
+!!!!!!!!!!!!!!!!!!!!!!!!Warning!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+Options:
+ -r read
+ -w write
+ -e erase
+ -p print "super block of EEPROM" (date and size stored data)
+
+Daniel Smolik
+marvin@sitour.cz
diff --git a/eepromer/eeprom.8 b/eepromer/eeprom.8
new file mode 100644
index 0000000..0489590
--- /dev/null
+++ b/eepromer/eeprom.8
@@ -0,0 +1,64 @@
+.\"
+.\" eeprom.8 - manpage for the i2c-tools/eeprom utility
+.\" Copyright (C) 2013 Jaromir Capik
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+.\"
+.TH eeprom "8" "Jul 2013" "i2c-tools" "System Administration"
+.SH NAME
+eeprom \- reads and writes 24Cxx EEPROMs connected to I2C serial bus
+.SH SYNOPSIS
+.B eeprom
+[-d dev] [-a addr] [-p pgs] [-w] [-y] [-f file]
+.SH DESCRIPTION
+.B eeprom
+can be used for reading from / writing to I2C EEPROMs like the popular
+24C16, 24C08, 24C04, etc.
+In contrast to
+.B eeprommer
+which supports 24C256-type EEPROMs,
+this tool works with 1-byte addresses!
+.SH NOTES
+Don't forget to load your i2c chipset and the i2c-dev drivers.
+.P
+Pages/addresses:
+ EEPROMs with more than 256 bytes appear as if they
+ were several EEPROMs with consecutive addresses on the bus
+ so we might as well address several separate EEPROMs with
+ increasing addresses
+.SH PARAMETERS
+.TP
+.B dev
+device (default /dev/i2c-0)
+.TP
+.B addr
+base address of EEPROM (default 0xA0)
+.TP
+.B pgs
+number of pages to read (default 8)
+.TP
+.B \-w
+write to EEPROM (default is reading!)
+.TP
+.B \-y
+suppress warning when writing (default is to warn!)
+.TP
+.B \-f file
+copy EEPROM contents to/from file (default for read is test only; for write is all zeros)
+.SH SEE ALSO
+.BR eeprog (8),
+.BR eepromer (8)
+.SH AUTHOR
+Christian Vogel
diff --git a/eepromer/eeprom.c b/eepromer/eeprom.c
new file mode 100644
index 0000000..78f5481
--- /dev/null
+++ b/eepromer/eeprom.c
@@ -0,0 +1,299 @@
+/*
+This program is hereby placed into the public domain.
+Of course the program is provided without warranty of any kind.
+*/
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+/*
+ this program can read 24C16 (and probably smaller ones, too)
+ I wrote it as a quick and dirty hack because my satellite receiver
+ hung again... so I had to reprogram the eeprom where is stores it's
+ settings.
+ */
+
+#define DEFAULT_I2C_BUS "/dev/i2c-0"
+#define DEFAULT_EEPROM_ADDR 0x50 /* the 24C16 sits on i2c address 0x50 */
+#define DEFAULT_NUM_PAGES 8 /* we default to a 24C16 eeprom which has 8 pages */
+#define BYTES_PER_PAGE 256 /* one eeprom page is 256 byte */
+#define MAX_BYTES 8 /* max number of bytes to write in one chunk */
+ /* ... note: 24C02 and 24C01 only allow 8 bytes to be written in one chunk. *
+ * if you are going to write 24C04,8,16 you can change this to 16 */
+
+/* write len bytes (stored in buf) to eeprom at address addr, page-offset offset */
+/* if len=0 (buf may be NULL in this case) you can reposition the eeprom's read-pointer */
+/* return 0 on success, -1 on failure */
+int eeprom_write(int fd,
+ unsigned int addr,
+ unsigned int offset,
+ unsigned char *buf,
+ unsigned char len
+){
+ struct i2c_rdwr_ioctl_data msg_rdwr;
+ struct i2c_msg i2cmsg;
+ int i;
+ char _buf[MAX_BYTES + 1];
+
+ if(len>MAX_BYTES){
+ fprintf(stderr,"I can only write MAX_BYTES bytes at a time!\n");
+ return -1;
+ }
+
+ if(len+offset >256){
+ fprintf(stderr,"Sorry, len(%d)+offset(%d) > 256 (page boundary)\n",
+ len,offset);
+ return -1;
+ }
+
+ _buf[0]=offset; /* _buf[0] is the offset into the eeprom page! */
+ for(i=0;i<len;i++) /* copy buf[0..n] -> _buf[1..n+1] */
+ _buf[1+i]=buf[i];
+
+ msg_rdwr.msgs = &i2cmsg;
+ msg_rdwr.nmsgs = 1;
+
+ i2cmsg.addr = addr;
+ i2cmsg.flags = 0;
+ i2cmsg.len = 1+len;
+ i2cmsg.buf = _buf;
+
+ if((i=ioctl(fd,I2C_RDWR,&msg_rdwr))<0){
+ perror("ioctl()");
+ fprintf(stderr,"ioctl returned %d\n",i);
+ return -1;
+ }
+
+ if(len>0)
+ fprintf(stderr,"Wrote %d bytes to eeprom at 0x%02x, offset %08x\n",
+ len,addr,offset);
+ else
+ fprintf(stderr,"Positioned pointer in eeprom at 0x%02x to offset %08x\n",
+ addr,offset);
+
+ return 0;
+}
+
+/* read len bytes stored in eeprom at address addr, offset offset in array buf */
+/* return -1 on error, 0 on success */
+int eeprom_read(int fd,
+ unsigned int addr,
+ unsigned int offset,
+ unsigned char *buf,
+ unsigned char len
+){
+ struct i2c_rdwr_ioctl_data msg_rdwr;
+ struct i2c_msg i2cmsg;
+ int i;
+
+ if(len>MAX_BYTES){
+ fprintf(stderr,"I can only write MAX_BYTES bytes at a time!\n");
+ return -1;
+ }
+
+ if(eeprom_write(fd,addr,offset,NULL,0)<0)
+ return -1;
+
+ msg_rdwr.msgs = &i2cmsg;
+ msg_rdwr.nmsgs = 1;
+
+ i2cmsg.addr = addr;
+ i2cmsg.flags = I2C_M_RD;
+ i2cmsg.len = len;
+ i2cmsg.buf = buf;
+
+ if((i=ioctl(fd,I2C_RDWR,&msg_rdwr))<0){
+ perror("ioctl()");
+ fprintf(stderr,"ioctl returned %d\n",i);
+ return -1;
+ }
+
+ fprintf(stderr,"Read %d bytes from eeprom at 0x%02x, offset %08x\n",
+ len,addr,offset);
+
+ return 0;
+}
+
+
+
+int main(int argc, char **argv){
+ int i,j;
+
+ /* filedescriptor and name of device */
+ int d;
+ char *dn=DEFAULT_I2C_BUS;
+
+ /* filedescriptor and name of data file */
+ int f=-1;
+ char *fn=NULL;
+
+ unsigned int addr=DEFAULT_EEPROM_ADDR;
+ int rwmode=0;
+ int pages=DEFAULT_NUM_PAGES;
+
+ int force=0; /* suppress warning on write! */
+
+ while((i=getopt(argc,argv,"d:a:p:wyf:h"))>=0){
+ switch(i){
+ case 'h':
+ fprintf(stderr,"%s [-d dev] [-a adr] [-p pgs] [-w] [-y] [-f file]\n",argv[0]);
+ fprintf(stderr,"\tdev: device, e.g. /dev/i2c-0 (def)\n");
+ fprintf(stderr,"\tadr: base address of eeprom, eg 0xA0 (def)\n");
+ fprintf(stderr,"\tpgs: number of pages to read, eg 8 (def)\n");
+ fprintf(stderr,"\t-w : write to eeprom (default is reading!)\n");
+ fprintf(stderr,"\t-y : suppress warning when writing (default is to warn!)\n");
+ fprintf(stderr,"\t-f file: copy eeprom contents to/from file\n");
+ fprintf(stderr,"\t (default for read is test only; for write is all zeros)\n");
+ fprintf(stderr,"Note on pages/addresses:\n");
+ fprintf(stderr,"\teeproms with more than 256 byte appear as if they\n");
+ fprintf(stderr,"\twere several eeproms with consecutive addresses on the bus\n");
+ fprintf(stderr,"\tso we might as well address several separate eeproms with\n");
+ fprintf(stderr,"\tincreasing addresses....\n\n");
+ exit(1);
+ break;
+ case 'd':
+ dn=optarg;
+ break;
+ case 'a':
+ if(sscanf(optarg,"0x%x",&addr)!=1){
+ fprintf(stderr,"Cannot parse '%s' as addrs., example: 0xa0\n",
+ optarg);
+ exit(1);
+ }
+ break;
+ case 'p':
+ if(sscanf(optarg,"%d",&pages)!=1){
+ fprintf(stderr,"Cannot parse '%s' as number of pages, example: 8\n",
+ optarg);
+ exit(1);
+ }
+ break;
+ case 'w':
+ rwmode++;
+ break;
+ case 'f':
+ fn=optarg;
+ break;
+ case 'y':
+ force++;
+ break;
+ }
+
+ }
+
+ fprintf(stderr,"base-address of eeproms : 0x%02x\n",addr);
+ fprintf(stderr,"number of pages to read : %d (0x%02x .. 0x%02x)\n",
+ pages,addr,addr+pages-1);
+
+ if(fn){
+ if(!rwmode) /* if we are reading, *WRITE* to file */
+ f=open(fn,O_WRONLY|O_CREAT,0666);
+ else /* if we are writing to eeprom, *READ* from file */
+ f=open(fn,O_RDONLY);
+ if(f<0){
+ fprintf(stderr,"Could not open data-file %s for reading or writing\n",fn);
+ perror(fn);
+ exit(1);
+ }
+ fprintf(stderr,"file opened for %7s : %s\n",rwmode?"reading":"writing",fn);
+ fprintf(stderr," on filedescriptor : %d\n",f);
+ }
+
+ if((d=open(dn,O_RDWR))<0){
+ fprintf(stderr,"Could not open i2c at %s\n",dn);
+ perror(dn);
+ exit(1);
+ }
+
+ fprintf(stderr,"i2c-devicenode is : %s\n",dn);
+ fprintf(stderr," on filedescriptor : %d\n\n",d);
+
+ /***
+ *** I'm not the one to blame of you screw your computer!
+ ***/
+ if(rwmode && ! force){
+ unsigned char warnbuf[4];
+ fprintf(stderr,"**WARNING**\n");
+ fprintf(stderr," - \tYou have chosen to WRITE to this eeprom.\n");
+ fprintf(stderr,"\tMake sure that this tiny chip is *NOT* vital to the\n");
+ fprintf(stderr,"\toperation of your computer as you can easily corrupt\n");
+ fprintf(stderr,"\tthe configuration memory of your SDRAM-memory-module,\n");
+ fprintf(stderr,"\tyour IBM ThinkPad or whatnot...! Fixing these errors can be\n");
+ fprintf(stderr,"\ta time-consuming and very costly process!\n\n");
+ fprintf(stderr,"Things to consider:\n");
+ fprintf(stderr," - \tYou can have more than one i2c-bus, check in /proc/bus/i2c\n");
+ fprintf(stderr,"\tand specify the correct one with -d\n");
+ fprintf(stderr,"\tright now you have chosen to use '%s'\n",dn);
+ fprintf(stderr," - \tA eeprom can occupy several i2c-addresses (one per page)\n");
+ fprintf(stderr,"\tso please make sure that there is no vital eeprom in your computer\n");
+ fprintf(stderr,"\tsitting at addresses between 0x%02x and 0x%02x\n",addr,addr+pages-1);
+
+ fprintf(stderr,"Enter 'yes' to continue:");
+ fflush(stderr);
+ if(!fgets(warnbuf,sizeof(warnbuf),stdin)){
+ fprintf(stderr,"\nCould not read confirmation from stdin!\n");
+ exit(1);
+ }
+ if(strncmp(warnbuf,"yes",3)){
+ fprintf(stderr,"\n** ABORTING WRITE! **, you did not answer 'yes'\n");
+ exit(1);
+ }
+ }
+
+ for(i=0;i<pages;i++){
+ unsigned char buf[BYTES_PER_PAGE];
+
+ if(rwmode){
+
+ if(f>=0){
+ j=read(f,buf,sizeof(buf));
+ if(j<0){
+ fprintf(stderr,"Cannot read from file '%s'\n",fn);
+ perror(fn);
+ exit(1);
+ }
+ if(j!=sizeof(buf)){
+ fprintf(stderr,"File '%s' is too small, padding eeprom with zeroes\n",fn);
+ while(j<sizeof(buf))
+ buf[j++]=0;
+ }
+ } else {
+ for(j=0;j<sizeof(buf);j++)
+ buf[j]=0;
+ }
+ for(j=0;j<(BYTES_PER_PAGE/MAX_BYTES);j++)
+ if(eeprom_write(d,addr+i,j*MAX_BYTES,buf+(j*MAX_BYTES),MAX_BYTES)<0)
+ exit(1);
+ } else {
+ for(j=0;j<(BYTES_PER_PAGE/MAX_BYTES);j++)
+ if(eeprom_read(d,addr+i,j*MAX_BYTES,buf+(j*MAX_BYTES),MAX_BYTES)<0)
+ exit(1);
+ }
+
+
+ if(!rwmode && f>=0){
+ j=write(f,buf,sizeof(buf));
+ if(j!=sizeof(buf)){
+ fprintf(stderr,"Cannot write to file '%s'\n",fn);
+ perror(fn);
+ exit(1);
+ }
+ }
+
+ }
+
+ if(f>=0)
+ close(f);
+
+ close(d);
+
+ exit(0);
+
+}
diff --git a/eepromer/eepromer.8 b/eepromer/eepromer.8
new file mode 100644
index 0000000..c3f3239
--- /dev/null
+++ b/eepromer/eepromer.8
@@ -0,0 +1,61 @@
+.\"
+.\" eeprom.8 - manpage for the i2c-tools/eeprom utility
+.\" Copyright (C) 2013 Jaromir Capik
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+.\"
+.TH eepromer "8" "Jul 2013" "i2c-tools" "System Administration"
+.SH NAME
+eepromer \- reads and writes 24Cxx EEPROMs connected to I2C serial bus
+.SH SYNOPSIS
+.B eepromer
+[-r|-w|-e|-p] -f <device> <i2c-addr>
+.SH DESCRIPTION
+The EEPROM must be a large EEPROM which uses a 2-byte address
+field (24C32 or larger). It will NOT WORK on small EEPROMs
+(24C01 - 24C16) such as those used on SDRAM DIMMs.
+.SH NOTES
+Don't forget to load your i2c chipset and the i2c-dev drivers.
+.P
+Tested only on 24C256.
+.P
+.SH PARAMETERS
+.TP
+.I Actions
+.TP
+.B \-r
+Read
+.TP
+.B \-w
+Write
+.TP
+.B \-e
+Erase
+.TP
+.B \-p
+Print header
+.TP
+.I Bus
+.TP
+.B \-f device
+Device file representing the I2C bus (eg. /dev/i2c-0)
+.TP
+.B i2c-addr
+I2C bus address of the EEPROM (eg. 0x3A)
+.SH SEE ALSO
+.BR eeprog (8),
+.BR eeprom (8)
+.SH AUTHOR
+Daniel Smolik
diff --git a/eepromer/eepromer.c b/eepromer/eepromer.c
new file mode 100644
index 0000000..1536b15
--- /dev/null
+++ b/eepromer/eepromer.c
@@ -0,0 +1,723 @@
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+
+#define MAX_BLK_SIZE 64
+#define EEPROM_SIZE 32768
+#define READ 1
+#define WRITE 0
+#define ERASE 2
+#define PHEADER 3
+#define VER "eepromer v 0.4 (c) Daniel Smolik 2001\n"
+#define HEAD_SIZE sizeof(struct mini_inode)
+#define START_ADDR 0
+#define FORCE 1
+/*
+To disable startup warning #undef WARNINC
+
+
+*/
+
+#define WARNINC
+
+
+int block_write(int file,int dev_addr,int eeprom_addr,unsigned char *buf,int lenght);int block_write(int file,int dev_addr,int eeprom_addr,unsigned char *buf,int lenght);
+int block_read(int file,int dev_addr,int eeprom_addr,unsigned char *buf);
+
+/* block_read read block 64 bytes length and returns actual length of data*/
+void help(void);
+int init(char *device,int addr);
+int content_write(int file, int addr);
+int content_read(int file, int addr);
+int inode_write(int file, int dev_addr, int lenght);
+int inode_read(int file, int dev_addr, void *p_inode);
+void pheader(int file, int addr);
+void erase(int file,int addr,int eeprom_size);
+void made_address(int addr,unsigned char *buf);
+void warn(void);
+void bar(void);
+
+
+static int stav=0;
+
+
+
+static struct mini_inode {
+
+ time_t timestamp;
+ int data_len;
+ char data[56];
+
+ } m_ind,*p_ind;
+
+
+
+void help(void)
+{
+ FILE *fptr;
+ char s[100];
+
+ fprintf(stderr,"Syntax: eepromer [-r|-w|-e|-p] -f /dev/i2c-X ADDRESS \n\n");
+ fprintf(stderr," ADDRESS is address of i2c device eg. 0x51\n");
+
+ if((fptr = fopen("/proc/bus/i2c", "r"))) {
+ fprintf(stderr," Installed I2C busses:\n");
+ while(fgets(s, 100, fptr))
+ fprintf(stderr, " %s", s);
+ fclose(fptr);
+ }
+}
+
+
+
+
+
+int main(int argc, char *argv[]){
+
+ int i, file, addr;
+ int action; //in this variable will be (-r,-w,-e)
+ char device[45];
+ int force;
+
+ p_ind=&m_ind;
+ force=0;
+
+
+
+
+ for(i=1; i < argc;i++){
+
+
+ if(!strcmp("-r",argv[i])) {
+ action=READ;
+ break;
+ }
+ if(!strcmp("-e",argv[i])) {
+ action=ERASE;
+ break;
+ }
+ if(!strcmp("-w",argv[i])) {
+ action=WRITE;
+ break;
+ }
+ if(!strcmp("-p",argv[i])) {
+ action=PHEADER;
+ break;
+ }
+ if(!strcmp("-force",argv[i])) {
+ force=FORCE;
+ break;
+ }
+ if(!strcmp("-v",argv[i])) {
+ fprintf(stderr,VER);
+ exit(0);
+ break;
+ }
+ else {
+
+ fprintf(stderr,"Error: No action specified !\n");
+ help();
+ exit(1);
+ }
+
+ }
+
+
+#ifdef WARNINC
+
+ if(force!=FORCE) warn();
+
+#endif
+
+
+ if(argc < 5) {
+ fprintf(stderr,"Error: No i2c address specified !\n");
+ help();
+ exit(1);
+
+ }
+
+
+ for(i=1; i < argc;i++){
+
+
+ if(!strcmp("-f",argv[i])) {
+ strcpy(device,argv[i+1]);
+ break;
+ }
+
+ }
+
+ if(!strlen(device)) {
+
+ fprintf(stderr,"Error: No device specified !\n");
+ help();
+ exit(1);
+ }
+
+
+ if(! (addr=strtol(argv[4],NULL,16))) {
+
+ fprintf(stderr,"Error: Bad device address !\n");
+ help();
+ exit(1);
+ }
+
+ if(! (file=init(device,addr))){
+
+ fprintf(stderr,"Error: Init failed !\n");
+ exit(1);
+ }
+
+
+ switch(action){
+
+ case READ:
+ content_read(file,addr);
+ break;
+
+ case WRITE:
+ content_write(file,addr);
+ break;
+
+ case ERASE: erase(file,addr,EEPROM_SIZE);
+ break;
+ case PHEADER: pheader(file,addr);
+ break;
+
+ default:
+ fprintf(stderr,"Internal error!\n");
+ exit(1); break;
+
+ }
+
+
+ close(file);
+ exit(0);
+
+}
+
+
+
+/****************************************************************************/
+/* Low level function */
+/* */
+/****************************************************************************/
+
+
+
+
+
+int block_write(int file,int dev_addr,int eeprom_addr,unsigned char *buf,int lenght){
+
+ unsigned char buff[2];
+ struct i2c_msg msg[2];
+
+ struct i2c_ioctl_rdwr_data {
+
+ struct i2c_msg *msgs; /* ptr to array of simple messages */
+ int nmsgs; /* number of messages to exchange */
+ } msgst;
+
+
+
+ if ( lenght > (MAX_BLK_SIZE) ) {
+
+ fprintf(stderr,
+ "Error: Block too large:\n");
+
+ }
+
+
+ //bar();
+
+ made_address(eeprom_addr,buff);
+
+
+ msg[0].addr = dev_addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = buff;
+
+
+ msg[1].addr = dev_addr;
+ msg[1].flags = I2C_M_NOSTART;
+ msg[1].len = lenght;
+ msg[1].buf = buf;
+
+
+ msgst.msgs = msg;
+ msgst.nmsgs = 2;
+
+
+ if (ioctl(file,I2C_RDWR,&msgst) < 0){
+
+ fprintf(stderr,
+ "Error: Transaction failed: %s\n",
+ strerror(errno));
+
+ return 1;
+
+ }
+
+ return 0;
+
+}
+
+
+
+
+int block_read(int file,int dev_addr,int eeprom_addr,unsigned char *buf){
+
+ int ln;
+ char buff[2]; //={0x0,0x0};
+
+ struct i2c_msg msg[2];
+
+ struct i2c_ioctl_rdwr_data {
+
+ struct i2c_msg *msgs; /* ptr to array of simple messages */
+ int nmsgs; /* number of messages to exchange */
+ } msgst;
+
+
+
+ made_address(eeprom_addr,buff);
+ ln=0;
+ //bar();
+
+ msg[0].addr = dev_addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = buff;
+
+
+ msg[1].addr = dev_addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = MAX_BLK_SIZE;
+ msg[1].buf = buf;
+
+
+
+
+ msgst.msgs = msg;
+ msgst.nmsgs = 2;
+
+
+
+
+ if ((ln = ioctl(file, I2C_RDWR, &msgst)) < 0) {
+
+ fprintf(stderr,
+ "Error: Read error:%d\n",ln);
+ return ln;
+ }
+
+ return ln;
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void made_address(int addr,unsigned char *buf){
+
+ int k;
+
+ //addr = addr & 0xFFFF; /*odstranim nepoterbne bity*/
+
+ k=addr;
+ buf[1]=(unsigned char) (k & 0xFF); //vyrobim druhy byte adresy
+ k=addr & 0xFF00 ;
+ buf[0]= ((unsigned char) (k >> 8)) & 0x7F;
+
+
+ return;
+}
+
+
+int init(char *device,int addr) {
+
+ int file;
+ unsigned long funcs;
+
+ if ((file = open(device,O_RDWR)) < 0) {
+
+ fprintf(stderr,"Error: Could not open file %s\n",
+ device);
+
+ return 0;
+ }
+
+
+ /* check adapter functionality */
+ if (ioctl(file,I2C_FUNCS,&funcs) < 0) {
+ fprintf(stderr,
+ "Error: Could not get the adapter functionality matrix: %s\n",
+ strerror(errno));
+ close(file);
+ return 0;
+ }
+
+ /* The I2C address */
+ if (ioctl(file,I2C_SLAVE,addr) < 0) {
+ /* ERROR HANDLING; you can check errno to see what went wrong */
+ fprintf(stderr,
+ "Error: Cannot communicate with slave: %s\n",
+ strerror(errno));
+
+ close(file);
+ return 0;
+ }
+
+ return file;
+}
+
+
+int content_write(int file, int addr){
+
+ unsigned char buf[MAX_BLK_SIZE];
+ unsigned char pom;
+ int i, j, k, delka, addr_cnt;
+
+ delka=0;
+ addr_cnt=HEAD_SIZE;
+ k=0;
+
+ for(j=0;j<MAX_BLK_SIZE;j++)
+ buf[j]=0;
+
+
+
+ i=0;
+
+ for(;;) {
+
+ delka=fread(&pom,1,1,stdin);
+
+ if( delka > 0 ){
+ buf[i]=pom;
+ }
+
+ if(i==(MAX_BLK_SIZE-1) || (delka < 1)) {
+
+
+
+ if(block_write(file,addr,addr_cnt,buf,delka<1?i:(i+1)) !=0) {
+
+ fprintf(stderr,"Block write failed\n");
+ return 1;
+
+ }
+ //printf("i:%d\n",i);
+ addr_cnt=addr_cnt + i + (delka==1?1:0); //+i
+
+ for(j=0;j<MAX_BLK_SIZE;j++)
+ buf[j]=0;
+
+ i=0;
+ if(delka<1) {
+
+ //pisu EOF
+
+
+ if(inode_write(file,addr,(addr_cnt-HEAD_SIZE)) !=0) {
+
+ fprintf(stderr,"Inode write failed\n");
+ return 1;
+
+ }
+ break;
+ }
+
+
+ } else i++;
+
+ }
+
+ return 0;
+
+}
+
+
+int content_read(int file, int addr){
+
+ unsigned char buf[MAX_BLK_SIZE];
+ int i, j, k, delka;
+
+ delka=0;
+ k=0;
+
+
+ inode_read(file,addr,p_ind );
+
+
+ for(i=HEAD_SIZE;i<= (HEAD_SIZE + p_ind->data_len);i=i+MAX_BLK_SIZE ) {
+
+
+ if(block_read(file,addr,i,buf) !=0) {
+
+ fprintf(stderr,"Block read failed\n");
+ return 1;
+
+ }
+
+ if( (HEAD_SIZE + p_ind->data_len - i) < MAX_BLK_SIZE ) {
+ k= HEAD_SIZE + p_ind->data_len - i;
+ }else {
+ k=MAX_BLK_SIZE;
+ }
+
+
+ for(j=0;j<k ;j++){
+
+ putc(buf[j],stdout);
+
+ }
+
+
+ }
+
+ return 0;
+
+}
+
+
+
+void erase(int file, int addr,int eeprom_size){
+
+ unsigned char buf[MAX_BLK_SIZE];
+ int i, j, k, delka;
+
+ delka=0;
+ k=0;
+
+ for(j=0;j<MAX_BLK_SIZE;j++)
+ buf[j]=0;
+
+
+
+
+
+ for(i=0;i<eeprom_size;i=i+MAX_BLK_SIZE) {
+
+
+ if(block_write(file,addr,i,buf,MAX_BLK_SIZE) !=0) {
+
+ fprintf(stderr,"Block write failed\n");
+ return;
+
+ }
+
+ }
+
+ return;
+
+}
+
+
+
+void bar(void){
+
+
+ if( stav > 70 ) stav=0;
+
+
+ switch(stav) {
+
+
+ case 10: fwrite("\\",1,1,stderr);
+ fflush(stderr);
+ rewind(stderr);
+ break;
+ case 20: fwrite("|",1,1,stderr);
+ fflush(stderr);
+ rewind(stderr);
+ break;
+ case 30: fwrite("/",1,1,stderr);
+ fflush(stderr);
+ rewind(stderr);
+ break;
+ case 40: fwrite("-",1,1,stderr);
+ fflush(stderr);
+ rewind(stderr);
+ break;
+ case 50: fwrite("\\",1,1,stderr);
+ fflush(stderr);
+ rewind(stderr);
+ break;
+ case 60: fwrite("|",1,1,stderr);
+ fflush(stderr);
+ rewind(stderr);
+ break;
+ case 70: fwrite("/",1,1,stderr);
+ fflush(stderr);
+ rewind(stderr);
+ break;
+ }
+ stav++;
+
+}
+
+
+
+
+
+int inode_write(int file,int dev_addr,int lenght){
+
+ unsigned char buff[2];
+ struct i2c_msg msg[2];
+
+ struct i2c_ioctl_rdwr_data {
+
+ struct i2c_msg *msgs; /* ptr to array of simple messages */
+ int nmsgs; /* number of messages to exchange */
+ } msgst;
+
+
+ m_ind.timestamp=time(NULL);
+ m_ind.data_len=lenght;
+
+
+
+
+
+ //bar();
+
+ made_address(START_ADDR,buff);
+
+
+ msg[0].addr = dev_addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = buff;
+
+
+ msg[1].addr = dev_addr;
+ msg[1].flags = I2C_M_NOSTART;
+ msg[1].len = sizeof(struct mini_inode);
+ msg[1].buf = (char *) &m_ind;
+
+
+
+ msgst.msgs = msg;
+ msgst.nmsgs = 2;
+
+
+ if (ioctl(file,I2C_RDWR,&msgst) < 0){
+
+ fprintf(stderr,
+ "Error: Transaction failed: %s\n",
+ strerror(errno));
+
+ return 1;
+
+ }
+
+ return 0;
+
+}
+
+
+
+int inode_read(int file,int dev_addr,void *p_inode ){
+
+
+ #define POK 32
+ int ln;
+ char buff[2]; //={0x0,0x0};
+
+ struct i2c_msg msg[2];
+
+ struct i2c_ioctl_rdwr_data {
+
+ struct i2c_msg *msgs; /* ptr to array of simple messages */
+ int nmsgs; /* number of messages to exchange */
+ } msgst;
+
+ made_address(START_ADDR,buff);
+
+ ln=0;
+ //bar();
+
+ msg[0].addr = dev_addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = buff;
+
+
+ msg[1].addr = dev_addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = sizeof(struct mini_inode);
+ msg[1].buf = p_inode;
+
+
+
+
+ msgst.msgs = msg;
+ msgst.nmsgs = 2;
+
+
+ if ((ln = ioctl(file, I2C_RDWR, &msgst)) < 0) {
+
+ fprintf(stderr,
+ "Error: Read error:%d\n",ln);
+ return ln;
+ }
+
+
+
+ return ln;
+
+}
+
+
+void pheader(int file,int dev_addr){
+
+ struct tm *p_tm;
+ char time_buf[15],*p_buf;
+
+ p_buf=time_buf;
+ inode_read(file,dev_addr,p_ind );
+ p_tm=localtime(&p_ind->timestamp);
+ strftime(p_buf,sizeof(time_buf),"%Y%m%d%H%M%S",p_tm);
+ printf("LEN=%d,TIME=%s\n",p_ind->data_len,p_buf);
+ return;
+}
+
+
+
+
+#ifdef WARNINC
+void warn(void)
+{
+
+ fprintf(stderr,"\n\n!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!\n");
+ fprintf(stderr,"This program is intended for use on eeproms\nusing external busses such as i2c-pport.\n");
+ fprintf(stderr,"Do not use this on your SDRAM DIMM EEPROMS\nunless you REALLY REALLY know what you are\ndoing!!! Doing so will render your SDRAM\nUSELESS and leave your system UNBOOTABLE!!!\n");
+ fprintf(stderr,"To disable this warning use -force\n");
+ fprintf(stderr,"\n\nPress ENTER to continue or hit Control-C NOW !!!!\n\n\n");
+
+ getchar();
+}
+#endif
diff --git a/include/Module.mk b/include/Module.mk
new file mode 100644
index 0000000..92f9005
--- /dev/null
+++ b/include/Module.mk
@@ -0,0 +1,29 @@
+# Linux I2C header files
+#
+# Copyright (C) 2007, 2012 Jean Delvare <jdelvare@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+INCLUDE_DIR := include
+
+INCLUDE_TARGETS := i2c/smbus.h
+
+#
+# Commands
+#
+
+install-include: $(addprefix $(INCLUDE_DIR)/,$(INCLUDE_TARGETS))
+ $(INSTALL_DIR) $(DESTDIR)$(incdir)/i2c
+ for file in $(INCLUDE_TARGETS) ; do \
+ $(INSTALL_DATA) $(INCLUDE_DIR)/$$file $(DESTDIR)$(incdir)/$$file ; done
+
+uninstall-include:
+ for file in $(INCLUDE_TARGETS) ; do \
+ $(RM) $(DESTDIR)$(incdir)/$$file ; done
+
+install: install-include
+
+uninstall: uninstall-include
diff --git a/include/i2c/smbus.h b/include/i2c/smbus.h
new file mode 100644
index 0000000..18a5305
--- /dev/null
+++ b/include/i2c/smbus.h
@@ -0,0 +1,61 @@
+/*
+ smbus.h - SMBus level access helper functions
+
+ Copyright (C) 1995-97 Simon G. Vogl
+ Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#ifndef LIB_I2C_SMBUS_H
+#define LIB_I2C_SMBUS_H
+
+#define I2C_API_VERSION 0x100
+
+#include <linux/types.h>
+#include <linux/i2c.h>
+
+extern __s32 i2c_smbus_access(int file, char read_write, __u8 command,
+ int size, union i2c_smbus_data *data);
+
+extern __s32 i2c_smbus_write_quick(int file, __u8 value);
+extern __s32 i2c_smbus_read_byte(int file);
+extern __s32 i2c_smbus_write_byte(int file, __u8 value);
+extern __s32 i2c_smbus_read_byte_data(int file, __u8 command);
+extern __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value);
+extern __s32 i2c_smbus_read_word_data(int file, __u8 command);
+extern __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);
+extern __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value);
+
+/* Returns the number of read bytes */
+extern __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values);
+extern __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
+ const __u8 *values);
+
+/* Returns the number of read bytes */
+/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
+ ask for less than 32 bytes, your code will only work with kernels
+ 2.6.23 and later. */
+extern __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
+ __u8 *values);
+extern __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length,
+ const __u8 *values);
+
+/* Returns the number of read bytes */
+extern __s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length,
+ __u8 *values);
+
+#endif /* LIB_I2C_SMBUS_H */
diff --git a/include/linux/i2c-dev.h b/include/linux/i2c-dev.h
new file mode 100644
index 0000000..839d25a
--- /dev/null
+++ b/include/linux/i2c-dev.h
@@ -0,0 +1,72 @@
+/*
+ i2c-dev.h - i2c-bus driver, char device interface
+
+ Copyright (C) 1995-97 Simon G. Vogl
+ Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#ifndef _LINUX_I2C_DEV_H
+#define _LINUX_I2C_DEV_H
+
+#include <linux/types.h>
+
+
+/* /dev/i2c-X ioctl commands. The ioctl's parameter is always an
+ * unsigned long, except for:
+ * - I2C_FUNCS, takes pointer to an unsigned long
+ * - I2C_RDWR, takes pointer to struct i2c_rdwr_ioctl_data
+ * - I2C_SMBUS, takes pointer to struct i2c_smbus_ioctl_data
+ */
+#define I2C_RETRIES 0x0701 /* number of times a device address should
+ be polled when not acknowledging */
+#define I2C_TIMEOUT 0x0702 /* set timeout in units of 10 ms */
+
+/* NOTE: Slave address is 7 or 10 bits, but 10-bit addresses
+ * are NOT supported! (due to code brokenness)
+ */
+#define I2C_SLAVE 0x0703 /* Use this slave address */
+#define I2C_SLAVE_FORCE 0x0706 /* Use this slave address, even if it
+ is already in use by a driver! */
+#define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */
+
+#define I2C_FUNCS 0x0705 /* Get the adapter functionality mask */
+
+#define I2C_RDWR 0x0707 /* Combined R/W transfer (one STOP only) */
+
+#define I2C_PEC 0x0708 /* != 0 to use PEC with SMBus */
+#define I2C_SMBUS 0x0720 /* SMBus transfer */
+
+
+/* This is the structure as used in the I2C_SMBUS ioctl call */
+struct i2c_smbus_ioctl_data {
+ __u8 read_write;
+ __u8 command;
+ __u32 size;
+ union i2c_smbus_data *data;
+};
+
+/* This is the structure as used in the I2C_RDWR ioctl call */
+struct i2c_rdwr_ioctl_data {
+ struct i2c_msg *msgs; /* pointers to i2c_msgs */
+ __u32 nmsgs; /* number of i2c_msgs */
+};
+
+#define I2C_RDRW_IOCTL_MAX_MSGS 42
+
+
+#endif /* _LINUX_I2C_DEV_H */
diff --git a/lib/Module.mk b/lib/Module.mk
new file mode 100644
index 0000000..432a051
--- /dev/null
+++ b/lib/Module.mk
@@ -0,0 +1,102 @@
+# I2C library for Linux
+#
+# Copyright (C) 2012 Jean Delvare <jdelvare@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+LIB_DIR := lib
+
+LIB_CFLAGS := -Wstrict-prototypes -Wshadow -Wpointer-arith -Wcast-qual \
+ -Wcast-align -Wwrite-strings -Wnested-externs -Winline \
+ -W -Wundef -Wmissing-prototypes -Iinclude
+
+# The main and minor version of the library
+# The library soname (major number) must be changed if and only if the
+# interface is changed in a backward incompatible way. The interface is
+# defined by the public header files - in this case they are only smbus.h.
+LIB_MAINVER := 0
+LIB_MINORVER := 1.0
+LIB_VER := $(LIB_MAINVER).$(LIB_MINORVER)
+
+# The shared and static library names
+LIB_SHBASENAME := libi2c.so
+LIB_SHSONAME := $(LIB_SHBASENAME).$(LIB_MAINVER)
+LIB_SHLIBNAME := $(LIB_SHBASENAME).$(LIB_VER)
+LIB_STLIBNAME := libi2c.a
+
+LIB_TARGETS := $(LIB_SHLIBNAME)
+LIB_LINKS := $(LIB_SHSONAME) $(LIB_SHBASENAME)
+LIB_OBJECTS := smbus.o
+ifeq ($(BUILD_STATIC_LIB),1)
+LIB_TARGETS += $(LIB_STLIBNAME)
+LIB_OBJECTS += smbus.ao
+endif
+
+#
+# Libraries
+#
+
+$(LIB_DIR)/$(LIB_SHLIBNAME): $(LIB_DIR)/smbus.o
+ $(CC) -shared $(LDFLAGS) -Wl,--version-script=$(LIB_DIR)/libi2c.map -Wl,-soname,$(LIB_SHSONAME) -o $@ $^ -lc
+
+$(LIB_DIR)/$(LIB_SHSONAME):
+ $(RM) $@
+ $(LN) $(LIB_SHLIBNAME) $@
+
+$(LIB_DIR)/$(LIB_SHBASENAME):
+ $(RM) $@
+ $(LN) $(LIB_SHLIBNAME) $@
+
+$(LIB_DIR)/$(LIB_STLIBNAME): $(LIB_DIR)/smbus.ao
+ $(RM) $@
+ $(AR) rcvs $@ $^
+
+#
+# Objects
+# Each object must be built twice, once for the shared library and
+# once again for the static library.
+#
+
+$(LIB_DIR)/smbus.o: $(LIB_DIR)/smbus.c $(INCLUDE_DIR)/i2c/smbus.h
+ $(CC) $(SOCFLAGS) $(LIB_CFLAGS) -c $< -o $@
+
+$(LIB_DIR)/smbus.ao: $(LIB_DIR)/smbus.c $(INCLUDE_DIR)/i2c/smbus.h
+ $(CC) $(CFLAGS) $(LIB_CFLAGS) -c $< -o $@
+
+#
+# Commands
+#
+
+all-lib: $(addprefix $(LIB_DIR)/,$(LIB_TARGETS) $(LIB_LINKS))
+
+strip-lib: $(addprefix $(LIB_DIR)/,$(LIB_TARGETS))
+ strip $(addprefix $(LIB_DIR)/,$(LIB_TARGETS))
+
+clean-lib:
+ $(RM) $(addprefix $(LIB_DIR)/,*.o *.ao $(LIB_TARGETS) $(LIB_LINKS))
+
+install-lib: $(addprefix $(LIB_DIR)/,$(LIB_TARGETS))
+ $(INSTALL_DIR) $(DESTDIR)$(libdir)
+ $(INSTALL_PROGRAM) $(LIB_DIR)/$(LIB_SHLIBNAME) $(DESTDIR)$(libdir)
+ $(LN) $(LIB_SHLIBNAME) $(DESTDIR)$(libdir)/$(LIB_SHSONAME)
+ $(LN) $(LIB_SHSONAME) $(DESTDIR)$(libdir)/$(LIB_SHBASENAME)
+ifeq ($(BUILD_STATIC_LIB),1)
+ $(INSTALL_DATA) $(LIB_DIR)/$(LIB_STLIBNAME) $(DESTDIR)$(libdir)
+endif
+
+uninstall-lib:
+ for library in $(LIB_TARGETS) $(LIB_LINKS) ; do \
+ $(RM) $(DESTDIR)$(libdir)/$$library ; done
+
+all: all-lib
+
+strip: strip-lib
+
+clean: clean-lib
+
+install: install-lib
+
+uninstall: uninstall-lib
diff --git a/lib/libi2c.map b/lib/libi2c.map
new file mode 100644
index 0000000..3aed540
--- /dev/null
+++ b/lib/libi2c.map
@@ -0,0 +1,18 @@
+{
+global:
+ i2c_smbus_access;
+ i2c_smbus_write_quick;
+ i2c_smbus_read_byte;
+ i2c_smbus_write_byte;
+ i2c_smbus_read_byte_data;
+ i2c_smbus_write_byte_data;
+ i2c_smbus_read_word_data;
+ i2c_smbus_write_word_data;
+ i2c_smbus_process_call;
+ i2c_smbus_read_block_data;
+ i2c_smbus_write_block_data;
+ i2c_smbus_read_i2c_block_data;
+ i2c_smbus_write_i2c_block_data;
+ i2c_smbus_block_process_call;
+local: *;
+ };
diff --git a/lib/smbus.c b/lib/smbus.c
new file mode 100644
index 0000000..3a2c45a
--- /dev/null
+++ b/lib/smbus.c
@@ -0,0 +1,224 @@
+/*
+ smbus.c - SMBus level access helper functions
+
+ Copyright (C) 1995-1997 Simon G. Vogl
+ Copyright (C) 1998-1999 Frodo Looijaard <frodol@dds.nl>
+ Copyright (C) 2012-2013 Jean Delvare <jdelvare@suse.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#include <errno.h>
+#include <stddef.h>
+#include <i2c/smbus.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+/* Compatibility defines */
+#ifndef I2C_SMBUS_I2C_BLOCK_BROKEN
+#define I2C_SMBUS_I2C_BLOCK_BROKEN I2C_SMBUS_I2C_BLOCK_DATA
+#endif
+#ifndef I2C_FUNC_SMBUS_PEC
+#define I2C_FUNC_SMBUS_PEC I2C_FUNC_SMBUS_HWPEC_CALC
+#endif
+
+__s32 i2c_smbus_access(int file, char read_write, __u8 command,
+ int size, union i2c_smbus_data *data)
+{
+ struct i2c_smbus_ioctl_data args;
+ __s32 err;
+
+ args.read_write = read_write;
+ args.command = command;
+ args.size = size;
+ args.data = data;
+
+ err = ioctl(file, I2C_SMBUS, &args);
+ if (err == -1)
+ err = -errno;
+ return err;
+}
+
+
+__s32 i2c_smbus_write_quick(int file, __u8 value)
+{
+ return i2c_smbus_access(file, value, 0, I2C_SMBUS_QUICK, NULL);
+}
+
+__s32 i2c_smbus_read_byte(int file)
+{
+ union i2c_smbus_data data;
+ int err;
+
+ err = i2c_smbus_access(file, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data);
+ if (err < 0)
+ return err;
+
+ return 0x0FF & data.byte;
+}
+
+__s32 i2c_smbus_write_byte(int file, __u8 value)
+{
+ return i2c_smbus_access(file, I2C_SMBUS_WRITE, value,
+ I2C_SMBUS_BYTE, NULL);
+}
+
+__s32 i2c_smbus_read_byte_data(int file, __u8 command)
+{
+ union i2c_smbus_data data;
+ int err;
+
+ err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
+ I2C_SMBUS_BYTE_DATA, &data);
+ if (err < 0)
+ return err;
+
+ return 0x0FF & data.byte;
+}
+
+__s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value)
+{
+ union i2c_smbus_data data;
+ data.byte = value;
+ return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_BYTE_DATA, &data);
+}
+
+__s32 i2c_smbus_read_word_data(int file, __u8 command)
+{
+ union i2c_smbus_data data;
+ int err;
+
+ err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
+ I2C_SMBUS_WORD_DATA, &data);
+ if (err < 0)
+ return err;
+
+ return 0x0FFFF & data.word;
+}
+
+__s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value)
+{
+ union i2c_smbus_data data;
+ data.word = value;
+ return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_WORD_DATA, &data);
+}
+
+__s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
+{
+ union i2c_smbus_data data;
+ data.word = value;
+ if (i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_PROC_CALL, &data))
+ return -1;
+ else
+ return 0x0FFFF & data.word;
+}
+
+/* Returns the number of read bytes */
+__s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values)
+{
+ union i2c_smbus_data data;
+ int i, err;
+
+ err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
+ I2C_SMBUS_BLOCK_DATA, &data);
+ if (err < 0)
+ return err;
+
+ for (i = 1; i <= data.block[0]; i++)
+ values[i-1] = data.block[i];
+ return data.block[0];
+}
+
+__s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
+ const __u8 *values)
+{
+ union i2c_smbus_data data;
+ int i;
+ if (length > I2C_SMBUS_BLOCK_MAX)
+ length = I2C_SMBUS_BLOCK_MAX;
+ for (i = 1; i <= length; i++)
+ data.block[i] = values[i-1];
+ data.block[0] = length;
+ return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_BLOCK_DATA, &data);
+}
+
+/* Returns the number of read bytes */
+/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
+ ask for less than 32 bytes, your code will only work with kernels
+ 2.6.23 and later. */
+__s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
+ __u8 *values)
+{
+ union i2c_smbus_data data;
+ int i, err;
+
+ if (length > I2C_SMBUS_BLOCK_MAX)
+ length = I2C_SMBUS_BLOCK_MAX;
+ data.block[0] = length;
+
+ err = i2c_smbus_access(file, I2C_SMBUS_READ, command,
+ length == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
+ I2C_SMBUS_I2C_BLOCK_DATA, &data);
+ if (err < 0)
+ return err;
+
+ for (i = 1; i <= data.block[0]; i++)
+ values[i-1] = data.block[i];
+ return data.block[0];
+}
+
+__s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length,
+ const __u8 *values)
+{
+ union i2c_smbus_data data;
+ int i;
+ if (length > I2C_SMBUS_BLOCK_MAX)
+ length = I2C_SMBUS_BLOCK_MAX;
+ for (i = 1; i <= length; i++)
+ data.block[i] = values[i-1];
+ data.block[0] = length;
+ return i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_I2C_BLOCK_BROKEN, &data);
+}
+
+/* Returns the number of read bytes */
+__s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length,
+ __u8 *values)
+{
+ union i2c_smbus_data data;
+ int i, err;
+
+ if (length > I2C_SMBUS_BLOCK_MAX)
+ length = I2C_SMBUS_BLOCK_MAX;
+ for (i = 1; i <= length; i++)
+ data.block[i] = values[i-1];
+ data.block[0] = length;
+
+ err = i2c_smbus_access(file, I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_BLOCK_PROC_CALL, &data);
+ if (err < 0)
+ return err;
+
+ for (i = 1; i <= data.block[0]; i++)
+ values[i-1] = data.block[i];
+ return data.block[0];
+}
diff --git a/py-smbus/Module.mk b/py-smbus/Module.mk
new file mode 100644
index 0000000..04ceb64
--- /dev/null
+++ b/py-smbus/Module.mk
@@ -0,0 +1,31 @@
+# Python bindings for Linux SMBus access through i2c-dev
+#
+# Copyright (C) 2009 Mike Frysinger <vapier@gentoo.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+PY_SMBUS_DIR := py-smbus
+
+PYTHON ?= python
+DISTUTILS := \
+ cd $(PY_SMBUS_DIR) && \
+ $(PYTHON) setup.py
+
+all-python: $(INCLUDE_DIR)/i2c/smbus.h
+ $(DISTUTILS) build
+
+clean-python:
+ $(DISTUTILS) clean
+ rm -rf py-smbus/build
+
+install-python:
+ $(DISTUTILS) install
+
+all: all-python
+
+clean: clean-python
+
+install: install-python
diff --git a/py-smbus/README b/py-smbus/README
new file mode 100644
index 0000000..60d2ffd
--- /dev/null
+++ b/py-smbus/README
@@ -0,0 +1,26 @@
+
+README: py-smbus
+
+To build:
+ $ python setup.py build
+On most GNU/Linux distributions, you'll need to install the python-devel
+package for the build to succeed.
+
+To install (will also build if necessary):
+ $ python setup.py install
+
+For general build/install help:
+ $ python setup.py --help-commands
+
+Frequently Answered Question:
+
+Q: It's throwing exceptions, nothing works, what's wrong?
+
+ A1: You need write permissions to the i2c-dev devices. Try running as root.
+
+ A2: Addresses in Linux/I2C are the most-sig 7 bits, right-justified. E.g.
+ if your device uses address 0xc0 to write and 0xc1 to read, then use
+ address 0x60 with this module.
+
+ A3: Some other kernel driver has claimed that I2C address. Unload it first.
+
diff --git a/py-smbus/setup.py b/py-smbus/setup.py
new file mode 100644
index 0000000..28a4500
--- /dev/null
+++ b/py-smbus/setup.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+from distutils.core import setup, Extension
+
+setup( name="smbus",
+ version="1.1",
+ description="Python bindings for Linux SMBus access through i2c-dev",
+ author="Mark M. Hoffman",
+ author_email="mhoffman@lightlink.com",
+ maintainer="Mark M. Hoffman",
+ maintainer_email="linux-i2c@vger.kernel.org",
+ license="GPLv2",
+ url="https://i2c.wiki.kernel.org/index.php/I2C_Tools",
+ ext_modules=[Extension(
+ "smbus",
+ ["smbusmodule.c"],
+ extra_compile_args=['-I../include'],
+ extra_link_args=['-L../lib', '-li2c']
+ )]
+)
diff --git a/py-smbus/smbusmodule.c b/py-smbus/smbusmodule.c
new file mode 100644
index 0000000..48a408b
--- /dev/null
+++ b/py-smbus/smbusmodule.c
@@ -0,0 +1,744 @@
+/*
+ * smbusmodule.c - Python bindings for Linux SMBus access through i2c-dev
+ * Copyright (C) 2005-2007 Mark M. Hoffman <mhoffman@lightlink.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <Python.h>
+#include "structmember.h"
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <i2c/smbus.h>
+
+/*
+** These are required to build this module against Linux older than 2.6.23.
+*/
+#ifndef I2C_SMBUS_I2C_BLOCK_BROKEN
+#undef I2C_SMBUS_I2C_BLOCK_DATA
+#define I2C_SMBUS_I2C_BLOCK_BROKEN 6
+#define I2C_SMBUS_I2C_BLOCK_DATA 8
+#endif
+
+PyDoc_STRVAR(SMBus_module_doc,
+ "This module defines an object type that allows SMBus transactions\n"
+ "on hosts running the Linux kernel. The host kernel must have I2C\n"
+ "support, I2C device interface support, and a bus adapter driver.\n"
+ "All of these can be either built-in to the kernel, or loaded from\n"
+ "modules.\n"
+ "\n"
+ "Because the I2C device interface is opened R/W, users of this\n"
+ "module usually must have root permissions.\n");
+
+typedef struct {
+ PyObject_HEAD
+
+ int fd; /* open file descriptor: /dev/i2c-?, or -1 */
+ int addr; /* current client SMBus address */
+ int pec; /* !0 => Packet Error Codes enabled */
+} SMBus;
+
+static PyObject *
+SMBus_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ SMBus *self;
+
+ if ((self = (SMBus *)type->tp_alloc(type, 0)) == NULL)
+ return NULL;
+
+ self->fd = -1;
+ self->addr = -1;
+ self->pec = 0;
+
+ return (PyObject *)self;
+}
+
+PyDoc_STRVAR(SMBus_close_doc,
+ "close()\n\n"
+ "Disconnects the object from the bus.\n");
+
+static PyObject *
+SMBus_close(SMBus *self)
+{
+ if ((self->fd != -1) && (close(self->fd) == -1)) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ self->fd = -1;
+ self->addr = -1;
+ self->pec = 0;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static void
+SMBus_dealloc(SMBus *self)
+{
+ PyObject *ref = SMBus_close(self);
+ Py_XDECREF(ref);
+
+#if PY_MAJOR_VERSION >= 3
+ Py_TYPE(self)->tp_free((PyObject *)self);
+#else
+ self->ob_type->tp_free((PyObject *)self);
+#endif
+}
+
+#define MAXPATH 16
+
+PyDoc_STRVAR(SMBus_open_doc,
+ "open(bus)\n\n"
+ "Connects the object to the specified SMBus.\n");
+
+static PyObject *
+SMBus_open(SMBus *self, PyObject *args, PyObject *kwds)
+{
+ int bus;
+ char path[MAXPATH];
+
+ static char *kwlist[] = {"bus", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:open", kwlist, &bus))
+ return NULL;
+
+ if (snprintf(path, MAXPATH, "/dev/i2c-%d", bus) >= MAXPATH) {
+ PyErr_SetString(PyExc_OverflowError,
+ "Bus number is invalid.");
+ return NULL;
+ }
+
+ if ((self->fd = open(path, O_RDWR, 0)) == -1) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static int
+SMBus_init(SMBus *self, PyObject *args, PyObject *kwds)
+{
+ int bus = -1;
+
+ static char *kwlist[] = {"bus", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:__init__",
+ kwlist, &bus))
+ return -1;
+
+ if (bus >= 0) {
+ SMBus_open(self, args, kwds);
+ if (PyErr_Occurred())
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * private helper function, 0 => success, !0 => error
+ */
+static int
+SMBus_set_addr(SMBus *self, int addr)
+{
+ int ret = 0;
+
+ if (self->addr != addr) {
+ ret = ioctl(self->fd, I2C_SLAVE, addr);
+ self->addr = addr;
+ }
+
+ return ret;
+}
+
+#define SMBus_SET_ADDR(self, addr) do { \
+ if (SMBus_set_addr(self, addr)) { \
+ PyErr_SetFromErrno(PyExc_IOError); \
+ return NULL; \
+ } \
+} while(0)
+
+PyDoc_STRVAR(SMBus_write_quick_doc,
+ "write_quick(addr)\n\n"
+ "Perform SMBus Quick transaction.\n");
+
+static PyObject *
+SMBus_write_quick(SMBus *self, PyObject *args)
+{
+ int addr;
+ __s32 result;
+
+ if (!PyArg_ParseTuple(args, "i:write_quick", &addr))
+ return NULL;
+
+ SMBus_SET_ADDR(self, addr);
+
+ if ((result = i2c_smbus_write_quick(self->fd, I2C_SMBUS_WRITE))) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(SMBus_read_byte_doc,
+ "read_byte(addr) -> result\n\n"
+ "Perform SMBus Read Byte transaction.\n");
+
+static PyObject *
+SMBus_read_byte(SMBus *self, PyObject *args)
+{
+ int addr;
+ __s32 result;
+
+ if (!PyArg_ParseTuple(args, "i:read_byte", &addr))
+ return NULL;
+
+ SMBus_SET_ADDR(self, addr);
+
+ if ((result = i2c_smbus_read_byte(self->fd)) == -1) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ return Py_BuildValue("l", (long)result);
+}
+
+PyDoc_STRVAR(SMBus_write_byte_doc,
+ "write_byte(addr, val)\n\n"
+ "Perform SMBus Write Byte transaction.\n");
+
+static PyObject *
+SMBus_write_byte(SMBus *self, PyObject *args)
+{
+ int addr, val;
+ __s32 result;
+
+ if (!PyArg_ParseTuple(args, "ii:write_byte", &addr, &val))
+ return NULL;
+
+ SMBus_SET_ADDR(self, addr);
+
+ if ((result = i2c_smbus_write_byte(self->fd, (__u8)val)) == -1) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(SMBus_read_byte_data_doc,
+ "read_byte_data(addr, cmd) -> result\n\n"
+ "Perform SMBus Read Byte Data transaction.\n");
+
+static PyObject *
+SMBus_read_byte_data(SMBus *self, PyObject *args)
+{
+ int addr, cmd;
+ __s32 result;
+
+ if (!PyArg_ParseTuple(args, "ii:read_byte_data", &addr, &cmd))
+ return NULL;
+
+ SMBus_SET_ADDR(self, addr);
+
+ if ((result = i2c_smbus_read_byte_data(self->fd, (__u8)cmd)) == -1) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ return Py_BuildValue("l", (long)result);
+}
+
+PyDoc_STRVAR(SMBus_write_byte_data_doc,
+ "write_byte_data(addr, cmd, val)\n\n"
+ "Perform SMBus Write Byte Data transaction.\n");
+
+static PyObject *
+SMBus_write_byte_data(SMBus *self, PyObject *args)
+{
+ int addr, cmd, val;
+ __s32 result;
+
+ if (!PyArg_ParseTuple(args, "iii:write_byte_data", &addr, &cmd, &val))
+ return NULL;
+
+ SMBus_SET_ADDR(self, addr);
+
+ if ((result = i2c_smbus_write_byte_data(self->fd,
+ (__u8)cmd, (__u8)val)) == -1) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(SMBus_read_word_data_doc,
+ "read_word_data(addr, cmd) -> result\n\n"
+ "Perform SMBus Read Word Data transaction.\n");
+
+static PyObject *
+SMBus_read_word_data(SMBus *self, PyObject *args)
+{
+ int addr, cmd;
+ __s32 result;
+
+ if (!PyArg_ParseTuple(args, "ii:read_word_data", &addr, &cmd))
+ return NULL;
+
+ SMBus_SET_ADDR(self, addr);
+
+ if ((result = i2c_smbus_read_word_data(self->fd, (__u8)cmd)) == -1) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ return Py_BuildValue("l", (long)result);
+}
+
+PyDoc_STRVAR(SMBus_write_word_data_doc,
+ "write_word_data(addr, cmd, val)\n\n"
+ "Perform SMBus Write Word Data transaction.\n");
+
+static PyObject *
+SMBus_write_word_data(SMBus *self, PyObject *args)
+{
+ int addr, cmd, val;
+ __s32 result;
+
+ if (!PyArg_ParseTuple(args, "iii:write_word_data", &addr, &cmd, &val))
+ return NULL;
+
+ SMBus_SET_ADDR(self, addr);
+
+ if ((result = i2c_smbus_write_word_data(self->fd,
+ (__u8)cmd, (__u16)val)) == -1) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(SMBus_process_call_doc,
+ "process_call(addr, cmd, val)\n\n"
+ "Perform SMBus Process Call transaction.\n");
+
+static PyObject *
+SMBus_process_call(SMBus *self, PyObject *args)
+{
+ int addr, cmd, val;
+ __s32 result;
+
+ if (!PyArg_ParseTuple(args, "iii:process_call", &addr, &cmd, &val))
+ return NULL;
+
+ SMBus_SET_ADDR(self, addr);
+
+ if ((result = i2c_smbus_process_call(self->fd,
+ (__u8)cmd, (__u16)val)) == -1) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/*
+ * private helper function; returns a new list of integers
+ */
+static PyObject *
+SMBus_buf_to_list(__u8 const *buf, int len)
+{
+ PyObject *list = PyList_New(len);
+ int ii;
+
+ if (list == NULL)
+ return NULL;
+
+ for (ii = 0; ii < len; ii++) {
+ PyObject *val = Py_BuildValue("l", (long)buf[ii]);
+ PyList_SET_ITEM(list, ii, val);
+ }
+ return list;
+}
+
+PyDoc_STRVAR(SMBus_read_block_data_doc,
+ "read_block_data(addr, cmd) -> results\n\n"
+ "Perform SMBus Read Block Data transaction.\n");
+
+static PyObject *
+SMBus_read_block_data(SMBus *self, PyObject *args)
+{
+ int addr, cmd;
+ union i2c_smbus_data data;
+
+ if (!PyArg_ParseTuple(args, "ii:read_block_data", &addr, &cmd))
+ return NULL;
+
+ SMBus_SET_ADDR(self, addr);
+
+ /* save a bit of code by calling the access function directly */
+ if (i2c_smbus_access(self->fd, I2C_SMBUS_READ, (__u8)cmd,
+ I2C_SMBUS_BLOCK_DATA, &data)) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ /* first byte of the block contains (remaining) data length */
+ return SMBus_buf_to_list(&data.block[1], data.block[0]);
+}
+
+/*
+ * private helper function: convert an integer list to union i2c_smbus_data
+ */
+static int
+SMBus_list_to_data(PyObject *list, union i2c_smbus_data *data)
+{
+ static char *msg = "Third argument must be a list of at least one, "
+ "but not more than 32 integers";
+ int ii, len;
+
+ if (!PyList_Check(list)) {
+ PyErr_SetString(PyExc_TypeError, msg);
+ return 0; /* fail */
+ }
+
+ if ((len = PyList_GET_SIZE(list)) > 32) {
+ PyErr_SetString(PyExc_OverflowError, msg);
+ return 0; /* fail */
+ }
+
+ /* first byte is the length */
+ data->block[0] = (__u8)len;
+
+ for (ii = 0; ii < len; ii++) {
+ PyObject *val = PyList_GET_ITEM(list, ii);
+#if PY_MAJOR_VERSION >= 3
+ if (!PyLong_Check(val)) {
+ PyErr_SetString(PyExc_TypeError, msg);
+ return 0; /* fail */
+ }
+ data->block[ii+1] = (__u8)PyLong_AS_LONG(val);
+#else
+ if (!PyInt_Check(val)) {
+ PyErr_SetString(PyExc_TypeError, msg);
+ return 0; /* fail */
+ }
+ data->block[ii+1] = (__u8)PyInt_AS_LONG(val);
+#endif
+ }
+
+ return 1; /* success */
+}
+
+PyDoc_STRVAR(SMBus_write_block_data_doc,
+ "write_block_data(addr, cmd, [vals])\n\n"
+ "Perform SMBus Write Block Data transaction.\n");
+
+static PyObject *
+SMBus_write_block_data(SMBus *self, PyObject *args)
+{
+ int addr, cmd;
+ union i2c_smbus_data data;
+
+ if (!PyArg_ParseTuple(args, "iiO&:write_block_data", &addr, &cmd,
+ SMBus_list_to_data, &data))
+ return NULL;
+
+ SMBus_SET_ADDR(self, addr);
+
+ /* save a bit of code by calling the access function directly */
+ if (i2c_smbus_access(self->fd, I2C_SMBUS_WRITE, (__u8)cmd,
+ I2C_SMBUS_BLOCK_DATA, &data)) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(SMBus_block_process_call_doc,
+ "block_process_call(addr, cmd, [vals]) -> results\n\n"
+ "Perform SMBus Block Process Call transaction.\n");
+
+static PyObject *
+SMBus_block_process_call(SMBus *self, PyObject *args)
+{
+ int addr, cmd;
+ union i2c_smbus_data data;
+
+ if (!PyArg_ParseTuple(args, "iiO&:block_process_call", &addr, &cmd,
+ SMBus_list_to_data, &data))
+ return NULL;
+
+ SMBus_SET_ADDR(self, addr);
+
+ /* save a bit of code by calling the access function directly */
+ if (i2c_smbus_access(self->fd, I2C_SMBUS_WRITE, (__u8)cmd,
+ I2C_SMBUS_BLOCK_PROC_CALL, &data)) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ /* first byte of the block contains (remaining) data length */
+ return SMBus_buf_to_list(&data.block[1], data.block[0]);
+}
+
+PyDoc_STRVAR(SMBus_read_i2c_block_data_doc,
+ "read_i2c_block_data(addr, cmd, len=32) -> results\n\n"
+ "Perform I2C Block Read transaction.\n");
+
+static PyObject *
+SMBus_read_i2c_block_data(SMBus *self, PyObject *args)
+{
+ int addr, cmd, len=32;
+ union i2c_smbus_data data;
+
+ if (!PyArg_ParseTuple(args, "ii|i:read_i2c_block_data", &addr, &cmd,
+ &len))
+ return NULL;
+
+ SMBus_SET_ADDR(self, addr);
+
+ data.block[0] = len;
+ /* save a bit of code by calling the access function directly */
+ if (i2c_smbus_access(self->fd, I2C_SMBUS_READ, (__u8)cmd,
+ len == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN:
+ I2C_SMBUS_I2C_BLOCK_DATA, &data)) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ /* first byte of the block contains (remaining) data length */
+ return SMBus_buf_to_list(&data.block[1], data.block[0]);
+}
+
+PyDoc_STRVAR(SMBus_write_i2c_block_data_doc,
+ "write_i2c_block_data(addr, cmd, [vals])\n\n"
+ "Perform I2C Block Write transaction.\n");
+
+static PyObject *
+SMBus_write_i2c_block_data(SMBus *self, PyObject *args)
+{
+ int addr, cmd;
+ union i2c_smbus_data data;
+
+ if (!PyArg_ParseTuple(args, "iiO&:write_i2c_block_data", &addr, &cmd,
+ SMBus_list_to_data, &data))
+ return NULL;
+
+ SMBus_SET_ADDR(self, addr);
+
+ /* save a bit of code by calling the access function directly */
+ if (i2c_smbus_access(self->fd, I2C_SMBUS_WRITE, (__u8)cmd,
+ I2C_SMBUS_I2C_BLOCK_BROKEN, &data)) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(SMBus_type_doc,
+ "SMBus([bus]) -> SMBus\n\n"
+ "Return a new SMBus object that is (optionally) connected to the\n"
+ "specified I2C device interface.\n");
+
+static PyMethodDef SMBus_methods[] = {
+ {"open", (PyCFunction)SMBus_open, METH_VARARGS | METH_KEYWORDS,
+ SMBus_open_doc},
+ {"close", (PyCFunction)SMBus_close, METH_NOARGS,
+ SMBus_close_doc},
+ {"write_quick", (PyCFunction)SMBus_write_quick, METH_VARARGS,
+ SMBus_write_quick_doc},
+ {"read_byte", (PyCFunction)SMBus_read_byte, METH_VARARGS,
+ SMBus_read_byte_doc},
+ {"write_byte", (PyCFunction)SMBus_write_byte, METH_VARARGS,
+ SMBus_write_byte_doc},
+ {"read_byte_data", (PyCFunction)SMBus_read_byte_data, METH_VARARGS,
+ SMBus_read_byte_data_doc},
+ {"write_byte_data", (PyCFunction)SMBus_write_byte_data, METH_VARARGS,
+ SMBus_write_byte_data_doc},
+ {"read_word_data", (PyCFunction)SMBus_read_word_data, METH_VARARGS,
+ SMBus_read_word_data_doc},
+ {"write_word_data", (PyCFunction)SMBus_write_word_data, METH_VARARGS,
+ SMBus_write_word_data_doc},
+ {"process_call", (PyCFunction)SMBus_process_call, METH_VARARGS,
+ SMBus_process_call_doc},
+ {"read_block_data", (PyCFunction)SMBus_read_block_data, METH_VARARGS,
+ SMBus_read_block_data_doc},
+ {"write_block_data", (PyCFunction)SMBus_write_block_data, METH_VARARGS,
+ SMBus_write_block_data_doc},
+ {"block_process_call", (PyCFunction)SMBus_block_process_call,
+ METH_VARARGS, SMBus_block_process_call_doc},
+ {"read_i2c_block_data", (PyCFunction)SMBus_read_i2c_block_data,
+ METH_VARARGS, SMBus_read_i2c_block_data_doc},
+ {"write_i2c_block_data", (PyCFunction)SMBus_write_i2c_block_data,
+ METH_VARARGS, SMBus_write_i2c_block_data_doc},
+ {NULL},
+};
+
+static PyObject *
+SMBus_get_pec(SMBus *self, void *closure)
+{
+ PyObject *result = self->pec ? Py_True : Py_False;
+ Py_INCREF(result);
+ return result;
+}
+
+static int
+SMBus_set_pec(SMBus *self, PyObject *val, void *closure)
+{
+ int pec;
+
+ pec = PyObject_IsTrue(val);
+
+ if (val == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "Cannot delete attribute");
+ return -1;
+ }
+ else if (pec == -1) {
+ PyErr_SetString(PyExc_TypeError,
+ "The pec attribute must be a boolean.");
+ return -1;
+ }
+
+ if (self->pec != pec) {
+ if (ioctl(self->fd, I2C_PEC, pec)) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return -1;
+ }
+ self->pec = pec;
+ }
+
+ return 0;
+}
+
+static PyGetSetDef SMBus_getset[] = {
+ {"pec", (getter)SMBus_get_pec, (setter)SMBus_set_pec,
+ "True if Packet Error Codes (PEC) are enabled"},
+ {NULL},
+};
+
+static PyTypeObject SMBus_type = {
+#if PY_MAJOR_VERSION >= 3
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "SMBus", /* tp_name */
+#else
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "smbus.SMBus", /* tp_name */
+#endif
+ sizeof(SMBus), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)SMBus_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ SMBus_type_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ SMBus_methods, /* tp_methods */
+ 0, /* tp_members */
+ SMBus_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)SMBus_init, /* tp_init */
+ 0, /* tp_alloc */
+ SMBus_new, /* tp_new */
+};
+
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef SMBusModule = {
+ PyModuleDef_HEAD_INIT,
+ "SMBus", /* m_name */
+ SMBus_module_doc, /* m_doc */
+ -1, /* m_size */
+ NULL, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+#define INIT_RETURN(m) return m
+#define INIT_FNAME PyInit_smbus
+#else
+static PyMethodDef SMBus_module_methods[] = {
+ {NULL}
+};
+#define INIT_RETURN(m) return
+#define INIT_FNAME initsmbus
+#endif
+
+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+INIT_FNAME(void)
+{
+ PyObject* m;
+
+ if (PyType_Ready(&SMBus_type) < 0)
+ INIT_RETURN(NULL);
+
+#if PY_MAJOR_VERSION >= 3
+ m = PyModule_Create(&SMBusModule);
+#else
+ m = Py_InitModule3("smbus", SMBus_module_methods, SMBus_module_doc);
+#endif
+ if (m == NULL)
+ INIT_RETURN(NULL);
+
+ Py_INCREF(&SMBus_type);
+ PyModule_AddObject(m, "SMBus", (PyObject *)&SMBus_type);
+
+ INIT_RETURN(m);
+}
+
diff --git a/stub/Module.mk b/stub/Module.mk
new file mode 100644
index 0000000..50c4d18
--- /dev/null
+++ b/stub/Module.mk
@@ -0,0 +1,27 @@
+# Helper for the Linux i2c-stub bus driver
+#
+# Copyright (C) 2007-2009 Jean Delvare <jdelvare@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+STUB_DIR := stub
+
+#
+# Commands
+#
+
+install-stub: $(STUB_DIR)/i2c-stub-from-dump
+ $(INSTALL_DIR) $(DESTDIR)$(sbindir) $(DESTDIR)$(man8dir)
+ $(INSTALL_PROGRAM) $(STUB_DIR)/i2c-stub-from-dump $(DESTDIR)$(sbindir)
+ $(INSTALL_DATA) $(STUB_DIR)/i2c-stub-from-dump.8 $(DESTDIR)$(man8dir)
+
+uninstall-stub:
+ $(RM) $(DESTDIR)$(sbindir)/i2c-stub-from-dump
+ $(RM) $(DESTDIR)$(man8dir)/i2c-stub-from-dump.8
+
+install: install-stub
+
+uninstall: uninstall-stub
diff --git a/stub/i2c-stub-from-dump b/stub/i2c-stub-from-dump
new file mode 100755
index 0000000..a74e16e
--- /dev/null
+++ b/stub/i2c-stub-from-dump
@@ -0,0 +1,229 @@
+#!/usr/bin/perl -w
+#
+# Copyright (C) 2007-2012 Jean Delvare <jdelvare@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA
+#
+# This script feeds the i2c-stub driver with dump data from a real
+# I2C or SMBus chip. This can be useful when writing a driver for
+# a device you do not have access to, but of which you have a dump.
+
+use strict;
+use vars qw($bus_nr @addr $err);
+
+# Kernel version detection code by Mark M. Hoffman,
+# copied from sensors-detect.
+use vars qw(@kernel_version);
+
+sub initialize_kernel_version
+{
+ `uname -r` =~ /(\d+)\.(\d+)\.(\d+)(.*)/;
+ @kernel_version = ($1, $2, $3, $4);
+}
+
+sub kernel_version_at_least
+{
+ my ($vers, $plvl, $slvl) = @_;
+ return 1 if ($kernel_version[0] > $vers ||
+ ($kernel_version[0] == $vers &&
+ ($kernel_version[1] > $plvl ||
+ ($kernel_version[1] == $plvl &&
+ ($kernel_version[2] >= $slvl)))));
+ return 0;
+}
+
+# Find out the i2c bus number of i2c-stub
+sub get_i2c_stub_bus_number
+{
+ my $nr;
+
+ open(FH, "i2cdetect -l |") || die "Can't run i2cdetect";
+ while (<FH>) {
+ next unless m/^i2c-(\d+).*\tSMBus stub/;
+ $nr = $1;
+ last;
+ }
+ close(FH);
+
+ return $nr;
+}
+
+# Unload i2c-stub if we need an address it doesn't offer
+sub check_chip_addr {
+ my $chip_addr_file = shift;
+ my @addr = @{shift()};
+ local $_;
+
+ open(CHIP_ADDR, $chip_addr_file) || return;
+ $_ = <CHIP_ADDR>;
+ chomp;
+ my %stub_addr = map { $_ => 1 } split ',';
+ close(CHIP_ADDR);
+
+ foreach my $addr (@addr) {
+ unless (exists $stub_addr{$addr}) {
+ print STDERR "Cycling i2c-stub to get the address we need\n";
+ system("/sbin/rmmod", "i2c-stub");
+ return;
+ }
+ }
+}
+
+# Load the required kernel drivers if needed
+sub load_kernel_drivers
+{
+ local $_;
+ my @addr = @{shift()};
+ my $nr;
+
+ # i2c-stub may be loaded without the address we want
+ check_chip_addr("/sys/module/i2c_stub/parameters/chip_addr", \@addr);
+
+ # Maybe everything is already loaded
+ $nr = get_i2c_stub_bus_number();
+ return $nr if defined $nr;
+
+ system("/sbin/modprobe", "i2c-dev") == 0 || exit 1;
+ if (kernel_version_at_least(2, 6, 19)) {
+ system("/sbin/modprobe", "i2c-stub",
+ "chip_addr=".join(',', @addr)) == 0 || exit 1;
+ } else {
+ system("/sbin/modprobe", "i2c-stub") == 0 || exit 1;
+ }
+ # udev may take some time to create the device node
+ if (!(-x "/sbin/udevadm" && system("/sbin/udevadm settle") == 0)
+ && !(-x "/sbin/udevsettle" && system("/sbin/udevsettle") == 0)) {
+ sleep(1);
+ }
+
+ $nr = get_i2c_stub_bus_number();
+ if (!defined($nr)) {
+ print STDERR "Please load i2c-stub first\n";
+ exit 2;
+ }
+
+ return $nr;
+}
+
+sub process_dump
+{
+ my ($addr, $dump) = @_;
+ my $err = 0;
+ my ($bytes, $words);
+
+ open(DUMP, $dump) || die "Can't open $dump: $!\n";
+ OUTER_LOOP:
+ while (<DUMP>) {
+ if (m/^([0-9a-f]0) ?[:|](( [0-9a-fX]{2}){16})/i) {
+ # Byte dump
+ my $offset = hex($1);
+ my @values = split(/ /, $2);
+ shift(@values);
+ for (my $i = 0; $i < 16 && (my $val = shift(@values)); $i++) {
+ next if $val =~ m/X/;
+ if (system("i2cset", "-y", $bus_nr, $addr,
+ sprintf("0x\%02x", $offset+$i),
+ "0x$val", "b")) {
+ $err = 3;
+ last OUTER_LOOP;
+ }
+ $bytes++;
+ }
+ } elsif (m/^([0-9a-f][08]) ?[:|](( [0-9a-fX]{4}){8})/i) {
+ # Word dump
+ my $offset = hex($1);
+ my @values = split(/ /, $2);
+ shift(@values);
+ for (my $i = 0; $i < 8 && (my $val = shift(@values)); $i++) {
+ next if $val =~ m/X/;
+ if (system("i2cset", "-y", $bus_nr, $addr,
+ sprintf("0x\%02x", $offset+$i),
+ "0x$val", "w")) {
+ $err = 3;
+ last OUTER_LOOP;
+ }
+ $words++;
+ }
+ }
+ }
+ close(DUMP);
+
+ if ($bytes) {
+ printf SAVEOUT "$bytes byte values written to \%d-\%04x\n",
+ $bus_nr, $addr;
+ }
+
+ if ($words) {
+ printf SAVEOUT "$words word values written to \%d-\%04x\n",
+ $bus_nr, $addr;
+ }
+
+ if (!$err && !$bytes && !$words) {
+ printf SAVEOUT "Only garbage found in dump file $dump\n";
+ $err = 1;
+ }
+
+ return $err;
+}
+
+if ($>) {
+ print "You must be root to use this script\n";
+ exit 1;
+}
+
+if (@ARGV < 2) {
+ print STDERR "Usage: i2c-stub-from-dump <addr>[,<addr>,...] <dump file> [<dump file> ...]\n";
+ exit 1;
+}
+
+# Check the parameters
+@addr = split(/,/, shift @ARGV);
+foreach (@addr) {
+ unless (m/^0x[0-7][0-9a-f]$/i) {
+ print STDERR "Invalid address $_\n";
+ exit 1;
+ }
+ $_ = oct $_;
+}
+
+if (@addr < @ARGV) {
+ print STDERR "Fewer addresses than dumps provided\n";
+ exit 4;
+}
+
+initialize_kernel_version();
+if (@addr > 1 && !kernel_version_at_least(2, 6, 24)) {
+ print STDERR "Multiple addresses not supported by this kernel version\n";
+ exit 5;
+}
+
+$bus_nr = load_kernel_drivers(\@addr);
+
+# We don't want to see the output of 256 i2cset
+open(SAVEOUT, ">&STDOUT");
+open(STDOUT, ">/dev/null");
+foreach (@addr) {
+ if (!@ARGV) {
+ printf STDERR "Skipping \%d-\%04x, no dump file privided\n",
+ $bus_nr, $_;
+ next;
+ }
+ $err = process_dump($_, shift @ARGV);
+ last if $err;
+}
+close(STDOUT);
+
+exit($err);
diff --git a/stub/i2c-stub-from-dump.8 b/stub/i2c-stub-from-dump.8
new file mode 100644
index 0000000..b1550ed
--- /dev/null
+++ b/stub/i2c-stub-from-dump.8
@@ -0,0 +1,52 @@
+.TH I2C-STUB-FROM-DUMP 8 "March 2010"
+.SH NAME
+i2c-stub-from-dump \- feed i2c-stub with dump files
+
+.SH SYNOPSIS
+.B i2c-stub-from-dump
+.IR address [, address ,...]
+.IR dump-file " [" dump-file " ...]"
+
+.SH DESCRIPTION
+i2c-stub-from-dump is a small helper script for the i2c-stub kernel driver.
+It lets you setup one or more fake I2C chips on the i2c-stub bus based on
+dumps of the chips you want to emulate.
+
+i2c-stub-from-dump requires i2cdetect and i2cset to be installed and
+reachable through the user's PATH. The former is used to find out the i2c-stub
+bus number, while the latter is used to write to the fake I2C chips.
+
+.SH EXAMPLE
+You have an I2C chip on system A. You would like to do some development on its
+driver on system B. Here are the few steps you have to follow.
+
+On system A, use i2cdump to capture a dump from the chip. Assuming that the
+chip in question lives at address 0x4c on I2C bus 0, you would run:
+
+ i2cdump -y 0 0x4c b > chip.dump
+
+Adjust the bus number and chip address for your case. i2cdetect can help
+you find out their values. If the device uses word (16-bit) register
+access instead of the traditional byte (8-bit) access, use mode \fBw\fR
+instead of \fBb\fR.
+
+Copy the dump file to system B.
+
+On system B, run:
+
+ i2c-stub-from-dump 0x4c chip.dump
+
+This will load the required i2c-dev and i2c-stub kernel drivers if needed,
+then write all the register values to the emulated I2C chip at address 0x4c.
+Again, adjust the address as needed.
+
+.SH LIMITATIONS
+There are some limitations to the kind of devices that can be handled:
+.IP \(bu
+Device must not have banks (as most Winbond devices do).
+
+.SH SEE ALSO
+i2cdump(8), i2cdetect(8), i2cset(8)
+
+.SH AUTHOR
+Jean Delvare
diff --git a/tools/.gitignore b/tools/.gitignore
new file mode 100644
index 0000000..24e467d
--- /dev/null
+++ b/tools/.gitignore
@@ -0,0 +1,5 @@
+/i2cdump
+/i2cset
+/i2cget
+/i2cdetect
+/i2ctransfer
diff --git a/tools/Android.mk b/tools/Android.mk
new file mode 100644
index 0000000..9726141
--- /dev/null
+++ b/tools/Android.mk
@@ -0,0 +1,51 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := i2cget
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := i2cget.c i2cbusses.c util.c ../lib/smbus.c
+LOCAL_CFLAGS := -Wstrict-prototypes -Wshadow -Wpointer-arith -Wcast-qual \
+ -Wcast-align -Wwrite-strings -Wnested-externs -Winline \
+ -W -Wundef -Wmissing-prototypes -Iinclude
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := i2cset
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := i2cset.c i2cbusses.c util.c ../lib/smbus.c
+LOCAL_CFLAGS := -Wstrict-prototypes -Wshadow -Wpointer-arith -Wcast-qual \
+ -Wcast-align -Wwrite-strings -Wnested-externs -Winline \
+ -W -Wundef -Wmissing-prototypes -Iinclude
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := i2cdetect
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := i2cdetect.c i2cbusses.c util.c ../lib/smbus.c
+LOCAL_CFLAGS := -Wstrict-prototypes -Wshadow -Wpointer-arith -Wcast-qual \
+ -Wcast-align -Wwrite-strings -Wnested-externs -Winline \
+ -W -Wundef -Wmissing-prototypes -Iinclude
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := i2cdump
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := i2cdump.c i2cbusses.c util.c ../lib/smbus.c
+LOCAL_CFLAGS := -Wstrict-prototypes -Wshadow -Wpointer-arith -Wcast-qual \
+ -Wcast-align -Wwrite-strings -Wnested-externs -Winline \
+ -W -Wundef -Wmissing-prototypes -Iinclude
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := i2ctransfer
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := i2ctransfer.c i2cbusses.c util.c ../lib/smbus.c
+LOCAL_CFLAGS := -Wstrict-prototypes -Wshadow -Wpointer-arith -Wcast-qual \
+ -Wcast-align -Wwrite-strings -Wnested-externs -Winline \
+ -W -Wundef -Wmissing-prototypes -Iinclude
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include
+include $(BUILD_EXECUTABLE)
diff --git a/tools/Module.mk b/tools/Module.mk
new file mode 100644
index 0000000..6421a23
--- /dev/null
+++ b/tools/Module.mk
@@ -0,0 +1,98 @@
+# I2C tools for Linux
+#
+# Copyright (C) 2007, 2012 Jean Delvare <jdelvare@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+TOOLS_DIR := tools
+
+TOOLS_CFLAGS := -Wstrict-prototypes -Wshadow -Wpointer-arith -Wcast-qual \
+ -Wcast-align -Wwrite-strings -Wnested-externs -Winline \
+ -W -Wundef -Wmissing-prototypes -Iinclude
+ifeq ($(USE_STATIC_LIB),1)
+TOOLS_LDFLAGS := $(LIB_DIR)/$(LIB_STLIBNAME)
+else
+TOOLS_LDFLAGS := -L$(LIB_DIR) -li2c
+endif
+
+TOOLS_TARGETS := i2cdetect i2cdump i2cset i2cget i2ctransfer
+
+#
+# Programs
+#
+
+$(TOOLS_DIR)/i2cdetect: $(TOOLS_DIR)/i2cdetect.o $(TOOLS_DIR)/i2cbusses.o
+ $(CC) $(LDFLAGS) -o $@ $^ $(TOOLS_LDFLAGS)
+
+$(TOOLS_DIR)/i2cdump: $(TOOLS_DIR)/i2cdump.o $(TOOLS_DIR)/i2cbusses.o $(TOOLS_DIR)/util.o
+ $(CC) $(LDFLAGS) -o $@ $^ $(TOOLS_LDFLAGS)
+
+$(TOOLS_DIR)/i2cset: $(TOOLS_DIR)/i2cset.o $(TOOLS_DIR)/i2cbusses.o $(TOOLS_DIR)/util.o
+ $(CC) $(LDFLAGS) -o $@ $^ $(TOOLS_LDFLAGS)
+
+$(TOOLS_DIR)/i2cget: $(TOOLS_DIR)/i2cget.o $(TOOLS_DIR)/i2cbusses.o $(TOOLS_DIR)/util.o
+ $(CC) $(LDFLAGS) -o $@ $^ $(TOOLS_LDFLAGS)
+
+$(TOOLS_DIR)/i2ctransfer: $(TOOLS_DIR)/i2ctransfer.o $(TOOLS_DIR)/i2cbusses.o $(TOOLS_DIR)/util.o
+ $(CC) $(LDFLAGS) -o $@ $^ $(TOOLS_LDFLAGS)
+
+#
+# Objects
+#
+
+$(TOOLS_DIR)/i2cdetect.o: $(TOOLS_DIR)/i2cdetect.c $(TOOLS_DIR)/i2cbusses.h version.h $(INCLUDE_DIR)/i2c/smbus.h
+ $(CC) $(CFLAGS) $(TOOLS_CFLAGS) -c $< -o $@
+
+$(TOOLS_DIR)/i2cdump.o: $(TOOLS_DIR)/i2cdump.c $(TOOLS_DIR)/i2cbusses.h $(TOOLS_DIR)/util.h version.h $(INCLUDE_DIR)/i2c/smbus.h
+ $(CC) $(CFLAGS) $(TOOLS_CFLAGS) -c $< -o $@
+
+$(TOOLS_DIR)/i2cset.o: $(TOOLS_DIR)/i2cset.c $(TOOLS_DIR)/i2cbusses.h $(TOOLS_DIR)/util.h version.h $(INCLUDE_DIR)/i2c/smbus.h
+ $(CC) $(CFLAGS) $(TOOLS_CFLAGS) -c $< -o $@
+
+$(TOOLS_DIR)/i2cget.o: $(TOOLS_DIR)/i2cget.c $(TOOLS_DIR)/i2cbusses.h $(TOOLS_DIR)/util.h version.h $(INCLUDE_DIR)/i2c/smbus.h
+ $(CC) $(CFLAGS) $(TOOLS_CFLAGS) -c $< -o $@
+
+$(TOOLS_DIR)/i2ctransfer.o: $(TOOLS_DIR)/i2ctransfer.c $(TOOLS_DIR)/i2cbusses.h $(TOOLS_DIR)/util.h version.h
+ $(CC) $(CFLAGS) $(TOOLS_CFLAGS) -c $< -o $@
+
+$(TOOLS_DIR)/i2cbusses.o: $(TOOLS_DIR)/i2cbusses.c $(TOOLS_DIR)/i2cbusses.h
+ $(CC) $(CFLAGS) $(TOOLS_CFLAGS) -c $< -o $@
+
+$(TOOLS_DIR)/util.o: $(TOOLS_DIR)/util.c $(TOOLS_DIR)/util.h
+ $(CC) $(CFLAGS) $(TOOLS_CFLAGS) -c $< -o $@
+
+#
+# Commands
+#
+
+all-tools: $(addprefix $(TOOLS_DIR)/,$(TOOLS_TARGETS))
+
+strip-tools: $(addprefix $(TOOLS_DIR)/,$(TOOLS_TARGETS))
+ strip $(addprefix $(TOOLS_DIR)/,$(TOOLS_TARGETS))
+
+clean-tools:
+ $(RM) $(addprefix $(TOOLS_DIR)/,*.o $(TOOLS_TARGETS))
+
+install-tools: $(addprefix $(TOOLS_DIR)/,$(TOOLS_TARGETS))
+ $(INSTALL_DIR) $(DESTDIR)$(sbindir) $(DESTDIR)$(man8dir)
+ for program in $(TOOLS_TARGETS) ; do \
+ $(INSTALL_PROGRAM) $(TOOLS_DIR)/$$program $(DESTDIR)$(sbindir) ; \
+ $(INSTALL_DATA) $(TOOLS_DIR)/$$program.8 $(DESTDIR)$(man8dir) ; done
+
+uninstall-tools:
+ for program in $(TOOLS_TARGETS) ; do \
+ $(RM) $(DESTDIR)$(sbindir)/$$program ; \
+ $(RM) $(DESTDIR)$(man8dir)/$$program.8 ; done
+
+all: all-tools
+
+strip: strip-tools
+
+clean: clean-tools
+
+install: install-tools
+
+uninstall: uninstall-tools
diff --git a/tools/NOTICE b/tools/NOTICE
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/tools/NOTICE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/tools/i2cbusses.c b/tools/i2cbusses.c
new file mode 100644
index 0000000..dad22ea
--- /dev/null
+++ b/tools/i2cbusses.c
@@ -0,0 +1,418 @@
+/*
+ i2cbusses: Print the installed i2c busses for both 2.4 and 2.6 kernels.
+ Part of user-space programs to access for I2C
+ devices.
+ Copyright (c) 1999-2003 Frodo Looijaard <frodol@dds.nl> and
+ Mark D. Studebaker <mdsxyz123@yahoo.com>
+ Copyright (C) 2008-2012 Jean Delvare <jdelvare@suse.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+/* For strdup and snprintf */
+#define _BSD_SOURCE 1 /* for glibc <= 2.19 */
+#define _DEFAULT_SOURCE 1 /* for glibc >= 2.19 */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h> /* for NAME_MAX */
+#include <sys/ioctl.h>
+#include <string.h>
+#include <strings.h> /* for strcasecmp() */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "i2cbusses.h"
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+enum adt { adt_dummy, adt_isa, adt_i2c, adt_smbus, adt_unknown };
+
+struct adap_type {
+ const char *funcs;
+ const char* algo;
+};
+
+static struct adap_type adap_types[5] = {
+ { .funcs = "dummy",
+ .algo = "Dummy bus", },
+ { .funcs = "isa",
+ .algo = "ISA bus", },
+ { .funcs = "i2c",
+ .algo = "I2C adapter", },
+ { .funcs = "smbus",
+ .algo = "SMBus adapter", },
+ { .funcs = "unknown",
+ .algo = "N/A", },
+};
+
+static enum adt i2c_get_funcs(int i2cbus)
+{
+ unsigned long funcs;
+ int file;
+ char filename[20];
+ enum adt ret;
+
+ file = open_i2c_dev(i2cbus, filename, sizeof(filename), 1);
+ if (file < 0)
+ return adt_unknown;
+
+ if (ioctl(file, I2C_FUNCS, &funcs) < 0)
+ ret = adt_unknown;
+ else if (funcs & I2C_FUNC_I2C)
+ ret = adt_i2c;
+ else if (funcs & (I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ ret = adt_smbus;
+ else
+ ret = adt_dummy;
+
+ close(file);
+ return ret;
+}
+
+/* Remove trailing spaces from a string
+ Return the new string length including the trailing NUL */
+static int rtrim(char *s)
+{
+ int i;
+
+ for (i = strlen(s) - 1; i >= 0 && (s[i] == ' ' || s[i] == '\n'); i--)
+ s[i] = '\0';
+ return i + 2;
+}
+
+void free_adapters(struct i2c_adap *adapters)
+{
+ int i;
+
+ for (i = 0; adapters[i].name; i++)
+ free(adapters[i].name);
+ free(adapters);
+}
+
+/* We allocate space for the adapters in bunches. The last item is a
+ terminator, so here we start with room for 7 adapters, which should
+ be enough in most cases. If not, we allocate more later as needed. */
+#define BUNCH 8
+
+/* n must match the size of adapters at calling time */
+static struct i2c_adap *more_adapters(struct i2c_adap *adapters, int n)
+{
+ struct i2c_adap *new_adapters;
+
+ new_adapters = realloc(adapters, (n + BUNCH) * sizeof(struct i2c_adap));
+ if (!new_adapters) {
+ free_adapters(adapters);
+ return NULL;
+ }
+ memset(new_adapters + n, 0, BUNCH * sizeof(struct i2c_adap));
+
+ return new_adapters;
+}
+
+struct i2c_adap *gather_i2c_busses(void)
+{
+ char s[120];
+ struct dirent *de, *dde;
+ DIR *dir, *ddir;
+ FILE *f;
+ char fstype[NAME_MAX], sysfs[NAME_MAX], n[NAME_MAX];
+ int foundsysfs = 0;
+ int count=0;
+ struct i2c_adap *adapters;
+
+ adapters = calloc(BUNCH, sizeof(struct i2c_adap));
+ if (!adapters)
+ return NULL;
+
+ /* look in /proc/bus/i2c */
+ if ((f = fopen("/proc/bus/i2c", "r"))) {
+ while (fgets(s, 120, f)) {
+ char *algo, *name, *type, *all;
+ int len_algo, len_name, len_type;
+ int i2cbus;
+
+ algo = strrchr(s, '\t');
+ *(algo++) = '\0';
+ len_algo = rtrim(algo);
+
+ name = strrchr(s, '\t');
+ *(name++) = '\0';
+ len_name = rtrim(name);
+
+ type = strrchr(s, '\t');
+ *(type++) = '\0';
+ len_type = rtrim(type);
+
+ sscanf(s, "i2c-%d", &i2cbus);
+
+ if ((count + 1) % BUNCH == 0) {
+ /* We need more space */
+ adapters = more_adapters(adapters, count + 1);
+ if (!adapters)
+ return NULL;
+ }
+
+ all = malloc(len_name + len_type + len_algo);
+ if (all == NULL) {
+ free_adapters(adapters);
+ return NULL;
+ }
+ adapters[count].nr = i2cbus;
+ adapters[count].name = strcpy(all, name);
+ adapters[count].funcs = strcpy(all + len_name, type);
+ adapters[count].algo = strcpy(all + len_name + len_type,
+ algo);
+ count++;
+ }
+ fclose(f);
+ goto done;
+ }
+
+ /* look in sysfs */
+ /* First figure out where sysfs was mounted */
+ if ((f = fopen("/proc/mounts", "r")) == NULL) {
+ goto done;
+ }
+ while (fgets(n, NAME_MAX, f)) {
+ sscanf(n, "%*[^ ] %[^ ] %[^ ] %*s\n", sysfs, fstype);
+ if (strcasecmp(fstype, "sysfs") == 0) {
+ foundsysfs++;
+ break;
+ }
+ }
+ fclose(f);
+ if (! foundsysfs) {
+ goto done;
+ }
+
+ /* Bus numbers in i2c-adapter don't necessarily match those in
+ i2c-dev and what we really care about are the i2c-dev numbers.
+ Unfortunately the names are harder to get in i2c-dev */
+ strcat(sysfs, "/class/i2c-dev");
+ if(!(dir = opendir(sysfs)))
+ goto done;
+ /* go through the busses */
+ while ((de = readdir(dir)) != NULL) {
+ if (!strcmp(de->d_name, "."))
+ continue;
+ if (!strcmp(de->d_name, ".."))
+ continue;
+
+ /* this should work for kernels 2.6.5 or higher and */
+ /* is preferred because is unambiguous */
+ sprintf(n, "%s/%s/name", sysfs, de->d_name);
+ f = fopen(n, "r");
+ /* this seems to work for ISA */
+ if(f == NULL) {
+ sprintf(n, "%s/%s/device/name", sysfs, de->d_name);
+ f = fopen(n, "r");
+ }
+ /* non-ISA is much harder */
+ /* and this won't find the correct bus name if a driver
+ has more than one bus */
+ if(f == NULL) {
+ sprintf(n, "%s/%s/device", sysfs, de->d_name);
+ if(!(ddir = opendir(n)))
+ continue;
+ while ((dde = readdir(ddir)) != NULL) {
+ if (!strcmp(dde->d_name, "."))
+ continue;
+ if (!strcmp(dde->d_name, ".."))
+ continue;
+ if ((!strncmp(dde->d_name, "i2c-", 4))) {
+ sprintf(n, "%s/%s/device/%s/name",
+ sysfs, de->d_name, dde->d_name);
+ if((f = fopen(n, "r")))
+ goto found;
+ }
+ }
+ }
+
+found:
+ if (f != NULL) {
+ int i2cbus;
+ enum adt type;
+ char *px;
+
+ px = fgets(s, 120, f);
+ fclose(f);
+ if (!px) {
+ fprintf(stderr, "%s: read error\n", n);
+ continue;
+ }
+ if ((px = strchr(s, '\n')) != NULL)
+ *px = 0;
+ if (!sscanf(de->d_name, "i2c-%d", &i2cbus))
+ continue;
+ if (!strncmp(s, "ISA ", 4)) {
+ type = adt_isa;
+ } else {
+ /* Attempt to probe for adapter capabilities */
+ type = i2c_get_funcs(i2cbus);
+ }
+
+ if ((count + 1) % BUNCH == 0) {
+ /* We need more space */
+ adapters = more_adapters(adapters, count + 1);
+ if (!adapters)
+ return NULL;
+ }
+
+ adapters[count].nr = i2cbus;
+ adapters[count].name = strdup(s);
+ if (adapters[count].name == NULL) {
+ free_adapters(adapters);
+ return NULL;
+ }
+ adapters[count].funcs = adap_types[type].funcs;
+ adapters[count].algo = adap_types[type].algo;
+ count++;
+ }
+ }
+ closedir(dir);
+
+done:
+ return adapters;
+}
+
+static int lookup_i2c_bus_by_name(const char *bus_name)
+{
+ struct i2c_adap *adapters;
+ int i, i2cbus = -1;
+
+ adapters = gather_i2c_busses();
+ if (adapters == NULL) {
+ fprintf(stderr, "Error: Out of memory!\n");
+ return -3;
+ }
+
+ /* Walk the list of i2c busses, looking for the one with the
+ right name */
+ for (i = 0; adapters[i].name; i++) {
+ if (strcmp(adapters[i].name, bus_name) == 0) {
+ if (i2cbus >= 0) {
+ fprintf(stderr,
+ "Error: I2C bus name is not unique!\n");
+ i2cbus = -4;
+ goto done;
+ }
+ i2cbus = adapters[i].nr;
+ }
+ }
+
+ if (i2cbus == -1)
+ fprintf(stderr, "Error: I2C bus name doesn't match any "
+ "bus present!\n");
+
+done:
+ free_adapters(adapters);
+ return i2cbus;
+}
+
+/*
+ * Parse an I2CBUS command line argument and return the corresponding
+ * bus number, or a negative value if the bus is invalid.
+ */
+int lookup_i2c_bus(const char *i2cbus_arg)
+{
+ unsigned long i2cbus;
+ char *end;
+
+ i2cbus = strtoul(i2cbus_arg, &end, 0);
+ if (*end || !*i2cbus_arg) {
+ /* Not a number, maybe a name? */
+ return lookup_i2c_bus_by_name(i2cbus_arg);
+ }
+ if (i2cbus > 0xFFFFF) {
+ fprintf(stderr, "Error: I2C bus out of range!\n");
+ return -2;
+ }
+
+ return i2cbus;
+}
+
+/*
+ * Parse a CHIP-ADDRESS command line argument and return the corresponding
+ * chip address, or a negative value if the address is invalid.
+ */
+int parse_i2c_address(const char *address_arg)
+{
+ long address;
+ char *end;
+
+ address = strtol(address_arg, &end, 0);
+ if (*end || !*address_arg) {
+ fprintf(stderr, "Error: Chip address is not a number!\n");
+ return -1;
+ }
+ if (address < 0x03 || address > 0x77) {
+ fprintf(stderr, "Error: Chip address out of range "
+ "(0x03-0x77)!\n");
+ return -2;
+ }
+
+ return address;
+}
+
+int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet)
+{
+ int file;
+
+ snprintf(filename, size, "/dev/i2c/%d", i2cbus);
+ filename[size - 1] = '\0';
+ file = open(filename, O_RDWR);
+
+ if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) {
+ sprintf(filename, "/dev/i2c-%d", i2cbus);
+ file = open(filename, O_RDWR);
+ }
+
+ if (file < 0 && !quiet) {
+ if (errno == ENOENT) {
+ fprintf(stderr, "Error: Could not open file "
+ "`/dev/i2c-%d' or `/dev/i2c/%d': %s\n",
+ i2cbus, i2cbus, strerror(ENOENT));
+ } else {
+ fprintf(stderr, "Error: Could not open file "
+ "`%s': %s\n", filename, strerror(errno));
+ if (errno == EACCES)
+ fprintf(stderr, "Run as root?\n");
+ }
+ }
+
+ return file;
+}
+
+int set_slave_addr(int file, int address, int force)
+{
+ /* With force, let the user read from/write to the registers
+ even when a driver is also running */
+ if (ioctl(file, force ? I2C_SLAVE_FORCE : I2C_SLAVE, address) < 0) {
+ fprintf(stderr,
+ "Error: Could not set address to 0x%02x: %s\n",
+ address, strerror(errno));
+ return -errno;
+ }
+
+ return 0;
+}
diff --git a/tools/i2cbusses.h b/tools/i2cbusses.h
new file mode 100644
index 0000000..26143a5
--- /dev/null
+++ b/tools/i2cbusses.h
@@ -0,0 +1,44 @@
+/*
+ i2cbusses.h - Part of the i2c-tools package
+
+ Copyright (C) 2004-2010 Jean Delvare <jdelvare@suse.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#ifndef _I2CBUSSES_H
+#define _I2CBUSSES_H
+
+#include <unistd.h>
+
+struct i2c_adap {
+ int nr;
+ char *name;
+ const char *funcs;
+ const char *algo;
+};
+
+struct i2c_adap *gather_i2c_busses(void);
+void free_adapters(struct i2c_adap *adapters);
+
+int lookup_i2c_bus(const char *i2cbus_arg);
+int parse_i2c_address(const char *address_arg);
+int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet);
+int set_slave_addr(int file, int address, int force);
+
+#define MISSING_FUNC_FMT "Error: Adapter does not have %s capability\n"
+
+#endif
diff --git a/tools/i2cdetect.8 b/tools/i2cdetect.8
new file mode 100644
index 0000000..81d0f97
--- /dev/null
+++ b/tools/i2cdetect.8
@@ -0,0 +1,123 @@
+.TH I2CDETECT 8 "October 2017"
+.SH NAME
+i2cdetect \- detect I2C chips
+
+.SH SYNOPSIS
+.B i2cdetect
+.RI [ -y ]
+.RI [ -a ]
+.RI [ -q | -r ]
+.I i2cbus
+.RI [ "first last" ]
+.br
+.B i2cdetect
+.I -F
+.I i2cbus
+.br
+.B i2cdetect
+.I -V
+.br
+.B i2cdetect
+.I -l
+
+.SH DESCRIPTION
+i2cdetect is a userspace program to scan an I2C bus for devices. It
+outputs a table with the list of detected devices on the specified bus.
+\fIi2cbus\fR indicates the number or name of the I2C bus to be scanned, and
+should correspond to one of the busses listed by \fIi2cdetect -l\fR.
+The optional parameters \fIfirst\fR and \fIlast\fR restrict the scanning
+range (default: from 0x03 to 0x77).
+.PP
+As there is no standard I2C detection command, i2cdetect uses arbitrary
+SMBus commands (namely SMBus quick write and SMBus receive byte) to probe
+for devices. By default, the command used is the one believed to be the
+safest for each address. See options \fI-q\fR and \fI-r\fR to change this
+behavior.
+.PP
+i2cdetect can also be used to query the functionalities of an I2C bus
+(see option \fB-F\fP.)
+
+.SH WARNING
+This program can confuse your I2C bus, cause data loss and worse!
+
+.SH INTERPRETING THE OUTPUT
+Each cell in the output table will contain one of the following symbols:
+.IP \(bu "\w'\(bu'u+1n"
+"--". The address was probed but no chip answered.
+.IP \(bu
+"UU". Probing was skipped, because this address is currently in use by
+a driver. This strongly suggests that there is a chip at this address.
+.IP \(bu
+An address number in hexadecimal, e.g. "2d" or "4e". A chip
+was found at this address.
+
+.SH OPTIONS
+.TP
+.B "\-y"
+Disable interactive mode. By default, i2cdetect will wait for a confirmation
+from the user before messing with the I2C bus. When this flag is used, it
+will perform the operation directly. This is mainly meant to be used in
+scripts.
+.TP
+.B "\-a"
+Force scanning of non-regular addresses. Not recommended.
+.TP
+.B "\-q"
+Use SMBus "quick write" command for probing.
+Not recommended. This is known to corrupt the Atmel AT24RF08 EEPROM
+found on many IBM Thinkpad laptops.
+.TP
+.B "\-r"
+Use SMBus "receive byte" command for probing.
+Not recommended. This is known to lock SMBus on various write-only
+chips (most notably clock chips at address 0x69).
+.TP
+.B "\-F"
+Display the list of functionalities implemented by the adapter and exit.
+.TP
+.B "\-V"
+Display the version and exit.
+.TP
+.B "\-l"
+Output a list of installed busses.
+
+.SH EXAMPLES
+.PP
+List all available I2C busses:
+.nf
+.RS
+# i2cdetect -l
+.RE
+.fi
+.PP
+Immediately scan the standard addresses on I2C bus 9 (i2c-9), using the
+default method for each address (no user confirmation):
+.nf
+.RS
+# i2cdetect -y 9
+.RE
+.fi
+.PP
+Query the functionalities of I2C bus 1 (i2c-1):
+.nf
+.RS
+# i2cdetect -F 1
+.RE
+.fi
+.PP
+Scan addresses 0x10 to 0x17 on the I2C bus named "SMBus I801 adapter at efa0",
+using the "receive byte" method, after user confirmation:
+.nf
+.RS
+# i2cdetect -r "SMBus I801 adapter at efa0" 0x10 0x17
+.RE
+.fi
+
+.SH SEE ALSO
+i2cdump(8), i2cget(8), i2cset(8), i2ctransfer(8), sensors-detect(8)
+
+.SH AUTHOR
+Frodo Looijaard, Mark D. Studebaker and Jean Delvare
+
+This manual page was originally written by Aurelien Jarno
+<aurel32@debian.org>, for the Debian GNU/Linux system.
diff --git a/tools/i2cdetect.c b/tools/i2cdetect.c
new file mode 100644
index 0000000..1804f84
--- /dev/null
+++ b/tools/i2cdetect.c
@@ -0,0 +1,386 @@
+/*
+ i2cdetect.c - a user-space program to scan for I2C devices
+ Copyright (C) 1999-2004 Frodo Looijaard <frodol@dds.nl>, and
+ Mark D. Studebaker <mdsxyz123@yahoo.com>
+ Copyright (C) 2004-2012 Jean Delvare <jdelvare@suse.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <i2c/smbus.h>
+#include "i2cbusses.h"
+#include "../version.h"
+
+#define MODE_AUTO 0
+#define MODE_QUICK 1
+#define MODE_READ 2
+#define MODE_FUNC 3
+
+static void help(void)
+{
+ fprintf(stderr,
+ "Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]\n"
+ " i2cdetect -F I2CBUS\n"
+ " i2cdetect -l\n"
+ " I2CBUS is an integer or an I2C bus name\n"
+ " If provided, FIRST and LAST limit the probing range.\n");
+}
+
+static int scan_i2c_bus(int file, int mode, unsigned long funcs,
+ int first, int last)
+{
+ int i, j;
+ int cmd, res;
+
+ printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\n");
+
+ for (i = 0; i < 128; i += 16) {
+ printf("%02x: ", i);
+ for(j = 0; j < 16; j++) {
+ fflush(stdout);
+
+ /* Select detection command for this address */
+ switch (mode) {
+ default:
+ cmd = mode;
+ break;
+ case MODE_AUTO:
+ if ((i+j >= 0x30 && i+j <= 0x37)
+ || (i+j >= 0x50 && i+j <= 0x5F))
+ cmd = MODE_READ;
+ else
+ cmd = MODE_QUICK;
+ break;
+ }
+
+ /* Skip unwanted addresses */
+ if (i+j < first || i+j > last
+ || (cmd == MODE_READ &&
+ !(funcs & I2C_FUNC_SMBUS_READ_BYTE))
+ || (cmd == MODE_QUICK &&
+ !(funcs & I2C_FUNC_SMBUS_QUICK))) {
+ printf(" ");
+ continue;
+ }
+
+ /* Set slave address */
+ if (ioctl(file, I2C_SLAVE, i+j) < 0) {
+ if (errno == EBUSY) {
+ printf("UU ");
+ continue;
+ } else {
+ fprintf(stderr, "Error: Could not set "
+ "address to 0x%02x: %s\n", i+j,
+ strerror(errno));
+ return -1;
+ }
+ }
+
+ /* Probe this address */
+ switch (cmd) {
+ default: /* MODE_QUICK */
+ /* This is known to corrupt the Atmel AT24RF08
+ EEPROM */
+ res = i2c_smbus_write_quick(file,
+ I2C_SMBUS_WRITE);
+ break;
+ case MODE_READ:
+ /* This is known to lock SMBus on various
+ write-only chips (mainly clock chips) */
+ res = i2c_smbus_read_byte(file);
+ break;
+ }
+
+ if (res < 0)
+ printf("-- ");
+ else
+ printf("%02x ", i+j);
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+struct func
+{
+ long value;
+ const char* name;
+};
+
+static const struct func all_func[] = {
+ { .value = I2C_FUNC_I2C,
+ .name = "I2C" },
+ { .value = I2C_FUNC_SMBUS_QUICK,
+ .name = "SMBus Quick Command" },
+ { .value = I2C_FUNC_SMBUS_WRITE_BYTE,
+ .name = "SMBus Send Byte" },
+ { .value = I2C_FUNC_SMBUS_READ_BYTE,
+ .name = "SMBus Receive Byte" },
+ { .value = I2C_FUNC_SMBUS_WRITE_BYTE_DATA,
+ .name = "SMBus Write Byte" },
+ { .value = I2C_FUNC_SMBUS_READ_BYTE_DATA,
+ .name = "SMBus Read Byte" },
+ { .value = I2C_FUNC_SMBUS_WRITE_WORD_DATA,
+ .name = "SMBus Write Word" },
+ { .value = I2C_FUNC_SMBUS_READ_WORD_DATA,
+ .name = "SMBus Read Word" },
+ { .value = I2C_FUNC_SMBUS_PROC_CALL,
+ .name = "SMBus Process Call" },
+ { .value = I2C_FUNC_SMBUS_WRITE_BLOCK_DATA,
+ .name = "SMBus Block Write" },
+ { .value = I2C_FUNC_SMBUS_READ_BLOCK_DATA,
+ .name = "SMBus Block Read" },
+ { .value = I2C_FUNC_SMBUS_BLOCK_PROC_CALL,
+ .name = "SMBus Block Process Call" },
+ { .value = I2C_FUNC_SMBUS_PEC,
+ .name = "SMBus PEC" },
+ { .value = I2C_FUNC_SMBUS_WRITE_I2C_BLOCK,
+ .name = "I2C Block Write" },
+ { .value = I2C_FUNC_SMBUS_READ_I2C_BLOCK,
+ .name = "I2C Block Read" },
+ { .value = 0, .name = "" }
+};
+
+static void print_functionality(unsigned long funcs)
+{
+ int i;
+
+ for (i = 0; all_func[i].value; i++) {
+ printf("%-32s %s\n", all_func[i].name,
+ (funcs & all_func[i].value) ? "yes" : "no");
+ }
+}
+
+/*
+ * Print the installed i2c busses. The format is those of Linux 2.4's
+ * /proc/bus/i2c for historical compatibility reasons.
+ */
+static void print_i2c_busses(void)
+{
+ struct i2c_adap *adapters;
+ int count;
+
+ adapters = gather_i2c_busses();
+ if (adapters == NULL) {
+ fprintf(stderr, "Error: Out of memory!\n");
+ return;
+ }
+
+ for (count = 0; adapters[count].name; count++) {
+ printf("i2c-%d\t%-10s\t%-32s\t%s\n",
+ adapters[count].nr, adapters[count].funcs,
+ adapters[count].name, adapters[count].algo);
+ }
+
+ free_adapters(adapters);
+}
+
+int main(int argc, char *argv[])
+{
+ char *end;
+ int i2cbus, file, res;
+ char filename[20];
+ unsigned long funcs;
+ int mode = MODE_AUTO;
+ int first = 0x03, last = 0x77;
+ int flags = 0;
+ int yes = 0, version = 0, list = 0;
+
+ /* handle (optional) flags first */
+ while (1+flags < argc && argv[1+flags][0] == '-') {
+ switch (argv[1+flags][1]) {
+ case 'V': version = 1; break;
+ case 'y': yes = 1; break;
+ case 'l': list = 1; break;
+ case 'F':
+ if (mode != MODE_AUTO && mode != MODE_FUNC) {
+ fprintf(stderr, "Error: Different modes "
+ "specified!\n");
+ exit(1);
+ }
+ mode = MODE_FUNC;
+ break;
+ case 'r':
+ if (mode == MODE_QUICK) {
+ fprintf(stderr, "Error: Different modes "
+ "specified!\n");
+ exit(1);
+ }
+ mode = MODE_READ;
+ break;
+ case 'q':
+ if (mode == MODE_READ) {
+ fprintf(stderr, "Error: Different modes "
+ "specified!\n");
+ exit(1);
+ }
+ mode = MODE_QUICK;
+ break;
+ case 'a':
+ first = 0x00;
+ last = 0x7F;
+ break;
+ default:
+ fprintf(stderr, "Error: Unsupported option "
+ "\"%s\"!\n", argv[1+flags]);
+ help();
+ exit(1);
+ }
+ flags++;
+ }
+
+ if (version) {
+ fprintf(stderr, "i2cdetect version %s\n", VERSION);
+ exit(0);
+ }
+
+ if (list) {
+ print_i2c_busses();
+ exit(0);
+ }
+
+ if (argc < flags + 2) {
+ fprintf(stderr, "Error: No i2c-bus specified!\n");
+ help();
+ exit(1);
+ }
+ i2cbus = lookup_i2c_bus(argv[flags+1]);
+ if (i2cbus < 0) {
+ help();
+ exit(1);
+ }
+
+ /* read address range if present */
+ if (argc == flags + 4 && mode != MODE_FUNC) {
+ int tmp;
+
+ tmp = strtol(argv[flags+2], &end, 0);
+ if (*end) {
+ fprintf(stderr, "Error: FIRST argment not a "
+ "number!\n");
+ help();
+ exit(1);
+ }
+ if (tmp < first || tmp > last) {
+ fprintf(stderr, "Error: FIRST argument out of range "
+ "(0x%02x-0x%02x)!\n", first, last);
+ help();
+ exit(1);
+ }
+ first = tmp;
+
+ tmp = strtol(argv[flags+3], &end, 0);
+ if (*end) {
+ fprintf(stderr, "Error: LAST argment not a "
+ "number!\n");
+ help();
+ exit(1);
+ }
+ if (tmp < first || tmp > last) {
+ fprintf(stderr, "Error: LAST argument out of range "
+ "(0x%02x-0x%02x)!\n", first, last);
+ help();
+ exit(1);
+ }
+ last = tmp;
+ } else if (argc != flags + 2) {
+ help();
+ exit(1);
+ }
+
+ file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0);
+ if (file < 0) {
+ exit(1);
+ }
+
+ if (ioctl(file, I2C_FUNCS, &funcs) < 0) {
+ fprintf(stderr, "Error: Could not get the adapter "
+ "functionality matrix: %s\n", strerror(errno));
+ close(file);
+ exit(1);
+ }
+
+ /* Special case, we only list the implemented functionalities */
+ if (mode == MODE_FUNC) {
+ close(file);
+ printf("Functionalities implemented by %s:\n", filename);
+ print_functionality(funcs);
+ exit(0);
+ }
+
+ if (!(funcs & (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_READ_BYTE))) {
+ fprintf(stderr,
+ "Error: Bus doesn't support detection commands\n");
+ close(file);
+ exit(1);
+ }
+ if (mode == MODE_QUICK && !(funcs & I2C_FUNC_SMBUS_QUICK)) {
+ fprintf(stderr, "Error: Can't use SMBus Quick Write command "
+ "on this bus\n");
+ close(file);
+ exit(1);
+ }
+ if (mode == MODE_READ && !(funcs & I2C_FUNC_SMBUS_READ_BYTE)) {
+ fprintf(stderr, "Error: Can't use SMBus Receive Byte command "
+ "on this bus\n");
+ close(file);
+ exit(1);
+ }
+ if (mode == MODE_AUTO) {
+ if (!(funcs & I2C_FUNC_SMBUS_QUICK))
+ fprintf(stderr, "Warning: Can't use SMBus Quick Write "
+ "command, will skip some addresses\n");
+ if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
+ fprintf(stderr, "Warning: Can't use SMBus Receive Byte "
+ "command, will skip some addresses\n");
+ }
+
+ if (!yes) {
+ char s[2];
+
+ fprintf(stderr, "WARNING! This program can confuse your I2C "
+ "bus, cause data loss and worse!\n");
+
+ fprintf(stderr, "I will probe file %s%s.\n", filename,
+ mode==MODE_QUICK?" using quick write commands":
+ mode==MODE_READ?" using receive byte commands":"");
+ fprintf(stderr, "I will probe address range 0x%02x-0x%02x.\n",
+ first, last);
+
+ fprintf(stderr, "Continue? [Y/n] ");
+ fflush(stderr);
+ if (!fgets(s, 2, stdin)
+ || (s[0] != '\n' && s[0] != 'y' && s[0] != 'Y')) {
+ fprintf(stderr, "Aborting on user request.\n");
+ exit(0);
+ }
+ }
+
+ res = scan_i2c_bus(file, mode, funcs, first, last);
+
+ close(file);
+
+ exit(res?1:0);
+}
diff --git a/tools/i2cdump.8 b/tools/i2cdump.8
new file mode 100644
index 0000000..fb7217e
--- /dev/null
+++ b/tools/i2cdump.8
@@ -0,0 +1,122 @@
+.TH I2CDUMP 8 "October 2017"
+.SH NAME
+i2cdump \- examine I2C registers
+
+.SH SYNOPSIS
+.B i2cdump
+.RB [ -f ]
+.RB [ "-r first-last" ]
+.RB [ -y ]
+.I i2cbus
+.I address
+.RI [ "mode " [ "bank " [ bankreg ]]]
+.br
+.B i2cdump
+.B -V
+
+.SH DESCRIPTION
+i2cdump is a small helper program to examine registers
+visible through the I2C bus.
+
+.SH OPTIONS
+.TP
+.B -V
+Display the version and exit.
+.TP
+.B -f
+Force access to the device even if it is already busy. By default, i2cdump
+will refuse to access a device which is already under the control of a
+kernel driver. Using this flag is dangerous, it can seriously confuse the
+kernel driver in question. It can also cause i2cdump to return invalid
+results. So use at your own risk and only if you know what you're doing.
+.TP
+.B -r first-last
+Limit the range of registers being accessed. This option is only available
+with modes \fBb\fP, \fBw\fP, \fBc\fP and \fBW\fP. For mode \fBW\fP,
+\fBfirst\fR must be even and \fBlast\fR must be odd.
+.TP
+.B -y
+Disable interactive mode. By default, i2cdump will wait for a confirmation
+from the user before messing with the I2C bus. When this flag is used, it
+will perform the operation directly. This is mainly meant to be used in
+scripts.
+.PP
+At least two options must be provided to i2cdump. \fIi2cbus\fR indicates the
+number or name of the I2C bus to be scanned. This number should correspond to one
+of the busses listed by \fIi2cdetect -l\fR. \fIaddress\fR indicates the
+address to be scanned on that bus, and is an integer between 0x03 and 0x77.
+.PP
+The \fImode\fR parameter, if specified, is one of the letters \fBb\fP, \fBw\fP,
+\fBs\fP, or \fBi\fP, corresponding to a read size of a single byte, a 16-bit
+word, an SMBus block, an I2C block, respectively. The \fBc\fP mode is a
+little different, it reads all bytes consecutively, and is useful for chips that
+have an address auto-increment feature, such as EEPROMs. The \fBW\fP mode is
+also special, it is similar to \fBw\fP except that a read command will only
+be issued on even register addresses; this is again mainly useful for EEPROMs.
+.PP
+A \fBp\fP can also be appended to the \fImode\fR parameter (except for
+\fBi\fP and \fBW\fP) to enable PEC. If the \fImode\fR parameter is omitted,
+i2cdump defaults to byte access without PEC.
+.PP
+The \fIbank\fR and \fIbankreg\fR parameters are useful on the W83781D and
+similar chips (at the time of writing, all Winbond and Asus chips).
+\fIbank\fR is an integer between 0 and 7, and \fIbankreg\fR is an integer
+between 0x00 and 0xFF (default value: 0x4E). The W83781D data sheet has more
+information on bank selection.
+
+.SH WARNING
+i2cdump can be dangerous if used improperly. Most notably, the \fBc\fP mode
+starts with WRITING a byte to the chip. On most chips it will be stored in the
+address pointer register, which is OK, but some chips with a single register
+or no (visible) register at all will most likely see this as a real WRITE,
+resulting in possible misbehavior or corruption. Do not use i2cdump
+on random addresses. Anyway, it is of little use unless you have good
+knowledge of the chip you're working with and an idea of what you are looking
+for.
+
+.SH EXAMPLES
+.PP
+Dump the whole contents of I2C device at 7-bit address 0x50 on bus 9
+(i2c-9), using the default read method (byte mode), after user confirmation:
+.nf
+.RS
+# i2cdump 9 0x50
+.RE
+.fi
+.PP
+Immediately dump the whole contents of I2C device at 7-bit address 0x50 on
+bus 9 (i2c-9), using I2C block read transactions (no user confirmation):
+.nf
+.RS
+# i2cdump -y 9 0x50 i
+.RE
+.fi
+If the device is an EEPROM, the output would typically be the same as output
+of the previous example.
+.PP
+Dump registers 0x00 to 0x3f of the I2C device at 7-bit address 0x2d on
+bus 1 (i2c-1), using the default read method (byte mode), after user
+confirmation:
+.nf
+.RS
+# i2cdump -r 0x00-0x3f 1 0x2d
+.RE
+.fi
+.PP
+Dump the registers of the SMBus device at address 0x69 on bus 0 (i2c-0),
+using one SMBus block read transaction with error checking enabled, after
+user confirmation:
+.nf
+.RS
+# i2cdump 0 0x69 sp
+.RE
+.fi
+
+.SH SEE ALSO
+i2cdetect(8), i2cget(8), i2cset(8), i2ctransfer(8), isadump(8)
+
+.SH AUTHOR
+Frodo Looijaard, Mark D. Studebaker and Jean Delvare
+
+This manual page was originally written by David Z Maze <dmaze@debian.org> for
+the Debian GNU/Linux system.
diff --git a/tools/i2cdump.c b/tools/i2cdump.c
new file mode 100644
index 0000000..a7bba72
--- /dev/null
+++ b/tools/i2cdump.c
@@ -0,0 +1,490 @@
+/*
+ i2cdump.c - a user-space program to dump I2C registers
+ Copyright (C) 2002-2003 Frodo Looijaard <frodol@dds.nl>, and
+ Mark D. Studebaker <mdsxyz123@yahoo.com>
+ Copyright (C) 2004-2012 Jean Delvare <jdelvare@suse.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <i2c/smbus.h>
+#include "i2cbusses.h"
+#include "util.h"
+#include "../version.h"
+
+static void help(void)
+{
+ fprintf(stderr,
+ "Usage: i2cdump [-f] [-y] [-r first-last] I2CBUS ADDRESS [MODE [BANK [BANKREG]]]\n"
+ " I2CBUS is an integer or an I2C bus name\n"
+ " ADDRESS is an integer (0x03 - 0x77)\n"
+ " MODE is one of:\n"
+ " b (byte, default)\n"
+ " w (word)\n"
+ " W (word on even register addresses)\n"
+ " s (SMBus block)\n"
+ " i (I2C block)\n"
+ " c (consecutive byte)\n"
+ " Append p for SMBus PEC\n");
+}
+
+static int check_funcs(int file, int size, int pec)
+{
+ unsigned long funcs;
+
+ /* check adapter functionality */
+ if (ioctl(file, I2C_FUNCS, &funcs) < 0) {
+ fprintf(stderr, "Error: Could not get the adapter "
+ "functionality matrix: %s\n", strerror(errno));
+ return -1;
+ }
+
+ switch(size) {
+ case I2C_SMBUS_BYTE:
+ if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "SMBus receive byte");
+ return -1;
+ }
+ if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "SMBus send byte");
+ return -1;
+ }
+ break;
+
+ case I2C_SMBUS_BYTE_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "SMBus read byte");
+ return -1;
+ }
+ break;
+
+ case I2C_SMBUS_WORD_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "SMBus read word");
+ return -1;
+ }
+ break;
+
+ case I2C_SMBUS_BLOCK_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "SMBus block read");
+ return -1;
+ }
+ break;
+
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "I2C block read");
+ return -1;
+ }
+ break;
+ }
+
+ if (pec
+ && !(funcs & (I2C_FUNC_SMBUS_PEC | I2C_FUNC_I2C))) {
+ fprintf(stderr, "Warning: Adapter does "
+ "not seem to support PEC\n");
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char *end;
+ int i, j, res, i2cbus, address, size, file;
+ int bank = 0, bankreg = 0x4E, old_bank = 0;
+ char filename[20];
+ int block[256], s_length = 0;
+ int pec = 0, even = 0;
+ int flags = 0;
+ int force = 0, yes = 0, version = 0;
+ const char *range = NULL;
+ int first = 0x00, last = 0xff;
+
+ /* handle (optional) flags first */
+ while (1+flags < argc && argv[1+flags][0] == '-') {
+ switch (argv[1+flags][1]) {
+ case 'V': version = 1; break;
+ case 'f': force = 1; break;
+ case 'r': range = argv[1+(++flags)]; break;
+ case 'y': yes = 1; break;
+ default:
+ fprintf(stderr, "Error: Unsupported option "
+ "\"%s\"!\n", argv[1+flags]);
+ help();
+ exit(1);
+ }
+ flags++;
+ }
+
+ if (version) {
+ fprintf(stderr, "i2cdump version %s\n", VERSION);
+ exit(0);
+ }
+
+ if (argc < flags + 2) {
+ fprintf(stderr, "Error: No i2c-bus specified!\n");
+ help();
+ exit(1);
+ }
+ i2cbus = lookup_i2c_bus(argv[flags+1]);
+ if (i2cbus < 0) {
+ help();
+ exit(1);
+ }
+
+ if (argc < flags + 3) {
+ fprintf(stderr, "Error: No address specified!\n");
+ help();
+ exit(1);
+ }
+ address = parse_i2c_address(argv[flags+2]);
+ if (address < 0) {
+ help();
+ exit(1);
+ }
+
+ if (argc < flags + 4) {
+ fprintf(stderr, "No size specified (using byte-data access)\n");
+ size = I2C_SMBUS_BYTE_DATA;
+ } else if (!strncmp(argv[flags+3], "b", 1)) {
+ size = I2C_SMBUS_BYTE_DATA;
+ pec = argv[flags+3][1] == 'p';
+ } else if (!strncmp(argv[flags+3], "w", 1)) {
+ size = I2C_SMBUS_WORD_DATA;
+ pec = argv[flags+3][1] == 'p';
+ } else if (!strncmp(argv[flags+3], "W", 1)) {
+ size = I2C_SMBUS_WORD_DATA;
+ even = 1;
+ } else if (!strncmp(argv[flags+3], "s", 1)) {
+ size = I2C_SMBUS_BLOCK_DATA;
+ pec = argv[flags+3][1] == 'p';
+ } else if (!strncmp(argv[flags+3], "c", 1)) {
+ size = I2C_SMBUS_BYTE;
+ pec = argv[flags+3][1] == 'p';
+ } else if (!strcmp(argv[flags+3], "i"))
+ size = I2C_SMBUS_I2C_BLOCK_DATA;
+ else {
+ fprintf(stderr, "Error: Invalid mode!\n");
+ help();
+ exit(1);
+ }
+
+ if (argc > flags + 4) {
+ bank = strtol(argv[flags+4], &end, 0);
+ if (*end || size == I2C_SMBUS_I2C_BLOCK_DATA) {
+ fprintf(stderr, "Error: Invalid bank number!\n");
+ help();
+ exit(1);
+ }
+ if ((size == I2C_SMBUS_BYTE_DATA || size == I2C_SMBUS_WORD_DATA)
+ && (bank < 0 || bank > 15)) {
+ fprintf(stderr, "Error: bank out of range!\n");
+ help();
+ exit(1);
+ }
+ if (size == I2C_SMBUS_BLOCK_DATA
+ && (bank < 0 || bank > 0xff)) {
+ fprintf(stderr, "Error: block command out of range!\n");
+ help();
+ exit(1);
+ }
+
+ if (argc > flags + 5) {
+ bankreg = strtol(argv[flags+5], &end, 0);
+ if (*end || size == I2C_SMBUS_BLOCK_DATA) {
+ fprintf(stderr, "Error: Invalid bank register "
+ "number!\n");
+ help();
+ exit(1);
+ }
+ if (bankreg < 0 || bankreg > 0xff) {
+ fprintf(stderr, "Error: bank out of range "
+ "(0-0xff)!\n");
+ help();
+ exit(1);
+ }
+ }
+ }
+
+ /* Parse optional range string */
+ if (range) {
+ char *dash;
+
+ first = strtol(range, &dash, 0);
+ if (dash == range || *dash != '-'
+ || first < 0 || first > 0xff) {
+ fprintf(stderr, "Error: Invalid range parameter!\n");
+ exit(1);
+ }
+ last = strtol(++dash, &end, 0);
+ if (end == dash || *end != '\0'
+ || last < first || last > 0xff) {
+ fprintf(stderr, "Error: Invalid range parameter!\n");
+ exit(1);
+ }
+
+ /* Check mode constraints */
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ case I2C_SMBUS_BYTE_DATA:
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ if (!even || (!(first%2) && last%2))
+ break;
+ /* Fall through */
+ default:
+ fprintf(stderr,
+ "Error: Range parameter not compatible with selected mode!\n");
+ exit(1);
+ }
+ }
+
+ file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0);
+ if (file < 0
+ || check_funcs(file, size, pec)
+ || set_slave_addr(file, address, force))
+ exit(1);
+
+ if (pec) {
+ if (ioctl(file, I2C_PEC, 1) < 0) {
+ fprintf(stderr, "Error: Could not set PEC: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+ }
+
+ if (!yes) {
+ fprintf(stderr, "WARNING! This program can confuse your I2C "
+ "bus, cause data loss and worse!\n");
+
+ fprintf(stderr, "I will probe file %s, address 0x%x, mode "
+ "%s\n", filename, address,
+ size == I2C_SMBUS_BLOCK_DATA ? "smbus block" :
+ size == I2C_SMBUS_I2C_BLOCK_DATA ? "i2c block" :
+ size == I2C_SMBUS_BYTE ? "byte consecutive read" :
+ size == I2C_SMBUS_BYTE_DATA ? "byte" : "word");
+ if (pec)
+ fprintf(stderr, "PEC checking enabled.\n");
+ if (even)
+ fprintf(stderr, "Only probing even register "
+ "addresses.\n");
+ if (bank) {
+ if (size == I2C_SMBUS_BLOCK_DATA)
+ fprintf(stderr, "Using command 0x%02x.\n",
+ bank);
+ else
+ fprintf(stderr, "Probing bank %d using bank "
+ "register 0x%02x.\n", bank, bankreg);
+ }
+ if (range) {
+ fprintf(stderr,
+ "Probe range limited to 0x%02x-0x%02x.\n",
+ first, last);
+ }
+
+ fprintf(stderr, "Continue? [Y/n] ");
+ fflush(stderr);
+ if (!user_ack(1)) {
+ fprintf(stderr, "Aborting on user request.\n");
+ exit(0);
+ }
+ }
+
+ /* See Winbond w83781d data sheet for bank details */
+ if (bank && size != I2C_SMBUS_BLOCK_DATA) {
+ res = i2c_smbus_read_byte_data(file, bankreg);
+ if (res >= 0) {
+ old_bank = res;
+ res = i2c_smbus_write_byte_data(file, bankreg,
+ bank | (old_bank & 0xf0));
+ }
+ if (res < 0) {
+ fprintf(stderr, "Error: Bank switching failed\n");
+ exit(1);
+ }
+ }
+
+ /* handle all but word data */
+ if (size != I2C_SMBUS_WORD_DATA || even) {
+ /* do the block transaction */
+ if (size == I2C_SMBUS_BLOCK_DATA
+ || size == I2C_SMBUS_I2C_BLOCK_DATA) {
+ unsigned char cblock[288];
+
+ if (size == I2C_SMBUS_BLOCK_DATA) {
+ res = i2c_smbus_read_block_data(file, bank,
+ cblock);
+ /* Remember returned block length for a nicer
+ display later */
+ s_length = res;
+ } else {
+ for (res = 0; res < 256; res += i) {
+ i = i2c_smbus_read_i2c_block_data(file,
+ res, 32, cblock + res);
+ if (i <= 0) {
+ res = i;
+ break;
+ }
+ }
+ }
+ if (res <= 0) {
+ fprintf(stderr, "Error: Block read failed, "
+ "return code %d\n", res);
+ exit(1);
+ }
+ if (res >= 256)
+ res = 256;
+ for (i = 0; i < res; i++)
+ block[i] = cblock[i];
+ if (size != I2C_SMBUS_BLOCK_DATA)
+ for (i = res; i < 256; i++)
+ block[i] = -1;
+ }
+
+ if (size == I2C_SMBUS_BYTE) {
+ res = i2c_smbus_write_byte(file, first);
+ if(res != 0) {
+ fprintf(stderr, "Error: Write start address "
+ "failed, return code %d\n", res);
+ exit(1);
+ }
+ }
+
+ printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f"
+ " 0123456789abcdef\n");
+ for (i = 0; i < 256; i+=16) {
+ if (size == I2C_SMBUS_BLOCK_DATA && i >= s_length)
+ break;
+ if (i/16 < first/16)
+ continue;
+ if (i/16 > last/16)
+ break;
+
+ printf("%02x: ", i);
+ for (j = 0; j < 16; j++) {
+ fflush(stdout);
+ /* Skip unwanted registers */
+ if (i+j < first || i+j > last) {
+ printf(" ");
+ if (size == I2C_SMBUS_WORD_DATA) {
+ printf(" ");
+ j++;
+ }
+ continue;
+ }
+
+ if (size == I2C_SMBUS_BYTE_DATA) {
+ block[i+j] = res =
+ i2c_smbus_read_byte_data(file, i+j);
+ } else if (size == I2C_SMBUS_WORD_DATA) {
+ res = i2c_smbus_read_word_data(file,
+ i+j);
+ if (res < 0) {
+ block[i+j] = res;
+ block[i+j+1] = res;
+ } else {
+ block[i+j] = res & 0xff;
+ block[i+j+1] = res >> 8;
+ }
+ } else if (size == I2C_SMBUS_BYTE) {
+ block[i+j] = res =
+ i2c_smbus_read_byte(file);
+ } else
+ res = block[i+j];
+
+ if (size == I2C_SMBUS_BLOCK_DATA
+ && i+j >= s_length) {
+ printf(" ");
+ } else if (res < 0) {
+ printf("XX ");
+ if (size == I2C_SMBUS_WORD_DATA)
+ printf("XX ");
+ } else {
+ printf("%02x ", block[i+j]);
+ if (size == I2C_SMBUS_WORD_DATA)
+ printf("%02x ", block[i+j+1]);
+ }
+ if (size == I2C_SMBUS_WORD_DATA)
+ j++;
+ }
+ printf(" ");
+
+ for (j = 0; j < 16; j++) {
+ if (size == I2C_SMBUS_BLOCK_DATA
+ && i+j >= s_length)
+ break;
+ /* Skip unwanted registers */
+ if (i+j < first || i+j > last) {
+ printf(" ");
+ continue;
+ }
+
+ res = block[i+j];
+ if (res < 0)
+ printf("X");
+ else
+ if ((res & 0xff) == 0x00
+ || (res & 0xff) == 0xff)
+ printf(".");
+ else
+ if ((res & 0xff) < 32
+ || (res & 0xff) >= 127)
+ printf("?");
+ else
+ printf("%c", res & 0xff);
+ }
+ printf("\n");
+ }
+ } else {
+ printf(" 0,8 1,9 2,a 3,b 4,c 5,d 6,e 7,f\n");
+ for (i = 0; i < 256; i+=8) {
+ if (i/8 < first/8)
+ continue;
+ if (i/8 > last/8)
+ break;
+
+ printf("%02x: ", i);
+ for (j = 0; j < 8; j++) {
+ /* Skip unwanted registers */
+ if (i+j < first || i+j > last) {
+ printf(" ");
+ continue;
+ }
+
+ res = i2c_smbus_read_word_data(file, i+j);
+ if (res < 0)
+ printf("XXXX ");
+ else
+ printf("%04x ", res & 0xffff);
+ }
+ printf("\n");
+ }
+ }
+ if (bank && size != I2C_SMBUS_BLOCK_DATA) {
+ i2c_smbus_write_byte_data(file, bankreg, old_bank);
+ }
+ exit(0);
+}
diff --git a/tools/i2cget.8 b/tools/i2cget.8
new file mode 100644
index 0000000..a1a1276
--- /dev/null
+++ b/tools/i2cget.8
@@ -0,0 +1,119 @@
+.TH I2CGET 8 "October 2017"
+.SH "NAME"
+i2cget \- read from I2C/SMBus chip registers
+
+.SH SYNOPSIS
+.B i2cget
+.RB [ -f ]
+.RB [ -y ]
+.I i2cbus
+.I chip-address
+.RI [ "data-address " [ mode ]]
+.br
+.B i2cget
+.B -V
+
+.SH DESCRIPTION
+i2cget is a small helper program to read registers visible through the I2C
+bus (or SMBus).
+
+.SH OPTIONS
+.TP
+.B -V
+Display the version and exit.
+.TP
+.B -f
+Force access to the device even if it is already busy. By default, i2cget
+will refuse to access a device which is already under the control of a
+kernel driver. Using this flag is dangerous, it can seriously confuse the
+kernel driver in question. It can also cause i2cget to return an invalid
+value. So use at your own risk and only if you know what you're doing.
+.TP
+.B -y
+Disable interactive mode. By default, i2cget will wait for a confirmation
+from the user before messing with the I2C bus. When this flag is used, it
+will perform the operation directly. This is mainly meant to be used in
+scripts. Use with caution.
+.PP
+There are two required options to i2cget. \fIi2cbus\fR indicates the number
+or name of the I2C bus to be scanned. This number should correspond to one of
+the busses listed by \fIi2cdetect -l\fR. \fIchip-address\fR specifies the
+address of the chip on that bus, and is an integer between 0x03 and 0x77.
+.PP
+\fIdata-address\fR specifies the address on that chip to read from, and is
+an integer between 0x00 and 0xFF. If omitted, the currently active register
+will be read (if that makes sense for the considered chip).
+.PP
+The \fImode\fR parameter, if specified, is one of the letters \fBb\fP,
+\fBw\fP or \fBc\fP, corresponding to a read byte data, a read word data or a
+write byte/read byte transaction, respectively. A \fBp\fP can also be appended
+to the \fImode\fR parameter to enable PEC. If the \fImode\fR parameter is omitted,
+i2cget defaults to a read byte data transaction, unless \fIdata-address\fR is
+also omitted, in which case the default (and only valid) transaction is a
+single read byte.
+
+.SH WARNING
+i2cget can be extremely dangerous if used improperly. I2C and SMBus are designed
+in such a way that an SMBus read transaction can be seen as a write transaction by
+certain chips. This is particularly true if setting \fImode\fR to \fBcp\fP (write byte/read
+byte with PEC). Be extremely careful using this program.
+
+.SH EXAMPLES
+.PP
+Get the value of 8-bit register 0x11 of the I2C device at 7-bit address 0x2d
+on bus 1 (i2c-1), after user confirmation:
+.nf
+.RS
+# i2cget 1 0x2d 0x11
+.RE
+.fi
+.PP
+Get the value of 16-bit register 0x00 of the I2C device at 7-bit address 0x48
+on bus 1 (i2c-1), after user confirmation:
+.nf
+.RS
+# i2cget 1 0x48 0x00 w
+.RE
+.fi
+.PP
+Set the internal pointer register of a 24C02 EEPROM at 7-bit address 0x50
+on bus 9 (i2c-9) to 0x00, then read the first 2 bytes from that EEPROM:
+.nf
+.RS
+# i2cset -y 9 0x50 0x00 ; i2cget -y 9 0x50 ; i2cget -y 9 0x50
+.RE
+.fi
+This assumes that the device automatically increments its internal pointer
+register on every read, and supports read byte transactions (read without
+specifying the register address, "Receive Byte" in SMBus terminology.)
+Most EEPROM devices behave that way. Note that this is only safe as long as
+nobody else is accessing the I2C device at the same time. A safer approach
+would be to use a "Read Word" SMBus transaction instead, or an I2C Block
+Read transaction to read more than 2 bytes.
+.PP
+Set the internal pointer register of a 24C32 EEPROM at 7-bit address 0x53
+on bus 9 (i2c-9) to 0x0000, then read the first 2 bytes from that EEPROM:
+.nf
+.RS
+# i2cset -y 9 0x53 0x00 0x00 ; i2cget -y 9 0x53 ; i2cget -y 9 0x53
+.RE
+.fi
+This again assumes that the device automatically increments its internal
+pointer register on every read, and supports read byte transactions. While
+the previous example was for a small EEPROM using 8-bit internal addressing,
+this example is for a larger EEPROM using 16-bit internal addressing. Beware
+that running this command on a small EEPROM using 8-bit internal addressing
+would actually \fIwrite\fR 0x00 to the first byte of that EEPROM. The safety
+concerns raised above still stand, however in this case there is no SMBus
+equivalent, so this is the only way to read data from a large EEPROM if your
+master isn't fully I2C capable. With a fully I2C capable master, you would
+use \fIi2ctransfer\fR to achieve the same in a safe and faster way.
+
+.SH SEE ALSO
+i2cdetect(8), i2cdump(8), i2cset(8), i2ctransfer(8)
+
+.SH AUTHOR
+Jean Delvare
+
+This manual page was strongly inspired from those written by David Z Maze
+for i2cset.
diff --git a/tools/i2cget.c b/tools/i2cget.c
new file mode 100644
index 0000000..2503942
--- /dev/null
+++ b/tools/i2cget.c
@@ -0,0 +1,259 @@
+/*
+ i2cget.c - A user-space program to read an I2C register.
+ Copyright (C) 2005-2012 Jean Delvare <jdelvare@suse.de>
+
+ Based on i2cset.c:
+ Copyright (C) 2001-2003 Frodo Looijaard <frodol@dds.nl>, and
+ Mark D. Studebaker <mdsxyz123@yahoo.com>
+ Copyright (C) 2004-2005 Jean Delvare
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <i2c/smbus.h>
+#include "i2cbusses.h"
+#include "util.h"
+#include "../version.h"
+
+static void help(void) __attribute__ ((noreturn));
+
+static void help(void)
+{
+ fprintf(stderr,
+ "Usage: i2cget [-f] [-y] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]\n"
+ " I2CBUS is an integer or an I2C bus name\n"
+ " ADDRESS is an integer (0x03 - 0x77)\n"
+ " MODE is one of:\n"
+ " b (read byte data, default)\n"
+ " w (read word data)\n"
+ " c (write byte/read byte)\n"
+ " Append p for SMBus PEC\n");
+ exit(1);
+}
+
+static int check_funcs(int file, int size, int daddress, int pec)
+{
+ unsigned long funcs;
+
+ /* check adapter functionality */
+ if (ioctl(file, I2C_FUNCS, &funcs) < 0) {
+ fprintf(stderr, "Error: Could not get the adapter "
+ "functionality matrix: %s\n", strerror(errno));
+ return -1;
+ }
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "SMBus receive byte");
+ return -1;
+ }
+ if (daddress >= 0
+ && !(funcs & I2C_FUNC_SMBUS_WRITE_BYTE)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "SMBus send byte");
+ return -1;
+ }
+ break;
+
+ case I2C_SMBUS_BYTE_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "SMBus read byte");
+ return -1;
+ }
+ break;
+
+ case I2C_SMBUS_WORD_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "SMBus read word");
+ return -1;
+ }
+ break;
+ }
+
+ if (pec
+ && !(funcs & (I2C_FUNC_SMBUS_PEC | I2C_FUNC_I2C))) {
+ fprintf(stderr, "Warning: Adapter does "
+ "not seem to support PEC\n");
+ }
+
+ return 0;
+}
+
+static int confirm(const char *filename, int address, int size, int daddress,
+ int pec)
+{
+ int dont = 0;
+
+ fprintf(stderr, "WARNING! This program can confuse your I2C "
+ "bus, cause data loss and worse!\n");
+
+ /* Don't let the user break his/her EEPROMs */
+ if (address >= 0x50 && address <= 0x57 && pec) {
+ fprintf(stderr, "STOP! EEPROMs are I2C devices, not "
+ "SMBus devices. Using PEC\non I2C devices may "
+ "result in unexpected results, such as\n"
+ "trashing the contents of EEPROMs. We can't "
+ "let you do that, sorry.\n");
+ return 0;
+ }
+
+ if (size == I2C_SMBUS_BYTE && daddress >= 0 && pec) {
+ fprintf(stderr, "WARNING! All I2C chips and some SMBus chips "
+ "will interpret a write\nbyte command with PEC as a"
+ "write byte data command, effectively writing a\n"
+ "value into a register!\n");
+ dont++;
+ }
+
+ fprintf(stderr, "I will read from device file %s, chip "
+ "address 0x%02x, ", filename, address);
+ if (daddress < 0)
+ fprintf(stderr, "current data\naddress");
+ else
+ fprintf(stderr, "data address\n0x%02x", daddress);
+ fprintf(stderr, ", using %s.\n",
+ size == I2C_SMBUS_BYTE ? (daddress < 0 ?
+ "read byte" : "write byte/read byte") :
+ size == I2C_SMBUS_BYTE_DATA ? "read byte data" :
+ "read word data");
+ if (pec)
+ fprintf(stderr, "PEC checking enabled.\n");
+
+ fprintf(stderr, "Continue? [%s] ", dont ? "y/N" : "Y/n");
+ fflush(stderr);
+ if (!user_ack(!dont)) {
+ fprintf(stderr, "Aborting on user request.\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ char *end;
+ int res, i2cbus, address, size, file;
+ int daddress;
+ char filename[20];
+ int pec = 0;
+ int flags = 0;
+ int force = 0, yes = 0, version = 0;
+
+ /* handle (optional) flags first */
+ while (1+flags < argc && argv[1+flags][0] == '-') {
+ switch (argv[1+flags][1]) {
+ case 'V': version = 1; break;
+ case 'f': force = 1; break;
+ case 'y': yes = 1; break;
+ default:
+ fprintf(stderr, "Error: Unsupported option "
+ "\"%s\"!\n", argv[1+flags]);
+ help();
+ exit(1);
+ }
+ flags++;
+ }
+
+ if (version) {
+ fprintf(stderr, "i2cget version %s\n", VERSION);
+ exit(0);
+ }
+
+ if (argc < flags + 3)
+ help();
+
+ i2cbus = lookup_i2c_bus(argv[flags+1]);
+ if (i2cbus < 0)
+ help();
+
+ address = parse_i2c_address(argv[flags+2]);
+ if (address < 0)
+ help();
+
+ if (argc > flags + 3) {
+ size = I2C_SMBUS_BYTE_DATA;
+ daddress = strtol(argv[flags+3], &end, 0);
+ if (*end || daddress < 0 || daddress > 0xff) {
+ fprintf(stderr, "Error: Data address invalid!\n");
+ help();
+ }
+ } else {
+ size = I2C_SMBUS_BYTE;
+ daddress = -1;
+ }
+
+ if (argc > flags + 4) {
+ switch (argv[flags+4][0]) {
+ case 'b': size = I2C_SMBUS_BYTE_DATA; break;
+ case 'w': size = I2C_SMBUS_WORD_DATA; break;
+ case 'c': size = I2C_SMBUS_BYTE; break;
+ default:
+ fprintf(stderr, "Error: Invalid mode!\n");
+ help();
+ }
+ pec = argv[flags+4][1] == 'p';
+ }
+
+ file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0);
+ if (file < 0
+ || check_funcs(file, size, daddress, pec)
+ || set_slave_addr(file, address, force))
+ exit(1);
+
+ if (!yes && !confirm(filename, address, size, daddress, pec))
+ exit(0);
+
+ if (pec && ioctl(file, I2C_PEC, 1) < 0) {
+ fprintf(stderr, "Error: Could not set PEC: %s\n",
+ strerror(errno));
+ close(file);
+ exit(1);
+ }
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ if (daddress >= 0) {
+ res = i2c_smbus_write_byte(file, daddress);
+ if (res < 0)
+ fprintf(stderr, "Warning - write failed\n");
+ }
+ res = i2c_smbus_read_byte(file);
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ res = i2c_smbus_read_word_data(file, daddress);
+ break;
+ default: /* I2C_SMBUS_BYTE_DATA */
+ res = i2c_smbus_read_byte_data(file, daddress);
+ }
+ close(file);
+
+ if (res < 0) {
+ fprintf(stderr, "Error: Read failed\n");
+ exit(2);
+ }
+
+ printf("0x%0*x\n", size == I2C_SMBUS_WORD_DATA ? 4 : 2, res);
+
+ exit(0);
+}
diff --git a/tools/i2cset.8 b/tools/i2cset.8
new file mode 100644
index 0000000..19887bd
--- /dev/null
+++ b/tools/i2cset.8
@@ -0,0 +1,131 @@
+.TH I2CSET 8 "October 2017"
+.SH "NAME"
+i2cset \- set I2C registers
+
+.SH SYNOPSIS
+.B i2cset
+.RB [ -f ]
+.RB [ -y ]
+.RB [ "-m mask" ]
+.RB [ -r ]
+.I i2cbus
+.I chip-address
+.I data-address
+.RI [ value ]
+.RI ...
+.RI [ mode ]
+.br
+.B i2cset
+.B -V
+
+.SH DESCRIPTION
+i2cset is a small helper program to set registers visible through the I2C
+bus.
+
+.SH OPTIONS
+.TP
+.B -V
+Display the version and exit.
+.TP
+.B -f
+Force access to the device even if it is already busy. By default, i2cset
+will refuse to access a device which is already under the control of a
+kernel driver. Using this flag is dangerous, it can seriously confuse the
+kernel driver in question. It can also cause i2cset to silently write to
+the wrong register. So use at your own risk and only if you know what
+you're doing.
+.TP
+.B -y
+Disable interactive mode. By default, i2cset will wait for a confirmation
+from the user before messing with the I2C bus. When this flag is used, it
+will perform the operation directly. This is mainly meant to be used in
+scripts.
+.TP
+.B -m mask
+The \fImask\fR parameter, if specified, describes which bits of \fIvalue\fR
+will be actually written to \fIdata-address\fR. Bits set to 1 in the mask
+are taken from \fIvalue\fR, while bits set to 0 will be read from
+\fIdata-address\fR and thus preserved by the operation. Please note that
+this parameter assumes that the read and write operations for the specified
+mode are symmetrical for the device you are accessing. This may or may not
+be the case, as neither I2C nor SMBus guarantees this.
+.TP
+.B -r
+Read back the value right after writing it, and compare the result with the
+value written. This used to be the default behavior. The same limitations
+apply as those of option \fB-m\fR.
+.PP
+There are three required options to i2cset. \fIi2cbus\fR indicates the number
+or name of the I2C bus to be scanned. This number should correspond to one of
+the busses listed by \fIi2cdetect -l\fR. \fIchip-address\fR specifies the
+address of the chip on that bus, and is an integer between 0x03 and 0x77.
+\fIdata-address\fR specifies the address on that chip to write to, and is an
+integer between 0x00 and 0xFF.
+.PP
+The \fIvalue\fR parameter, if specified, is the value to write to that
+location on the chip. If this parameter is omitted, then a short write is
+issued. For most chips, it simply sets an internal pointer to the target
+location, but doesn't actually write to that location. For a few chips
+though, in particular simple ones with a single register, this short write
+is an actual write. If the mode parameter is \fBs\fP or \fBi\fP, multiple
+values can be specified.
+.PP
+The \fImode\fR parameter, if specified, is one of the letters \fBb\fP,
+\fBw\fP, \fBs\fP, or \fBi\fP, corresponding to a write size of a single byte,
+a 16-bit word, a SMBus block write, or an I2C block write, respectively.
+For SMBus and I2C block writes, the write size is determined by the number
+of \fIvalue\fR parameters.
+Except for I2C block writes, a \fBp\fP can also be appended to the \fImode\fR
+parameter to enable PEC.
+If the \fImode\fR parameter is omitted, i2cset defaults to byte
+mode without PEC. The \fIvalue\fR provided must be within range for the
+specified data type (0x00-0xFF for byte and block writes, 0x0000-0xFFFF
+for words).
+Another possible mode is \fBc\fP, which doesn't write any value (so-called
+short write). You usually don't have to specify this mode, as it is the
+default when no value is provided, unless you also want to enable PEC.
+
+.SH WARNING
+i2cset can be extremely dangerous if used improperly. It can confuse your
+I2C bus, cause data loss, or have more serious side effects. Writing to
+a serial EEPROM on a memory DIMM (chip addresses between 0x50 and 0x57) may
+DESTROY your memory, leaving your system unbootable! Be extremely careful
+using this program.
+
+.SH EXAMPLES
+.PP
+Write value 0x42 to 8-bit register 0x11 of the I2C device at 7-bit
+address 0x2d on bus 1 (i2c-1), after user confirmation:
+.nf
+.RS
+# i2cset 1 0x2d 0x11 0x42
+.RE
+.fi
+.PP
+Immediately clear the 3 least significant bits of 8-bit register 0x11 of the
+I2C device at 7-bit address 0x2d on bus 1 (i2c-1) (no user confirmation):
+.nf
+.RS
+# i2cset -y -m 0x07 1 0x2d 0x11 0x00
+.RE
+.fi
+.PP
+Write value 0x5000 to 16-bit register 0x02 of the I2C device at 7-bit
+address 0x48 on bus 1 (i2c-1), after user confirmation:
+.nf
+.RS
+# i2cset 1 0x48 0x02 0x5000 w
+.RE
+.fi
+.PP
+Also see i2cget(8) for examples of combined usage of \fIi2cset\fR and
+\fIi2cget\fR.
+
+.SH SEE ALSO
+i2cdetect(8), i2cdump(8), i2cget(8), i2ctransfer(8), isaset(8)
+
+.SH AUTHOR
+Frodo Looijaard, Mark D. Studebaker and Jean Delvare
+
+This manual page was originally written by David Z Maze <dmaze@debian.org> for
+the Debian GNU/Linux system.
diff --git a/tools/i2cset.c b/tools/i2cset.c
new file mode 100644
index 0000000..0ccc1a0
--- /dev/null
+++ b/tools/i2cset.c
@@ -0,0 +1,433 @@
+/*
+ i2cset.c - A user-space program to write an I2C register.
+ Copyright (C) 2001-2003 Frodo Looijaard <frodol@dds.nl>, and
+ Mark D. Studebaker <mdsxyz123@yahoo.com>
+ Copyright (C) 2004-2012 Jean Delvare <jdelvare@suse.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <i2c/smbus.h>
+#include "i2cbusses.h"
+#include "util.h"
+#include "../version.h"
+
+static void help(void) __attribute__ ((noreturn));
+
+static void help(void)
+{
+ fprintf(stderr,
+ "Usage: i2cset [-f] [-y] [-m MASK] [-r] I2CBUS CHIP-ADDRESS DATA-ADDRESS [VALUE] ... [MODE]\n"
+ " I2CBUS is an integer or an I2C bus name\n"
+ " ADDRESS is an integer (0x03 - 0x77)\n"
+ " MODE is one of:\n"
+ " c (byte, no value)\n"
+ " b (byte data, default)\n"
+ " w (word data)\n"
+ " i (I2C block data)\n"
+ " s (SMBus block data)\n"
+ " Append p for SMBus PEC\n");
+ exit(1);
+}
+
+static int check_funcs(int file, int size, int pec)
+{
+ unsigned long funcs;
+
+ /* check adapter functionality */
+ if (ioctl(file, I2C_FUNCS, &funcs) < 0) {
+ fprintf(stderr, "Error: Could not get the adapter "
+ "functionality matrix: %s\n", strerror(errno));
+ return -1;
+ }
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "SMBus send byte");
+ return -1;
+ }
+ break;
+
+ case I2C_SMBUS_BYTE_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "SMBus write byte");
+ return -1;
+ }
+ break;
+
+ case I2C_SMBUS_WORD_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "SMBus write word");
+ return -1;
+ }
+ break;
+
+ case I2C_SMBUS_BLOCK_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "SMBus block write");
+ return -1;
+ }
+ break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "I2C block write");
+ return -1;
+ }
+ break;
+ }
+
+ if (pec
+ && !(funcs & (I2C_FUNC_SMBUS_PEC | I2C_FUNC_I2C))) {
+ fprintf(stderr, "Warning: Adapter does "
+ "not seem to support PEC\n");
+ }
+
+ return 0;
+}
+
+static int confirm(const char *filename, int address, int size, int daddress,
+ int value, int vmask, const unsigned char *block, int len,
+ int pec)
+{
+ int dont = 0;
+
+ fprintf(stderr, "WARNING! This program can confuse your I2C "
+ "bus, cause data loss and worse!\n");
+
+ if (address >= 0x50 && address <= 0x57) {
+ fprintf(stderr, "DANGEROUS! Writing to a serial "
+ "EEPROM on a memory DIMM\nmay render your "
+ "memory USELESS and make your system "
+ "UNBOOTABLE!\n");
+ dont++;
+ }
+
+ fprintf(stderr, "I will write to device file %s, chip address "
+ "0x%02x, data address\n0x%02x, ", filename, address, daddress);
+ if (size == I2C_SMBUS_BYTE)
+ fprintf(stderr, "no data.\n");
+ else if (size == I2C_SMBUS_BLOCK_DATA ||
+ size == I2C_SMBUS_I2C_BLOCK_DATA) {
+ int i;
+
+ fprintf(stderr, "data");
+ for (i = 0; i < len; i++)
+ fprintf(stderr, " 0x%02x", block[i]);
+ fprintf(stderr, ", mode %s.\n", size == I2C_SMBUS_BLOCK_DATA
+ ? "smbus block" : "i2c block");
+ } else
+ fprintf(stderr, "data 0x%02x%s, mode %s.\n", value,
+ vmask ? " (masked)" : "",
+ size == I2C_SMBUS_BYTE_DATA ? "byte" : "word");
+ if (pec)
+ fprintf(stderr, "PEC checking enabled.\n");
+
+ fprintf(stderr, "Continue? [%s] ", dont ? "y/N" : "Y/n");
+ fflush(stderr);
+ if (!user_ack(!dont)) {
+ fprintf(stderr, "Aborting on user request.\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ char *end;
+ const char *maskp = NULL;
+ int res, i2cbus, address, size, file;
+ int value, daddress, vmask = 0;
+ char filename[20];
+ int pec = 0;
+ int flags = 0;
+ int force = 0, yes = 0, version = 0, readback = 0;
+ unsigned char block[I2C_SMBUS_BLOCK_MAX];
+ int len;
+
+ /* handle (optional) flags first */
+ while (1+flags < argc && argv[1+flags][0] == '-') {
+ switch (argv[1+flags][1]) {
+ case 'V': version = 1; break;
+ case 'f': force = 1; break;
+ case 'y': yes = 1; break;
+ case 'm':
+ if (2+flags < argc)
+ maskp = argv[2+flags];
+ flags++;
+ break;
+ case 'r': readback = 1; break;
+ default:
+ fprintf(stderr, "Error: Unsupported option "
+ "\"%s\"!\n", argv[1+flags]);
+ help();
+ exit(1);
+ }
+ flags++;
+ }
+
+ if (version) {
+ fprintf(stderr, "i2cset version %s\n", VERSION);
+ exit(0);
+ }
+
+ if (argc < flags + 4)
+ help();
+
+ i2cbus = lookup_i2c_bus(argv[flags+1]);
+ if (i2cbus < 0)
+ help();
+
+ address = parse_i2c_address(argv[flags+2]);
+ if (address < 0)
+ help();
+
+ daddress = strtol(argv[flags+3], &end, 0);
+ if (*end || daddress < 0 || daddress > 0xff) {
+ fprintf(stderr, "Error: Data address invalid!\n");
+ help();
+ }
+
+ /* check for command/mode */
+ if (argc == flags + 4) {
+ /* Implicit "c" */
+ size = I2C_SMBUS_BYTE;
+ } else if (argc == flags + 5) {
+ /* "c", "cp", or implicit "b" */
+ if (!strcmp(argv[flags+4], "c")
+ || !strcmp(argv[flags+4], "cp")) {
+ size = I2C_SMBUS_BYTE;
+ pec = argv[flags+4][1] == 'p';
+ } else {
+ size = I2C_SMBUS_BYTE_DATA;
+ }
+ } else {
+ /* All other commands */
+ if (strlen(argv[argc-1]) > 2
+ || (strlen(argv[argc-1]) == 2 && argv[argc-1][1] != 'p')) {
+ fprintf(stderr, "Error: Invalid mode '%s'!\n", argv[argc-1]);
+ help();
+ }
+ switch (argv[argc-1][0]) {
+ case 'b': size = I2C_SMBUS_BYTE_DATA; break;
+ case 'w': size = I2C_SMBUS_WORD_DATA; break;
+ case 's': size = I2C_SMBUS_BLOCK_DATA; break;
+ case 'i': size = I2C_SMBUS_I2C_BLOCK_DATA; break;
+ default:
+ fprintf(stderr, "Error: Invalid mode '%s'!\n", argv[argc-1]);
+ help();
+ }
+ pec = argv[argc-1][1] == 'p';
+ if (size == I2C_SMBUS_BLOCK_DATA || size == I2C_SMBUS_I2C_BLOCK_DATA) {
+ if (pec && size == I2C_SMBUS_I2C_BLOCK_DATA) {
+ fprintf(stderr, "Error: PEC not supported for I2C block writes!\n");
+ help();
+ }
+ if (maskp) {
+ fprintf(stderr, "Error: Mask not supported for block writes!\n");
+ help();
+ }
+ if (argc > (int)sizeof(block) + flags + 5) {
+ fprintf(stderr, "Error: Too many arguments!\n");
+ help();
+ }
+ } else if (argc != flags + 6) {
+ fprintf(stderr, "Error: Too many arguments!\n");
+ help();
+ }
+ }
+
+ len = 0; /* Must always initialize len since it is passed to confirm() */
+
+ /* read values from command line */
+ switch (size) {
+ case I2C_SMBUS_BYTE_DATA:
+ case I2C_SMBUS_WORD_DATA:
+ value = strtol(argv[flags+4], &end, 0);
+ if (*end || value < 0) {
+ fprintf(stderr, "Error: Data value invalid!\n");
+ help();
+ }
+ if ((size == I2C_SMBUS_BYTE_DATA && value > 0xff)
+ || (size == I2C_SMBUS_WORD_DATA && value > 0xffff)) {
+ fprintf(stderr, "Error: Data value out of range!\n");
+ help();
+ }
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ for (len = 0; len + flags + 5 < argc; len++) {
+ value = strtol(argv[flags + len + 4], &end, 0);
+ if (*end || value < 0) {
+ fprintf(stderr, "Error: Data value invalid!\n");
+ help();
+ }
+ if (value > 0xff) {
+ fprintf(stderr, "Error: Data value out of range!\n");
+ help();
+ }
+ block[len] = value;
+ }
+ value = -1;
+ break;
+ default:
+ value = -1;
+ break;
+ }
+
+ if (maskp) {
+ vmask = strtol(maskp, &end, 0);
+ if (*end || vmask == 0) {
+ fprintf(stderr, "Error: Data value mask invalid!\n");
+ help();
+ }
+ if (((size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA)
+ && vmask > 0xff) || vmask > 0xffff) {
+ fprintf(stderr, "Error: Data value mask out of range!\n");
+ help();
+ }
+ }
+
+ file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0);
+ if (file < 0
+ || check_funcs(file, size, pec)
+ || set_slave_addr(file, address, force))
+ exit(1);
+
+ if (!yes && !confirm(filename, address, size, daddress,
+ value, vmask, block, len, pec))
+ exit(0);
+
+ if (vmask) {
+ int oldvalue;
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ oldvalue = i2c_smbus_read_byte(file);
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ oldvalue = i2c_smbus_read_word_data(file, daddress);
+ break;
+ default:
+ oldvalue = i2c_smbus_read_byte_data(file, daddress);
+ }
+
+ if (oldvalue < 0) {
+ fprintf(stderr, "Error: Failed to read old value\n");
+ exit(1);
+ }
+
+ value = (value & vmask) | (oldvalue & ~vmask);
+
+ if (!yes) {
+ fprintf(stderr, "Old value 0x%0*x, write mask "
+ "0x%0*x: Will write 0x%0*x to register "
+ "0x%02x\n",
+ size == I2C_SMBUS_WORD_DATA ? 4 : 2, oldvalue,
+ size == I2C_SMBUS_WORD_DATA ? 4 : 2, vmask,
+ size == I2C_SMBUS_WORD_DATA ? 4 : 2, value,
+ daddress);
+
+ fprintf(stderr, "Continue? [Y/n] ");
+ fflush(stderr);
+ if (!user_ack(1)) {
+ fprintf(stderr, "Aborting on user request.\n");
+ exit(0);
+ }
+ }
+ }
+
+ if (pec && ioctl(file, I2C_PEC, 1) < 0) {
+ fprintf(stderr, "Error: Could not set PEC: %s\n",
+ strerror(errno));
+ close(file);
+ exit(1);
+ }
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ res = i2c_smbus_write_byte(file, daddress);
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ res = i2c_smbus_write_word_data(file, daddress, value);
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ res = i2c_smbus_write_block_data(file, daddress, len, block);
+ break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ res = i2c_smbus_write_i2c_block_data(file, daddress, len, block);
+ break;
+ default: /* I2C_SMBUS_BYTE_DATA */
+ res = i2c_smbus_write_byte_data(file, daddress, value);
+ break;
+ }
+ if (res < 0) {
+ fprintf(stderr, "Error: Write failed\n");
+ close(file);
+ exit(1);
+ }
+
+ if (pec) {
+ if (ioctl(file, I2C_PEC, 0) < 0) {
+ fprintf(stderr, "Error: Could not clear PEC: %s\n",
+ strerror(errno));
+ close(file);
+ exit(1);
+ }
+ }
+
+ if (!readback) { /* We're done */
+ close(file);
+ exit(0);
+ }
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ res = i2c_smbus_read_byte(file);
+ value = daddress;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ res = i2c_smbus_read_word_data(file, daddress);
+ break;
+ default: /* I2C_SMBUS_BYTE_DATA */
+ res = i2c_smbus_read_byte_data(file, daddress);
+ }
+ close(file);
+
+ if (res < 0) {
+ printf("Warning - readback failed\n");
+ } else
+ if (res != value) {
+ printf("Warning - data mismatch - wrote "
+ "0x%0*x, read back 0x%0*x\n",
+ size == I2C_SMBUS_WORD_DATA ? 4 : 2, value,
+ size == I2C_SMBUS_WORD_DATA ? 4 : 2, res);
+ } else {
+ printf("Value 0x%0*x written, readback matched\n",
+ size == I2C_SMBUS_WORD_DATA ? 4 : 2, value);
+ }
+
+ exit(0);
+}
diff --git a/tools/i2ctransfer.8 b/tools/i2ctransfer.8
new file mode 100644
index 0000000..0dd43c9
--- /dev/null
+++ b/tools/i2ctransfer.8
@@ -0,0 +1,159 @@
+.TH i2ctransfer 8 "February 2017"
+.SH "NAME"
+i2ctransfer \- send user-defined I2C messages in one transfer
+
+.SH SYNOPSIS
+.B i2ctransfer
+.RB [ -f ]
+.RB [ -y ]
+.RB [ -v ]
+.I i2cbus desc
+.RI [ data ]
+.RI [ desc
+.RI [ data ]]
+.RI ...
+.br
+.B i2ctransfer
+.B -V
+
+.SH DESCRIPTION
+.B i2ctransfer
+is a program to create I2C messages and send them combined as one transfer.
+For read messages, the contents of the received buffers are printed to stdout, one line per read message.
+.br
+Please note the difference between a
+.I transfer
+and a
+.I message
+here.
+A transfer may consist of multiple messages and is started with a START condition and ends with a STOP condition as described in the I2C specification.
+Messages within the transfer are concatenated using the REPEATED START condition which is described there as well.
+There are some advantages of having multiple messages in one transfer.
+First, some devices keep their internal states for REPEATED START but reset them after a STOP.
+Second, you cannot get interrupted during one transfer, but it might happen between multiple transfers.
+Interruption could happen on hardware level by another I2C master on the bus, or at software level by another I2C user who got its transfer scheduled between yours.
+This program helps you to create proper transfers for your needs.
+
+.SH OPTIONS
+.TP
+.B -f
+Force access to the device even if it is already busy.
+By default,
+.B i2ctransfer
+will refuse to access addresses marked as reserved by the I2C standard or to a device which is already under the control of a kernel driver.
+Using this flag is dangerous, it can seriously confuse the kernel driver in question.
+It can also cause
+.B i2ctransfer
+to silently write to the wrong register.
+So use at your own risk and only if you know what you're doing.
+.TP
+.B -y
+Disable interactive mode.
+By default,
+.B i2ctransfer
+will wait for a confirmation from the user before messing with the I2C bus.
+When this flag is used, it will perform the operation directly.
+This is mainly meant to be used in scripts.
+.TP
+.B -v
+Enable verbose output.
+It will print infos about all messages sent, i.e. not only for read messages but also for write messages.
+.TP
+.B -V
+Display the version and exit.
+
+.SH ARGUMENTS
+.PP
+The first parameter
+.I i2cbus
+indicates the number or name of the I2C bus to be used.
+This number should correspond to one of the busses listed by
+.B i2cdetect -l.
+
+.PP
+The next parameter is one or multiple
+.I desc
+blocks.
+The number of blocks is limited by the Linux Kernel and defined by I2C_RDWR_IOCTL_MAX_MSGS (42 as of v4.10).
+.I desc
+blocks are composed like this:
+
+.I {r|w}<length_of_message>[@address]
+
+.TP
+.B {r|w}
+specifies if the message is read or write
+.TP
+.B <length_of_message>
+specifies the number of bytes read or written in this message.
+It is parsed as an unsigned 16 bit integer, but note that the Linux Kernel applies an additional upper limit (8192 as of v4.10).
+.TP
+.B [@address]
+specifies the 7-bit address of the chip to be accessed for this message, and is an integer.
+If omitted, reuse the previous address.
+Normally, addresses outside the range of 0x03-0x77 and addresses with a kernel driver attached to them will be blocked.
+With
+.I -f
+(force), all addresses can be used.
+Be very careful when using that!
+10-bit addresses are currently not supported at all.
+
+.PP
+If the I2C message is a write, then a
+.I data
+block with the data to be written follows.
+It consists of
+.I <length_of_message>
+bytes which can be marked with the usual prefixes for hexadecimal, octal, etc.
+To make it easier to create larger data blocks easily, the data byte can have a suffix.
+
+.TP
+=
+keep value constant until end of message (i.e. 0= means 0, 0, 0, ...)
+.TP
++
+increase value by 1 until end of message (i.e. 0+ means 0, 1, 2, ...)
+.TP
+-
+decrease value by 1 until end of message (i.e. 0xff- means 0xff, 0xfe, 0xfd, ...)
+.TP
+p
+use value as seed for an 8 bit pseudo random sequence (i.e. 0p means 0x00, 0x50, 0xb0, ...)
+
+.SH EXAMPLES
+.PP
+On bus 0, from an EEPROM at address 0x50, read 8 byte from offset 0x64
+(first message writes one byte to set the memory pointer to 0x64, second message reads from the same chip):
+.nf
+.RS
+# i2ctransfer 0 w1@0x50 0x64 r8
+.RE
+.fi
+.PP
+For the same EEPROM, at offset 0x42 write 0xff 0xfe ... 0xf0
+(one write message; first byte sets the memory pointer to 0x42, 0xff is the first data byte, all following data bytes are decreased by one):
+.nf
+.RS
+# i2ctransfer 0 w17@0x50 0x42 0xff-
+.RE
+.fi
+
+.SH WARNING
+.B i2ctransfer
+can be extremely dangerous if used improperly.
+It can confuse your I2C bus, cause data loss, or have more serious side effects.
+Writing to a serial EEPROM on a memory DIMM (chip addresses between 0x50 and 0x57) may DESTROY your memory, leaving your system unbootable!
+Be extremely careful using this program.
+
+.SH AUTHORS
+Wolfram Sang, based on
+.B i2cget
+by Jean Delvare
+
+This manual page was originally written by Wolfram Sang based on the manual
+for
+.B i2cset
+by David Z Maze <dmaze@debian.org>.
+
+.SH SEE ALSO
+.BR i2cdetect (8), i2cdump (8), i2cget (8), i2cset (8)
diff --git a/tools/i2ctransfer.c b/tools/i2ctransfer.c
new file mode 100644
index 0000000..38b6b4a
--- /dev/null
+++ b/tools/i2ctransfer.c
@@ -0,0 +1,342 @@
+/*
+ i2ctransfer.c - A user-space program to send concatenated i2c messages
+ Copyright (C) 2015-17 Wolfram Sang <wsa@sang-engineering.com>
+ Copyright (C) 2015-17 Renesas Electronics Corporation
+
+ Based on i2cget.c:
+ Copyright (C) 2005-2012 Jean Delvare <jdelvare@suse.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include "i2cbusses.h"
+#include "util.h"
+#include "../version.h"
+
+enum parse_state {
+ PARSE_GET_DESC,
+ PARSE_GET_DATA,
+};
+
+#define PRINT_STDERR (1 << 0)
+#define PRINT_READ_BUF (1 << 1)
+#define PRINT_WRITE_BUF (1 << 2)
+#define PRINT_HEADER (1 << 3)
+
+static void help(void)
+{
+ fprintf(stderr,
+ "Usage: i2ctransfer [-f] [-y] [-v] [-V] I2CBUS DESC [DATA] [DESC [DATA]]...\n"
+ " I2CBUS is an integer or an I2C bus name\n"
+ " DESC describes the transfer in the form: {r|w}LENGTH[@address]\n"
+ " 1) read/write-flag 2) LENGTH (range 0-65535) 3) I2C address (use last one if omitted)\n"
+ " DATA are LENGTH bytes for a write message. They can be shortened by a suffix:\n"
+ " = (keep value constant until LENGTH)\n"
+ " + (increase value by 1 until LENGTH)\n"
+ " - (decrease value by 1 until LENGTH)\n"
+ " p (use pseudo random generator until LENGTH with value as seed)\n\n"
+ "Example (bus 0, read 8 byte at offset 0x64 from EEPROM at 0x50):\n"
+ " # i2ctransfer 0 w1@0x50 0x64 r8\n"
+ "Example (same EEPROM, at offset 0x42 write 0xff 0xfe ... 0xf0):\n"
+ " # i2ctransfer 0 w17@0x50 0x42 0xff-\n");
+}
+
+static int check_funcs(int file)
+{
+ unsigned long funcs;
+
+ /* check adapter functionality */
+ if (ioctl(file, I2C_FUNCS, &funcs) < 0) {
+ fprintf(stderr, "Error: Could not get the adapter "
+ "functionality matrix: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (!(funcs & I2C_FUNC_I2C)) {
+ fprintf(stderr, MISSING_FUNC_FMT, "I2C transfers");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void print_msgs(struct i2c_msg *msgs, __u32 nmsgs, unsigned flags)
+{
+ FILE *output = flags & PRINT_STDERR ? stderr : stdout;
+ unsigned i;
+ __u16 j;
+
+ for (i = 0; i < nmsgs; i++) {
+ int read = msgs[i].flags & I2C_M_RD;
+ int print_buf = (read && (flags & PRINT_READ_BUF)) ||
+ (!read && (flags & PRINT_WRITE_BUF));
+
+ if (flags & PRINT_HEADER)
+ fprintf(output, "msg %u: addr 0x%02x, %s, len %u",
+ i, msgs[i].addr, read ? "read" : "write", msgs[i].len);
+
+ if (msgs[i].len && print_buf) {
+ if (flags & PRINT_HEADER)
+ fprintf(output, ", buf ");
+ for (j = 0; j < msgs[i].len - 1; j++)
+ fprintf(output, "0x%02x ", msgs[i].buf[j]);
+ /* Print final byte with newline */
+ fprintf(output, "0x%02x\n", msgs[i].buf[j]);
+ } else if (flags & PRINT_HEADER) {
+ fprintf(output, "\n");
+ }
+ }
+}
+
+static int confirm(const char *filename, struct i2c_msg *msgs, __u32 nmsgs)
+{
+ fprintf(stderr, "WARNING! This program can confuse your I2C bus, cause data loss and worse!\n");
+ fprintf(stderr, "I will send the following messages to device file %s:\n", filename);
+ print_msgs(msgs, nmsgs, PRINT_STDERR | PRINT_HEADER | PRINT_WRITE_BUF);
+
+ fprintf(stderr, "Continue? [y/N] ");
+ fflush(stderr);
+ if (!user_ack(0)) {
+ fprintf(stderr, "Aborting on user request.\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ char filename[20];
+ int i2cbus, address = -1, file, arg_idx = 1, nmsgs = 0, nmsgs_sent, i;
+ int force = 0, yes = 0, version = 0, verbose = 0;
+ struct i2c_msg msgs[I2C_RDRW_IOCTL_MAX_MSGS];
+ enum parse_state state = PARSE_GET_DESC;
+ unsigned buf_idx = 0;
+
+ for (i = 0; i < I2C_RDRW_IOCTL_MAX_MSGS; i++)
+ msgs[i].buf = NULL;
+
+ /* handle (optional) arg_idx first */
+ while (arg_idx < argc && argv[arg_idx][0] == '-') {
+ switch (argv[arg_idx][1]) {
+ case 'V': version = 1; break;
+ case 'v': verbose = 1; break;
+ case 'f': force = 1; break;
+ case 'y': yes = 1; break;
+ default:
+ fprintf(stderr, "Error: Unsupported option \"%s\"!\n",
+ argv[arg_idx]);
+ help();
+ exit(1);
+ }
+ arg_idx++;
+ }
+
+ if (version) {
+ fprintf(stderr, "i2ctransfer version %s\n", VERSION);
+ exit(0);
+ }
+
+ if (arg_idx == argc) {
+ help();
+ exit(1);
+ }
+
+ i2cbus = lookup_i2c_bus(argv[arg_idx++]);
+ if (i2cbus < 0)
+ exit(1);
+
+ file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0);
+ if (file < 0 || check_funcs(file))
+ exit(1);
+
+ while (arg_idx < argc) {
+ char *arg_ptr = argv[arg_idx];
+ unsigned long len, raw_data;
+ __u16 flags;
+ __u8 data, *buf;
+ char *end;
+
+ if (nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) {
+ fprintf(stderr, "Error: Too many messages (max: %d)\n",
+ I2C_RDRW_IOCTL_MAX_MSGS);
+ goto err_out;
+ }
+
+ switch (state) {
+ case PARSE_GET_DESC:
+ flags = 0;
+
+ switch (*arg_ptr++) {
+ case 'r': flags |= I2C_M_RD; break;
+ case 'w': break;
+ default:
+ fprintf(stderr, "Error: Invalid direction\n");
+ goto err_out_with_arg;
+ }
+
+ len = strtoul(arg_ptr, &end, 0);
+ if (len > 0xffff || arg_ptr == end) {
+ fprintf(stderr, "Error: Length invalid\n");
+ goto err_out_with_arg;
+ }
+
+ arg_ptr = end;
+ if (*arg_ptr) {
+ if (*arg_ptr++ != '@') {
+ fprintf(stderr, "Error: Unknown separator after length\n");
+ goto err_out_with_arg;
+ }
+
+ /* We skip 10-bit support for now. If we want it,
+ * it should be marked with a 't' flag before
+ * the address here.
+ */
+
+ if (!force) {
+ address = parse_i2c_address(arg_ptr);
+ if (address < 0)
+ goto err_out_with_arg;
+
+ /* Ensure address is not busy */
+ if (set_slave_addr(file, address, 0))
+ goto err_out_with_arg;
+ } else {
+ /* 'force' allows whole address range */
+ address = strtol(arg_ptr, &end, 0);
+ if (arg_ptr == end || *end || address > 0x7f) {
+ fprintf(stderr, "Error: Invalid chip address\n");
+ goto err_out_with_arg;
+ }
+ }
+ } else {
+ /* Reuse last address if possible */
+ if (address < 0) {
+ fprintf(stderr, "Error: No address given\n");
+ goto err_out_with_arg;
+ }
+ }
+
+ msgs[nmsgs].addr = address;
+ msgs[nmsgs].flags = flags;
+ msgs[nmsgs].len = len;
+
+ if (len) {
+ buf = malloc(len);
+ if (!buf) {
+ fprintf(stderr, "Error: No memory for buffer\n");
+ goto err_out_with_arg;
+ }
+ memset(buf, 0, len);
+ msgs[nmsgs].buf = buf;
+ }
+
+ if (flags & I2C_M_RD || len == 0) {
+ nmsgs++;
+ } else {
+ buf_idx = 0;
+ state = PARSE_GET_DATA;
+ }
+
+ break;
+
+ case PARSE_GET_DATA:
+ raw_data = strtoul(arg_ptr, &end, 0);
+ if (raw_data > 0xff || arg_ptr == end) {
+ fprintf(stderr, "Error: Invalid data byte\n");
+ goto err_out_with_arg;
+ }
+ data = raw_data;
+ len = msgs[nmsgs].len;
+
+ while (buf_idx < len) {
+ msgs[nmsgs].buf[buf_idx++] = data;
+
+ if (!*end)
+ break;
+
+ switch (*end) {
+ /* Pseudo randomness (8 bit AXR with a=13 and b=27) */
+ case 'p':
+ data = (data ^ 27) + 13;
+ data = (data << 1) | (data >> 7);
+ break;
+ case '+': data++; break;
+ case '-': data--; break;
+ case '=': break;
+ default:
+ fprintf(stderr, "Error: Invalid data byte suffix\n");
+ goto err_out_with_arg;
+ }
+ }
+
+ if (buf_idx == len) {
+ nmsgs++;
+ state = PARSE_GET_DESC;
+ }
+
+ break;
+
+ default:
+ /* Should never happen */
+ fprintf(stderr, "Internal Error: Unknown state in state machine!\n");
+ goto err_out;
+ }
+
+ arg_idx++;
+ }
+
+ if (state != PARSE_GET_DESC || nmsgs == 0) {
+ fprintf(stderr, "Error: Incomplete message\n");
+ goto err_out;
+ }
+
+ if (yes || confirm(filename, msgs, nmsgs)) {
+ struct i2c_rdwr_ioctl_data rdwr;
+
+ rdwr.msgs = msgs;
+ rdwr.nmsgs = nmsgs;
+ nmsgs_sent = ioctl(file, I2C_RDWR, &rdwr);
+ if (nmsgs_sent < 0) {
+ fprintf(stderr, "Error: Sending messages failed: %s\n", strerror(errno));
+ goto err_out;
+ } else if (nmsgs_sent < nmsgs) {
+ fprintf(stderr, "Warning: only %d/%d messages were sent\n", nmsgs_sent, nmsgs);
+ }
+
+ print_msgs(msgs, nmsgs_sent, PRINT_READ_BUF | (verbose ? PRINT_HEADER | PRINT_WRITE_BUF : 0));
+ }
+
+ close(file);
+
+ for (i = 0; i < nmsgs; i++)
+ free(msgs[i].buf);
+
+ exit(0);
+
+ err_out_with_arg:
+ fprintf(stderr, "Error: faulty argument is '%s'\n", argv[arg_idx]);
+ err_out:
+ close(file);
+
+ for (i = 0; i <= nmsgs; i++)
+ free(msgs[i].buf);
+
+ exit(1);
+}
diff --git a/tools/util.c b/tools/util.c
new file mode 100644
index 0000000..29e4958
--- /dev/null
+++ b/tools/util.c
@@ -0,0 +1,58 @@
+/*
+ util.c - helper functions
+ Copyright (C) 2006-2009 Jean Delvare <jdelvare@suse.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#include <stdio.h>
+#include "util.h"
+
+/* Return 1 if we should continue, 0 if we should abort */
+int user_ack(int def)
+{
+ char s[2];
+ int ret;
+
+ if (!fgets(s, 2, stdin))
+ return 0; /* Nack by default */
+
+ switch (s[0]) {
+ case 'y':
+ case 'Y':
+ ret = 1;
+ break;
+ case 'n':
+ case 'N':
+ ret = 0;
+ break;
+ default:
+ ret = def;
+ }
+
+ /* Flush extra characters */
+ while (s[0] != '\n') {
+ int c = fgetc(stdin);
+ if (c == EOF) {
+ ret = 0;
+ break;
+ }
+ s[0] = c;
+ }
+
+ return ret;
+}
+
diff --git a/tools/util.h b/tools/util.h
new file mode 100644
index 0000000..f4f4817
--- /dev/null
+++ b/tools/util.h
@@ -0,0 +1,26 @@
+/*
+ util - helper functions
+ Copyright (C) 2006-2009 Jean Delvare <jdelvare@suse.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA.
+*/
+
+#ifndef _UTIL_H
+#define _UTIL_H
+
+extern int user_ack(int def);
+
+#endif /* _UTIL_H */
diff --git a/version.h b/version.h
new file mode 100644
index 0000000..073379b
--- /dev/null
+++ b/version.h
@@ -0,0 +1 @@
+#define VERSION "4.0"