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"