Index: /lm-sensors/tags/V3-3-0/COPYING.LGPL
===================================================================
--- /lm-sensors/tags/V3-3-0/COPYING.LGPL	(revision 5860)
+++ /lm-sensors/tags/V3-3-0/COPYING.LGPL	(revision 5860)
@@ -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!
Index: /lm-sensors/tags/V3-3-0/doc/vid
===================================================================
--- /lm-sensors/tags/V3-3-0/doc/vid	(revision 5626)
+++ /lm-sensors/tags/V3-3-0/doc/vid	(revision 5626)
@@ -0,0 +1,58 @@
+VID pin settings
+--------------------
+
+The VID (Voltage Identification) pins on sensor chips
+are used to read the CPU Core voltage setting.
+
+The VID setting can be controlled by jumpers on the board,
+or, in newer motherboards, by settings in the BIOS.
+On these newer boards, the BIOS programs some device's
+pins which in turn controls a DC-DC Converter to set its
+output to a certain voltage. These pins are also connected to
+the sensor chip so that the VID setting can be read back
+by applications.
+
+There are between 4 and 8 VID pins (the more recent the CPU, the more
+numerous the VID pins.) The VID codes are defined by Intel in documents
+titled "VRM X.X DC-DC Converter Design Guidelines".
+(VRM = Voltage Regulator Module), or
+"Voltage Regulator-Down (VRD) X.X Design Guide".
+These documents are available at http://developer.intel.com.
+There are equivalent documents for AMD processors (look for a table
+named "VID Code Voltages" in processor specifications.)
+
+There are several different VRM document versions.
+The common versions are as follows:
+
+Document Version	Voltage Range	Increment	Processors
+----------------	-------------	---------	----------
+"2.4"			0.8 - 1.55	0.025V		AMD Opteron 24x
+
+8.2 (8.1, 8.3)		1.30 - 2.05V    0.05V		PII, PIII, Celeron
+			2.1 - 3.5V	0.10V
+
+8.4			1.30 - 2.05V    0.05V		PIII, Celeron
+							4 pins only
+
+8.5			1.050 - 1.825V	0.05V		PIII-S Tualatin
+
+9.0, (9.1)		1.100 - 1.850V	0.025V		P4, AMD Socket A
+
+10.0			0.8375 - 1.6000 0.0125V		Desktop Socket 478
+
+
+"2.4" is not an actual document version but simply a way to identify
+AMD Opteron 24x processors.
+Note that versions 8.1 - 8.4 are compatible.
+
+Since Linux 2.6.9, the correct VRM version is selected automatically
+based on the CPU brand and model. Thus you shouldn't have to care about
+it. If you are using a brand new CPU and even the latest kernel complains
+("Unknown VRM version of your CPU"), this means that the kernel must be
+taught about this new CPU model. Please report to us in this case.
+
+Remember, the VID pins on the sensor chips are inputs only.
+You cannot use them to manipulate the actual processor core voltage.
+
+------------------
+Copyright (c) 2002-2004 Mark D. Studebaker
Index: /lm-sensors/tags/V3-3-0/doc/fancontrol.txt
===================================================================
--- /lm-sensors/tags/V3-3-0/doc/fancontrol.txt	(revision 5717)
+++ /lm-sensors/tags/V3-3-0/doc/fancontrol.txt	(revision 5717)
@@ -0,0 +1,133 @@
+
+fancontrol - automated software based fan speed regulation
+
+
+Introduction
+
+fancontrol is a shellscript for use with lm_sensors. It reads its
+configuration from a file, then calculates fan speeds from temperatures and
+sets the corresponding pwm outputs to the computed values.  It is included in
+lm_sensors since version 2.8.0. For easy configuration, there's a script
+named pwmconfig which lets you interactively write your configuration file for
+fancontrol. Alternatively you can write this file yourself using the
+information from the configuration section of this document.
+
+Please be careful when using the fan control features of your mainboard, in
+addition to the risk of burning your CPU, at higher temperatures there will be
+a higher wearout of your other hardware components, too. So if you plan to use
+these components in 50 years, _maybe_ you shouldn't use fancontrol/your
+hardware at all.  Also please keep in mind most fans aren't designed to be
+powered by a PWMed voltage, for more detailed information on this I recommend
+this appnote: http://www.maxim-ic.com/appnotes.cfm/appnote_number/707
+In practice it doesn't seem to be a major issue, the fans will get slightly
+warmer, just be sure to have a temperature alarm and/or shutdown call, in case
+some fan fails, because you probably won't hear it anymore ;)
+
+Configuration
+
+Since most of you are going to use the pwmconfig script, the config
+file syntax will be discussed last. First I'm going to describe the various
+variables available for changing fancontrol's behaviour:
+
+INTERVAL
+	This variable defines at which interval in seconds the main loop of
+	fancontrol will be executed
+
+FCTEMPS
+	Maps pwm outputs to temperature sensors so fancontrol knows which
+	temperature sensors should be used for calculation of new values for
+	the corresponding pwm outputs.
+
+FCFANS
+	FCFANS records the association between a pwm and a fan.
+	Then fancontrol can check the fan speed and restart it if it
+	stops unexpectedly.
+
+MINTEMP
+	The temperature below which the fan gets switched to minimum speed.
+
+MAXTEMP
+	The temperature over which the fan gets switched to maximum speed.
+
+MINSTART
+	Sets the minimum speed at which the fan begins spinning. You should
+	use a safe value to be sure it works, even when the fan gets old.
+
+MINSTOP
+	The minimum speed at which the fan still spins. Use a safe value here,
+	too.
+
+MINPWM
+	The PWM value to use when the temperature is below MINTEMP.
+	Typically, this will be either 0 if it is OK for the fan to plain
+	stop, or the same value as MINSTOP if you don't want the fan to
+	ever stop.
+	If this value isn't defined, it defaults to 0 (stopped fan).
+
+MAXPWM
+	The PWM value to use when the temperature is over MAXTEMP.
+	If this value isn't defined, it defaults to 255 (full speed).
+
+A graph might help you understand how the different values relate
+to each other:
+
+    PWM ^
+    255 +
+        |
+        |
+        |                             ,-------------- MAXPWM
+        |                           ,'.
+        |                         ,'  .
+        |                       ,'    .
+        |                     ,'      .
+        |                   ,'        .
+        |                 ,'          .
+        |       MINSTOP .'            .
+        |               |             .
+        |               |             .
+        |               |             .
+ MINPWM |---------------'             .
+        |               .             .
+        |               .             .
+        |               .             .
+      0 +---------------+-------------+---------------->
+                     MINTEMP       MAXTEMP            t (degree C)
+
+The configuration file format is a bit strange:
+
+VARIABLE=chip/pwmdev=value chip/pwmdev2=value2 [...]
+VARIABLE2=[...]
+
+Each variable has its own line. The variable name is followed by an equal sign
+and the device=value pairs. These consist of the path to the pwm output for
+which the value is valid, equal sign followed by the value and are separated
+by a blank. Path can be absolute or relative (from /sys/bus/i2c/devices or
+/sys/class/hwmon depending on the kernel version).
+
+Example:
+
+MINTEMP=hwmon0/device/pwm1=40 hwmon0/device/pwm2=54
+
+You have to play with the temperature values a bit to get happy. For initial
+setup I recommend using the pwmconfig script. Small changes can be made by
+editing the config file directly following the rules above.
+
+
+The algorithm
+
+fancontrol first reads its configuration, writes it to arrays and loops its
+main function.  This function gets the temperatures and fanspeeds from
+kernel driver files and calculates new speeds depending on temperature
+changes, but only if the temp is between MINTEMP and MAXTEMP. After that, the
+new values are written to the pwm outputs.  The pwm value increases
+linearly with rising temperature.
+
+
+Planned features
+
+rc-scripts for some gnu/linux-distributions
+smoother regulation (temp interpolation)
+gui for configuration
+
+If you have other wishes or want to contribute something, please let me know:
+marius.reiner${AT}hdev.de
Index: /lm-sensors/tags/V3-3-0/doc/fan-divisors
===================================================================
--- /lm-sensors/tags/V3-3-0/doc/fan-divisors	(revision 4990)
+++ /lm-sensors/tags/V3-3-0/doc/fan-divisors	(revision 4990)
@@ -0,0 +1,99 @@
+Fan reading problems and solutions
+----------------------------------
+
+If you aren't getting the expected readings on your fans,
+try the following:
+
+- Do you get a fan reading in the BIOS or using a different OS?
+  If not, you may not have a fan with a tachometer output.
+  Look and see. Fans with tachometer outputs have at least 3 wires.
+  Fans with only 2 wires cannot, in general, report their speed.
+
+- Try experimenting with the fan divisor settings:
+
+
+Fan Divisor Settings
+--------------------
+
+Fan Divisor	Minimum RPM	Maximum RPM
+-----------	-----------	-----------
+  1		5314		1350000
+  2		2657		 675000		default on most chips
+  4		1328		 337500
+  8		 664		 168750
+ 16		 332		  84375		not supported on most chips
+ 32		 166		  42187		""
+ 64		  83		  21093		""
+128		  41		  10546		""
+
+Pick a divisor so that the nominal RPM is about 50%
+above the minimum. This is a good compromise between
+margin and accuracy. Note that most chips only support
+fan divisors of 1, 2, 4, and 8.
+
+If you have a 0 RPM reading some or all of the time,
+increase the divisor until you get good readings.
+
+If you have a nominal reading less than 1.25 times the
+minimum, increase the divisor to give you margin
+so that you will not get spurious alarms.
+
+If you have a nominal reading more than 3 times the
+minimum, decrease the divisor to provide better
+accuracy.
+
+
+How to change fan divisors
+--------------------------
+
+Put an entry "set fanN_div X" in the appropriate section of
+/etc/sensors3.conf and run 'sensors -s'
+(N is the number of the fan, and X is the divisor you want).
+
+
+Further details
+---------------
+
+Fan divisors are quite confusing.
+
+Sensor chips count fan speed by using the fan signal
+to gate an 8-bit counter driven by a 22.5 kHz clock.
+So the _higher_ the counter value, the _slower_ the fan,
+and vice versa.
+
+The term 'fan divisor' is a misnomer because it doesn't divide
+the fan signal, it divides the 22.5 kHz clock.
+Thus you _increase_ the divisor if you have a slow fan.
+
+The drivers account for the 'fan divisor' in their calculation
+of RPM. So changing the fan divisor will NOT change the
+nominal RPM reading, it will only affect the minimum and maximum
+readings and the accuracy of the readings.
+
+The actual formula is RPM = (60 * 22500) / (count * divisor)
+
+The readings are most accurate when the fan speed is low
+(i.e., close to the minimum possible RPM reading).
+As fan speed gets closer to the maximum possible RPM reading,
+the reading becomes quite inaccurate. Fortunately, most computer
+fans do not approach 1,000,000 RPM !
+
+
+Fan readings 2X too high
+------------------------
+
+This is rare, but can happen. This is typically caused by a fan which
+outputs 4 pulses by revolution, instead of the standard 2.
+
+As we learned above, you can _not_ fix this by changing
+the fan divisor. You must add entries into the appropriate
+section of /etc/sensors3.conf:
+
+	compute fanN  @/2,  2*@
+
+(N is the number of the fan)
+
+
+
+------------------
+Copyright (c) 2000-2004 Mark D. Studebaker
Index: /lm-sensors/tags/V3-3-0/doc/chips/MODPARMS
===================================================================
--- /lm-sensors/tags/V3-3-0/doc/chips/MODPARMS	(revision 5778)
+++ /lm-sensors/tags/V3-3-0/doc/chips/MODPARMS	(revision 5778)
@@ -0,0 +1,139 @@
+Parameters for modprobe and insmod
+==================================
+
+PRELIMINARY WARNING:
+This documentation file is outdated and needs to be entirely reviewed
+and updated. Until then, don't blindly trust what you read here.
+
+==================================
+SUMMARY
+
+	[modprobe,insmod] module {parameter...}
+	Parameters:
+		force=bus,address{,bus,address}
+		force_addr=address{,address}
+		force_[chipname]=bus,address{,bus,address}
+		force_subclients=bus,caddr,saddr,saddr
+		ignore=bus,address{,bus,address}
+		ignore_range=bus,start,end{,bus,start,end}
+		probe=bus,address{,bus,address}
+		probe_range=bus,start,end{,bus,start,end}
+		init=[0,1] (default 1)
+
+	All arguments are in decimal unless prefixed by "0x".
+	No spaces are allowed.
+
+==================================
+OVERVIEW
+
+All chip drivers have a few modprobe module parameters in common.
+(Insmod can also be used, but we recommend modprobe so that
+other required modules are automatically loaded.) These
+parameters can be used when a module is inserted, to give some additional
+information about how it should function. In this case, they tell where
+the module should look for what chips. Usually, you don't need them, and
+if you do, you are often told by sensors-detect which ones you need.
+
+If a module doesn't load with no parameters, you may need to
+add parameters. Check 'dmesg' for clues to what went wrong.
+
+
+BUS NUMBERING
+-------------
+I2C/SMBus adapters are numbered in the order they are inserted. If you want
+to know what number an adapter has, use the following command:
+
+  (for kernel 2.6.32 and newer)
+  grep . /sys/bus/i2c/devices/i2c-*/name
+
+  (for kernel 2.6.31 and older)
+  grep . /sys/class/i2c-adapter/i2c-*/name
+
+Alternatively, if you have installed i2c-tools, you can do:
+
+  modprobe i2c-dev
+  i2cdetect -l
+
+If you want 'any I2C bus', use '-1'.
+
+
+ADDRESS NUMBERING
+-----------------
+Each adapter has a number of addresses on I2C/SMBus adapters and/or the
+ISA bus that are always scanned if no modprobe parameters are given. The
+parameters below override those addresses that are scanned by default.
+
+All modprobe parameters described below take lists of positive integers
+(unsigned, in the range of 0 to 65535) as their arguments.  Integers
+are separated by comma's, and may be given as ordinary decimal numbers,
+as octal numbers (by starting them with a `0') or as hexadecimal numbers
+(by starting them with a `0x'). More information can be found by
+entering `man modprobe' or `man insmod'.
+So to summarize:
+	0x00 - 0x7f	Valid I2C Addresses
+	0x0000 - 0xffff	Valid ISA Bus Addresses
+
+
+==================================
+PARAMETER DETAILS
+
+`force', `force_*', `ignore' and `probe' parameters take pairs of numbers.
+Each first (odd) number is a bus number, each second (even) number is
+an (I2C or ISA) address.
+
+`ignore_range' and `probe_range' parameters take triples of numbers.
+Each first number is a bus number, each second number is the start address
+of a range, and each third number is the end address of a range. Ranges
+are always inclusive.
+
+`probe' and `probe_range' parameters tell the module to scan additional 
+addresses for a specific bus. These are treated just as the ordinary
+addresses scanned. This is useful for some chips that can be anywhere;
+to keep loading time (and false detections) down, only the most common
+addresses are scanned by default.
+
+`ignore' and `ignore_range' parameters tell the module not to scan
+specific addresses. They overrule the default addresses and any 
+addresses specified by `probe' and `probe_range' statements.
+
+The `force' parameter tells the module a supported chip is found at
+a specific address. It overrules all previously mentioned parameters.
+Sometimes, a chip can be in a specific internal state that makes
+detection impossible. If you specify it with the `force' parameter,
+it is first put into a recognizable state. Also, some detection routines
+are skipped. If the module supports several chips, it will still try
+to determine what chip is found on that address. If it can't determine
+this, the address will still not be used.
+
+There is a `force_*' parameter for each type of chip supported by a
+module. This is the strongest statement possible - it says that a
+specific type of chip is found on a specific address, and the module
+will skip all detection and recognition routines. This can lead to
+very strange results...
+
+`force_addr' is a parameter used for PCI sensor adapters.
+It is used to program the base address of the sensor registers.
+This is required if the BIOS does not initialize the base address.
+
+`force_subclients' is used to force the i2c addresses for subclients of
+a certain chip. Typical usage is `force_subclients=0,0x2d,0x4a,0x4b'
+to force the subclients of chip 0x2d on bus 0 to i2c addresses
+0x4a and 0x4b.
+This parameter is currently supported only by the w83781d driver
+and is useful for certain Tyan boards.
+
+`init' is used to tell a driver to bypass initializing a chip.
+This may be necessary if the BIOS has initialized the chip a certain way
+and the driver should not overwrite that initialization.
+It is also useful if normal initialization crashes the system.
+The default is 1, so the only useful value is `init=0'.
+This parameter is currently supported only by the w83781d driver.
+
+==================================
+EXAMPLE
+
+  # ISA address 0x390 contains a LM79
+  # Scan all I2C addresses (from 0 to 0x7f) for all I2C adapters
+  # But skip address 0x2d on adapter 0, and address 0x20 on adapter 1
+
+  modprobe lm78 force_lm79=9191,0x390 ignore=0,0x2d,1,0x20 probe_range=-1,0,0x7f
Index: /lm-sensors/tags/V3-3-0/doc/chips/SUMMARY
===================================================================
--- /lm-sensors/tags/V3-3-0/doc/chips/SUMMARY	(revision 5278)
+++ /lm-sensors/tags/V3-3-0/doc/chips/SUMMARY	(revision 5278)
@@ -0,0 +1,297 @@
+SUMMARY OF SUPPORTED CHIPS
+==========================
+
+This is a summary of the sensor and non-sensor chips supported by this
+package. It lists the features of each supported chip. For further
+information on a particular driver, see the documentation in this
+directory.
+
+To determine what chips you have in your system, run 'sensors-detect'.
+
+If your sensor chip is not detected by sensors-detect, please contact us
+(see http://www.lm-sensors.org/wiki/AuthorsAndContributors). Only contact us
+if you know for sure that your system includes a sensor chip (for
+example if you have technical documentation mentioning it, or if the
+BIOS setup screen presents monitoring values, or if you can get
+hardware monitoring to work using a different OS). Do *not* contact us
+if you have no proof that your system includes sensors. Such systems
+tend to be rare these days, but do still exist. This is especially true
+of laptops. If there are no sensors, there's nothing we can do.
+
+If you contact us, please include the following in your mail:
+ - The output of (as root) 'sensors-detect'. Make sure you unloaded
+   every chip driver beforehand (check that the sensors-detect script
+   does not complain that it couldn't probe some I2C addresses on any
+   given bus).
+ - Dumps of the chips that might be hardware monitoring chips. To get
+   these, follow the following steps:
+   1* Run 'i2cdetect -l'. This will list all available I2C (or SMBus)
+      busses on your system.
+   2* For each bus listed, run (as root) 'i2cdetect N' where N is the
+      bus number.
+   3* For each bus, if there is anything in the ranges 0x20-0x2f or
+      0x48-0x4f, dump the contents of that chip with (as root)
+      'i2cdump N 0xXX', where N is the bus number and XX is the
+      responsive address. Chips in other address ranges are way less
+      likely to be hardware monitoring chips. In particular, do not
+      send dumps of addresses 0x08, 0x0c, 0x10, 0x30-0x37, 0x42, 0x44,
+      0x50-0x5f, 0x61 and 0x69. These are *not* hadware monitoring
+      chips.
+ - The part numbers of the chips on your motherboard you think are the
+   sensor chips.
+
+-----------------------------------------------------------------------------
+
+SUPPORTED SENSOR CHIPS
+----------------------
+
+			Included Sensors	Controls Busses
+			----------------------	-------  -----------
+Driver  Chips		#temp	#vin	#fanin	pwm/dac	I2C	ISA
+---------------------------------------------------------------------
+adm1021
+	adm1021		2	-	-	-	yes	no
+	adm1021a	2	-	-	-	yes	no
+	adm1023		2	-	-	-	yes	no
+	gl523sm		2	-	-	-	yes	no
+	lm84		2	-	-	-	yes	no
+	max1617		2	-	-	-	yes	no
+	max1617a	2	-	-	-	yes	no
+	tc1068		2	-	-	-	yes	no
+	tcm1617		2	-	-	-	yes	no
+	thmc10		2	-	-	-	yes	no
+	mc1066		2	-	-	-	yes	no
+	  (xeon - no detection - requires force_adm1021 parameter)
+	xeon		1	-	-	-	yes	no
+	  (the following chips are detected as a max1617)
+	ne1617		2	-	-	-	yes	no
+	ne1617a		2	-	-	-	yes	no
+
+adm1024
+	adm1024		3	6	-	1 dac	yes	no
+
+adm1025
+	adm1025		2	6	-	-	yes	no
+	ne1619		2	6	-	-	yes	no
+
+adm1026
+	adm1026		3	17	8	2 pwm+dac yes	no
+
+adm1031
+	adm1030		2	-	1	1 pwm	yes	no
+	adm1031		3	-	2	2 pwm	yes	no
+
+adm9240
+	adm9240		1	6	2	1 dac	yes	no
+	ds1780		1	6	2	1 dac	yes	no
+	lm81		1	6	2	1 dac	yes	no
+
+asb100
+	asb100		4	7	3	1	yes	no
+
+bmcsensors
+	bmcsensors	?	?	?	-	no	no
+
+ds1621
+	ds1621		1	-	-	-	yes	no
+	  (the following chip is detected as a ds1621)
+	ds1625		1	-	-	-	yes	no
+
+f71805f
+	f71805f		9	3	3	-	no	yes
+
+fscher
+	fscher		3	3	3	-	yes	no
+
+fscpos
+	fscpos		3	3	3	-	yes	no
+
+fscscy
+	fscscy		4	3	6	-	yes	no
+
+gl518sm
+	gl518sm (r00)	1	1-4	2	-	yes	no
+	gl518sm (r80)	1	4	2	-	yes	no
+
+gl520sm
+	gl520sm		1-2	4-5	2	-	yes	no
+
+it87
+	it8712		3	8	3	3 pwm	yes	yes
+	(the following are reported as an "it87")
+	it8705		3	8	3	3 pwm	yes	yes
+	sis950		3	8	3	3 pwm	yes	yes
+
+lm63
+	lm63		2	-	1	1 pwm	yes	no
+
+lm75
+	lm75		1	-	-	-	yes	no
+	  (the following chips are detected as an lm75)
+	ds75		1	-	-	-	yes	no
+	ds1775		1	-	-	-	yes	no
+	max6625		1	-	-	-	yes	no
+	max6626		1	-	-	-	yes	no
+	tcn75		1	-	-	-	yes	no
+
+lm78
+	lm78		1	7	3	-	yes	yes
+	lm78-j		1	7	3	-	yes	yes
+	lm79		1	7	3	-	yes	yes
+
+lm80
+	lm80		1	7	2	-	yes	no
+
+lm83
+	lm83		4	-	-	-	yes	no
+	lm82		2	-	-	-	yes	no
+
+lm85
+	lm85		3	5	4	3 pwm	yes	no
+	adm1027		3	5	4	3 pwm	yes	no
+	adt7463		3	5	4	3 pwm	yes	no
+	emc6d100	3	8	4	3 pwm	yes	no
+	(emc6d101 is reported as emc6d100)
+	emc6d101	3	8	4	3 pwm	yes	no
+	emc6d102	3	8	4	3 pwm	yes	no
+
+lm87
+	lm87		2-3	6-8	0-2	1 pwm	yes	no
+
+lm90
+	lm90		2	-	-	-	yes	no
+	lm99		2	-	-	-	yes	no
+	lm86		2	-	-	-	yes	no
+	adm1032		2	-	-	-	yes	no
+	max6657		2	-	-	-	yes	no
+	(lm89 is detected as an lm99)
+	lm89		2	-	-	-	yes	no
+	(max6658 and max6659 are detected as a max6657)
+	max6658		2	-	-	-	yes	no
+	max6659		2	-	-	-	yes	no
+	(adt7461 only works in ADM1032 compatibility mode)
+	adt7461		2	-	-	-	yes	no
+
+lm92
+	(all are reported as an "lm92")
+	lm92		1	-	-	-	yes	no
+	max6633		1	-	-	-	yes	no
+	max6634		1	-	-	-	yes	no
+	max6635		1	-	-	-	yes	no
+	(lm76 needs force parameter)
+	lm76		1	-	-	-	yes	no
+
+max1619
+	max1619		2	-	-	-	yes	no
+
+max6650
+	(all are reported as a "max6650")
+	max6650		-	-	1	1 pwm	yes	no
+	max6651		-	-	4	1 pwm	yes	no
+
+maxilife
+	maxilife-as	5	4	3	-	yes	no
+	maxilife-co	5	4	3	-	yes	no
+	maxilife-cg	5	4	3	-	yes	no
+
+mtp008
+	mtp008		3	7	3	3 pwm	yes	no
+
+pc87360
+	pc87360		-	-	2	2 pwm	no	yes (LPC)
+	pc87363		-	-	2	2 pwm	no	yes (LPC)
+	pc87364		-	-	3	3 pwm	no	yes (LPC)
+	pc87365		2	11	3	3 pwm	no	yes (LPC)
+	pc87366		3-4	11	3	3 pwm	no	yes (LPC)
+
+pcf8591
+	pcf8591		-	2-4	-	1 dac	yes	no
+
+sis5595
+	sis5595		0-1	4-5	2	-	no	yes
+
+smartbatt
+	smartbatt	1	1	-	-	yes	no
+
+smsc47m1
+	(all are reported as a "smsc47m1")
+	smsc47b27x	-	-	2	2	no	yes (LPC)
+	smsc47m10x	-	-	2	2	no	yes (LPC)
+	smsc47m112	-	-	2	2	no	yes (LPC)
+	smsc47m13x	-	-	2	2	no	yes (LPC)
+	smsc47m14x	-	-	2	2	no	yes (LPC)
+	smsc47m15x	-	-	2	2	no	yes (LPC)
+	smsc47m192	-	-	2	2	no	yes (LPC)
+	smsc47m997	-	-	2	2	no	yes (LPC)
+	(reported as a "smsc47m2")
+	smsc47m292	-	-	3	3	no	yes (LPC)
+
+thmc50
+	adm1022		3	2	-	1 dac	yes	no
+	thmc50		3	2	-	1 dac	yes	no
+
+via686a
+	via686a		3	5	2	-	no	yes
+	vt8231		3	5	2	-	no	yes
+
+vt1211
+	vt1211		2-7	1-6	2	2	no	yes (LPC)
+
+vt8231
+	vt8231		2-7	2-7	2	2	no	yes
+
+w83627hf
+	w83627hf	3	9	3	2 pwm	no	yes (LPC)
+	w83627thf	3	7	3	3 dac	no	yes (LPC)
+	w83637hf	3	7	2	3 pwm	no	yes (LPC)
+	w83687thf	3	7	3	3 pw/da	no	yes (LPC)
+	w83697hf	2	8	2	2 pwm	no	yes (LPC)
+
+w83781d
+	as99127f	3	7	3	-	yes	no
+	w83781d		3	7	3	-	yes	yes
+	w83782d		3	9	3	2-4 pwm	yes	yes
+	w83783s		1-2	5-6	3	2 pwm	yes	no
+	w83791d		3	10	5	5 pwm	yes	no
+	w83627hf	3	9	3	2 pwm	yes	yes (LPC)
+
+w83l785ts
+	w83l785ts	1	-	-	-	yes	no
+
+w83792d
+	w83792d		3	9	7	3 pwm	yes	no
+
+xeontemp
+	xeontemp	1	-	-	-	yes	no
+
+
+-----------------------------------------------------------------------------
+
+COMPANY ID LIST
+---------------
+
+Many SMBus chips have a company ID at location 0x3E or 0xFE.
+
+Here is a partial list.
+This may help identify a chip after doing a 'i2cdump [bus] [address]'.
+
+0x01	National
+0x12C3	Asus (at 0x4F)
+0x1934	Fintek (at 0x5D-0x5E)
+0x23	Analog Devices (old products) or Fintek
+0x41	Analog Devices (new products)
+0x49	TI
+0x4D	Maxim
+0x54	On Semi
+0x5C	SMSC
+0x5D 	SMSC
+0x55	SMSC
+0x5CA3	Winbond (at 0x4F)
+0x61	Andigilog
+0x90	ITE (at 0x58)
+0xA1	Philips
+0xA3	Winbond (at 0x4F)
+0xC3	Asus (at 0x4F)
+0xDA	Dallas
+
+[A-Z]{3} at 0x00-0x02: Fujitsu-Siemens
Index: /lm-sensors/tags/V3-3-0/doc/svn
===================================================================
--- /lm-sensors/tags/V3-3-0/doc/svn	(revision 5187)
+++ /lm-sensors/tags/V3-3-0/doc/svn	(revision 5187)
@@ -0,0 +1,31 @@
+Subversion Repository 
+
+Anonymous access to our Subversion repository (SVN) is available. This means
+that users can have direct access to up-to-the-minute code.
+(http://subversion.tigris.org/)
+
+Driver development for 2.6 kernels is taking place directly in the kernel
+using GIT. Do not use the drivers from our SVN repository for 2.6 kernels, it
+will not work. The user-space tools will work though.
+
+We presume that you have Subversion installed on your machine. To check out a
+working copy of the code for this project use the following commands:
+
+svn checkout http://lm-sensors.org/svn/i2c/trunk i2c
+svn checkout http://lm-sensors.org/svn/lm-sensors/trunk lm-sensors
+
+To update the code, do a svn update from within the i2c or lm-sensors
+directories. Please read the README and INSTALL files of your working copy
+for how to build the code. Also read the related files in the 'doc' directory
+for info regarding the different bus and chip drivers. Lastly, you can use
+the regular svn commands on the files (like svn log [filename]) to see the
+comments made by the developers as changes were made.
+
+If you're not familiar with the use of Subversion we recommend the excellent
+Subversion book which is a valuable resource.
+(http://svnbook.red-bean.com/)
+
+Commits to the repositories are notified on the lm-sensors-notify
+mailing-list. Feel free to subscribe if you want to be informed of the
+changes made to the i2c and lm-sensors repositories. 
+(http://lists.lm-sensors.org/mailman/listinfo/lm-sensors-notify)
Index: /lm-sensors/tags/V3-3-0/doc/temperature-sensors
===================================================================
--- /lm-sensors/tags/V3-3-0/doc/temperature-sensors	(revision 1103)
+++ /lm-sensors/tags/V3-3-0/doc/temperature-sensors	(revision 1103)
@@ -0,0 +1,107 @@
+Thermistors and Transistors as Temperature Sensors
+--------------------------------------------------
+Temperature measuring chips that use external sensors
+generally are designed to use either thermistors or
+transistors. Check the driver documentation in doc/chips
+or the actual data sheets to determine which.
+
+Exceptions are the Winbond W83782D and W83783S which can use either.
+These chips default to termistors but can be configured to use
+Pentium II / Celeron diodes or 3904 transistors.
+
+Thermistors are about 10 times more sensitive than
+diodes or transistors at room temperature.
+
+
+Thermistors
+-----------
+(summarized from http://www.thermometrics.com/htmldocs/ntcapp.htm  -
+ most thermistor companies don't have much on their web sites but
+ Thermometrics has a comprehensive guide)
+
+NTC (negative temperature coefficient) thermistors decrease their
+resistance with an increase in temperature.
+They commonly have a parameter B (beta), units are K (kelvin).
+B is the "material constant" of the thermistor and
+is a measure of the change of resistance with respect to temperature.
+It is the slope of the line graphing (ln R) vs. (1/T).
+
+B values for three common termistor materials are
+approximately 3400K, 3900K, and 4300K.
+
+The other measure of thermistors is the temperature coefficient 'a' (alpha).
+This is the percentage change in resistance for a given change
+in temperature. This varies with temperature.
+
+T = temp in Kelvin (C + 273)
+
+A = (dR/dT) / R
+
+A = - B / T**2
+
+So by the last equation, a thermistor with a Beta of 3900 will change
+resistance about 4.4% for 1 degree change at 25C (298K).
+For a beta of 3435 as recommended in the
+Winbond W83782D data sheet the change is 3.8%.
+
+For thermistors, resistance (= voltage for a constant current)
+is exponentially related to temperature.
+
+PTC (positive temperature coefficient) thermistors increase
+in resistance with an increase in temperature. They are not
+commonly used for accurate temperature sensing.
+
+
+Transistors / Diodes
+--------------------
+To use a transistor as a sensor, hook the base of an NPN transistor
+(such as the 3904) to the collector.
+
+This is from Richard Dorf, "Electrical Engineering Handbook".
+
+For a diode, the voltage Vd = Vt ln(Id/Is).
+Vt = kT/q where k = Boltzman's constant 1.38 e-23;
+                q = elementary charge 1.6 e-19;
+                T = temp in kelvin.
+		Is is reverse saturation current.
+
+(more math and greek letters I don't understand completely)
+
+dVd/dT works out to, at room temperature, about 2.1 mV/degree
+or a temperature coefficient of about 0.32%/degree.
+
+For diodes, voltage is linear with temperature.
+
+
+
+Winbond W83782D and W83783S
+---------------------------
+These chips default to thermistors with Beta = 3435.
+Each sensor on the chip can be individually set to be 
+a diode, transistor, or thermistor.
+If the temperature reading changes much less than expected,
+the sensor type probably needs to be changed to a diode.
+See doc/chips/w83781d for details.
+
+
+Summary
+-------
+Thermistors are about 10 times more sensitive than
+diodes or transistors at room temperature.
+
+Thermistors change resistance exponentially with respect to temperature.
+Diodes and transistors change voltage linearly with respect to temperature.
+
+Sensors built into chips (including Pentium II / Celeron processors)
+are always diodes/transistors because that's what's on a chip
+already - transistors.
+
+External, discrete sensors can be either diodes/transistors or
+thermistors. Generally, sensor chips are designed to be connected
+to one or the other and cannot be configured. The Winbond
+W83782D/W83783S chips are exceptions, they can be
+configured for either.
+
+
+------------------
+Copyright (c) 1999, 2001 Mark D. Studebaker <mdsxyz123@yahoo.com>
Index: /lm-sensors/tags/V3-3-0/doc/developers/makefiles
===================================================================
--- /lm-sensors/tags/V3-3-0/doc/developers/makefiles	(revision 5055)
+++ /lm-sensors/tags/V3-3-0/doc/developers/makefiles	(revision 5055)
@@ -0,0 +1,54 @@
+The Makefiles in this package are rather advanced. They are partially based
+on the article "Recursive Make considered Harmful", written by Peter Miller:
+  http://miller.emu.id.au/pmiller/books/rmch/
+
+There is one big Makefile in the root of this package. It includes many
+other files; one for each directory in which source code is found. These
+included files are called 'Module.mk'.
+
+There are several interesting targets defined through this Makefile:
+  * all
+    Create everything in all directories.
+  * install
+    Install everything from all directories.
+  * uninstall
+    Uninstall everything.
+  * clean
+    Remove anything which can be regenerated from all directories. A call
+    of 'make clean' (without any other targets) will ignore any .d files;
+    this is useful when they are out of date (and prevent the calling of
+    any other target).
+
+The best way to understand the Module.mk subfiles is to examine one of them,
+for example lib/Module.mk. They are not too difficult to understand.
+
+There are several variables which can be set in the main Makefile. You can
+also specify them on the command-line; this overrules any definitions
+within the Makefile. For example: 'make all WARN=1' will enable all warnings.
+Examine main Makefile to see which ones are available. The most important
+ones for developers:
+  * WARN
+    Set to 1 to enable many compiler warnings.
+  * DEBUG
+    Set to 1 to enable any debugging code. Note that debugging code should
+    only output more information, and never make the code mis-behave.
+
+Several files are generated by Makefiles:
+  * .rd
+    Dependency files for executables. Automatically generated.
+  * .ad
+    Dependency files for static libraries. Automatically generated.
+  * .ld
+    Dependency files for shared libraries. Automatically generated.
+  * .ro
+    Object files for executables. They will be linked together to create
+    the executable.
+  * .ao
+    Object files for static libraries
+  * .lo
+    Object files for shared libraries
+The reason for using different extensions is to make the Makefile much
+simpler.
+
+There are lots of comments within the main Makefile. Please read them if
+you want to know more.
Index: /lm-sensors/tags/V3-3-0/doc/developers/applications
===================================================================
--- /lm-sensors/tags/V3-3-0/doc/developers/applications	(revision 5056)
+++ /lm-sensors/tags/V3-3-0/doc/developers/applications	(revision 5056)
@@ -0,0 +1,70 @@
+How to write applications which use the hwmon drivers
+=====================================================
+
+You have several choices in accessing sensor devices using the hwmon drivers.
+This document will briefly describe these methods, their advantages and
+disadvantages, and provide examples.
+
+From lowest-level to the highest-level, the access methods are:
+
+   1. sysfs access
+   2. libsensors library
+   3. sensors program
+
+
+Details:
+
+1. sysfs access
+---------------
+   Hwmon drivers create subdirectories in the sysfs filesystem (usually /sys)
+   which can be accessed directly by applications.
+   Naming and content standards for the entries in these subdirectories
+   are documented in the file Documentation/hwmon/sysfs-interface in the 2.6
+   kernel source tree. sysfs is the lowest-level access to sensor values.
+
+   Note that most drivers provide only raw sensor readings via /sys;
+   many readings must be scaled or adjusted, and these adjustments
+   must often be changed by the user. An application using /sys must
+   generally provide adjustment facilities and the requirements
+   of the adjustments can be quite complex. For this reason, this
+   approach isn't recommended. Instead, consider the libsensors library,
+   which offers a higher-level interface on top of sysfs (see below.)
+
+   Still, this method may work well for shell and perl scripts written to
+   access a specific device.
+
+   For an example of a program using /sys accesses, see gkrellm (though
+   recent versions use libsensors instead.) See also lib/sysfs.c.
+
+2. libsensors library
+---------------------
+   The libsensors library provides standardized access to all chip drivers.
+   It also provides a translation layer with settings in /etc/sensors.conf
+   so that your application sees adjusted (scaled) values using settings
+   provided by the user. Other facilities are sensor labelling, limit setting,
+   and ignoring individual sensors. This is the recommended way to access
+   the sensor chips.
+
+   The library API is documented in the libsensors(3) manual page. The
+   configuration file format is documented in the sensors.conf(5) manual
+   page.
+
+   Note that libsensors falls under the GPL, not the LGPL.
+   In more human language, that means it is FORBIDDEN to link any application
+   to the library, even to the shared version, if the application itself
+   does not fall under the GPL. This may or may not be changed in the future.
+   Contact us if you wish to discuss your application.
+
+   For examples of programs using libsensors accesses, see prog/sensors and
+   prog/sensord.
+
+3. sensors program
+------------------
+   The 'sensors' program is a text-based application that uses libsensors.
+   The output is fairly standardized; therefore this output could be
+   used by other applications.
+   One simple method is 'sensors|grep ALARM'.
+   Note though that there is no guarantee that the output format won't change
+   in the future, so be careful. We might improve the command-line interface
+   of 'sensors' someday to make it more convenient for external programs to
+   use it as a back-end.
Index: /lm-sensors/tags/V3-3-0/doc/developers/release_checklist
===================================================================
--- /lm-sensors/tags/V3-3-0/doc/developers/release_checklist	(revision 5866)
+++ /lm-sensors/tags/V3-3-0/doc/developers/release_checklist	(revision 5866)
@@ -0,0 +1,47 @@
+CHECKLIST FOR NEW RELEASES
+==========================
+
+Release steps:
+- Check out lm-sensors
+- Update if necessary, the version number of libsensors
+  (edit lib/Module.mk and change LIBMAINVER and/or LIBMINORVER)
+  Remember: update main number when interface changes, minor if new
+  functionality is added, and patch if only bugs are fixed.
+  `svn diff -r Vx-y-z lib' is your friend.
+- Update the CHANGES file (set version and release date)
+- Update lm_sensors.lsm (Version, Entered-date and Primary-site)
+- Update libsensors-API.txt and SENSORS_API_VERSION in lib/sensors.h
+- Update version.h
+- Commit
+- Tag the release using SVN:
+  svn copy http://lm-sensors.org/svn/lm-sensors/trunk \
+      http://lm-sensors.org/svn/lm-sensors/tags/V3-x-x \
+      -m "3.x.x release"
+
+- Move to a temporary directory and get a copy of the freshly tagged code:
+  svn export http://lm-sensors.org/svn/lm-sensors/tags/V3-x-x lm_sensors-3.x.x
+- Tar it up:
+  tar -jcf lm_sensors-3.x.x.tar.bz2 lm_sensors-3.x.x
+- Sign it:
+  gpg -b lm_sensors-3.x.x.tar.bz2
+- Post it:
+  scp lm_sensors-3.x.x.tar.bz2{,.sig} \
+      devel.atrpms.net:/srv/lm-sensors.org/dl/lm-sensors/releases
+- Set correct permissions:
+  ssh devel.atrpms.net chmod 664 \
+      '/srv/lm-sensors.org/dl/lm-sensors/releases/lm_sensors-3.x.x.tar.bz2{,.sig}'
+
+After release, remember to:
+- Copy to mirrors
+- Update the Download page on lm-sensors.org, then add a news item
+- Announce on the lm-sensors mailing list
+- Announce on Freshmeat
+- Add "+SVN" to version.h and commit
+- Mark the corresponding release milestone as complete using trac-admin
+  e.g. 'trac-admin /srv/lm-sensors.org/trac milestone completed 3.0.0 now'
+- Create a new version for the bug tracking system using trac-admin
+  e.g. 'trac-admin /srv/lm-sensors.org/trac version add 3.0.0 now'
+- Create a new release milestone using trac-admin
+  e.g. 'trac-admin /srv/lm-sensors.org/trac milestone add 3.0.1 2008-02-15'
+- Send lm_sensors.lsm to the LSM by mailing it to lsm@qqx.org with the
+  subject `add' (no quotes)
Index: /lm-sensors/tags/V3-3-0/doc/developers/lm_sensors.lsm
===================================================================
--- /lm-sensors/tags/V3-3-0/doc/developers/lm_sensors.lsm	(revision 5949)
+++ /lm-sensors/tags/V3-3-0/doc/developers/lm_sensors.lsm	(revision 5949)
@@ -0,0 +1,18 @@
+Begin4
+Title: lm_sensors
+Version: 3.3.0
+Entered-date: 2011-03-28
+Description: Hardware health monitoring package for Linux. It consists of 
+             a library to allow applications to read sensors data more
+             easily, and of an example program to pretty print this data.
+             Also included is a fan speed control script.
+             http://www.lm-sensors.org/
+Keywords: sensors health hardware-monitoring fan-speed
+Author: khali@linux-fr.org (Jean Delvare)
+Maintained-by: khali@linux-fr.org (Jean Delvare)
+Primary-site: http://dl.lm-sensors.org /lm-sensors/releases/
+              lm_sensors-3.3.0.tar.bz2
+              lm_sensors-3.3.0.tar.bz2.sig
+Alternate-site: ftp.netroedge.com /pub/lm-sensors/
+Copying-policy: GPL/LGPL
+End
Index: /lm-sensors/tags/V3-3-0/doc/libsensors-API.txt
===================================================================
--- /lm-sensors/tags/V3-3-0/doc/libsensors-API.txt	(revision 5949)
+++ /lm-sensors/tags/V3-3-0/doc/libsensors-API.txt	(revision 5949)
@@ -0,0 +1,88 @@
+libsensors API history
+======================
+
+SENSORS_API_VERSION tracks the evolutions made to the libsensors API
+over time. This document summarizes these evolutions so that application
+authors can quickly figure out how to test for the availability of a
+given new feature.
+
+0x431	lm-sensors 3.3.0
+* Added support for intrusion detection
+  enum sensors_feature_type SENSORS_FEATURE_INTRUSION
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_INTRUSION_ALARM
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_INTRUSION_BEEP
+* Added support for various new sysfs attributes
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_IN_LCRIT
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_IN_CRIT
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_IN_LCRIT_ALARM
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_IN_CRIT_ALARM
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_TEMP_LCRIT
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_TEMP_EMERGENCY
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_CAP
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_CAP_HYST
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_MAX
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_CRIT
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_ALARM
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_CAP_ALARM
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_MAX_ALARM
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_CRIT_ALARM
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_CURR_LCRIT
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_CURR_CRIT
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_CURR_LCRIT_ALARM
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_CURR_CRIT_ALARM
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_FAN_PULSES
+* Added support for humidity sensors
+  enum sensors_feature_type SENSORS_FEATURE_HUMIDITY
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_HUMIDITY_INPUT
+
+0x430	lm-sensors 3.2.0
+* License changed from GPL to LGPL
+
+0x421	lm-sensors 3.1.2
+* Added bus type "hid":
+  #define SENSORS_BUS_TYPE_HID
+
+0x420	lm-sensors 3.1.1
+* Added a method to free the memory allocated by sensors_parse_chip_name()
+  void sensors_free_chip_name(sensors_chip_name *chip);
+
+0x410	lm-sensors 3.1.0
+* Added bus type "acpi":
+  #define SENSORS_BUS_TYPE_ACPI
+* Added support for instantaneous power sensors
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_INPUT
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_INPUT_HIGHEST
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_INPUT_LOWEST
+* Added support for current sensors
+  enum sensors_feature_type SENSORS_FEATURE_CURR
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_CURR_INPUT
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_CURR_MIN
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_CURR_MAX
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_CURR_ALARM
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_CURR_MIN_ALARM
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_CURR_MAX_ALARM
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_CURR_BEEP
+* Added error value for excessive recursion depth
+  #define SENSORS_ERR_RECURSION 11
+* Added parse error reporting function including the configuration file
+  name
+  extern void (*sensors_parse_error_wfn) (const char *err,
+  					  const char *filename, int lineno);
+
+0x401	lm-sensors 3.0.2 to 3.0.3
+* Added bus type "virtual":
+  #define SENSORS_BUS_TYPE_VIRTUAL
+* Added support for power and energy sensors
+  enum sensors_feature_type SENSORS_FEATURE_POWER
+  enum sensors_feature_type SENSORS_FEATURE_ENERGY
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_AVERAGE
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_AVERAGE_HIGHEST
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_AVERAGE_LOWEST
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL
+  enum sensors_subfeature_type SENSORS_SUBFEATURE_ENERGY_INPUT
+
+0x400	lm-sensors 3.0.0 to 3.0.1
+* Initial API.
Index: /lm-sensors/tags/V3-3-0/doc/progs
===================================================================
--- /lm-sensors/tags/V3-3-0/doc/progs	(revision 5643)
+++ /lm-sensors/tags/V3-3-0/doc/progs	(revision 5643)
@@ -0,0 +1,64 @@
+This is a list of additional programs, that may or may not be installed by
+`make install', and that can be found in the `prog' directory of
+this package.
+
+These programs are generally small utilities used for debugging,
+and installation of the lm_sensors package, and for demonstrating
+the use of the drivers and libraries in this package.
+For more elaborate programs (for example, GUI sensor displays),
+see http://www.lm-sensors.org/wiki/UsefulLinks.
+
+
+* prog/daemon/healthd.sh (written in bash, not installed)
+  An example of a very simple hardware health monitoring daemon.
+
+* prog/detect/sensors-detect (written in Perl, installed by 'make install')
+  This program tries to detect the available SMBus adapters and the chips
+  connected to them, as well as Super-I/O and misc chips. It also generates
+  modprobe lines and module options. This program is stand-alone (can be
+  run without installing lm-sensors) and interactive.
+
+* prog/dump/isadump (written in C, installed by 'make install')
+  This program dumps the registers of ISA or Super-I/O chips.
+  Usual syntax:
+     isadump 0x295 0x296
+     isadump -k 0x55 0x2e 0x2f
+
+* prog/hotplug/unhide_ICH_SMBus (shell script, not installed)
+  It unhides the ICH Intel SMBus for 2.6 kernels
+
+* prog/init/lm_sensors.init (shell script, not installed)
+  A SysV init script to be installed in /etc/rc.d/init.d/
+  It inserts the bus and chip modules.
+
+* prog/init/sensord.init (shell script, not installed)
+  A SysV init script to be installed in /etc/rc.d/init.d/
+  It inserts the bus and chip modules and starts the sensord daemon.
+
+* prog/pwm/pwmconfig (shell script, installed by 'make install')
+  Formerly pwmtest, tests the pwm (pulse width modulation) outputs of sensors
+  for their effect on the fans and helps to setup the configfile for fancontrol.
+
+* prog/pwm/fancontrol (shell script, installed by 'make install')
+  Controls fanspeeds responding to changes on temperature sensors.
+  Configuration through pwmconfig.
+
+* prog/rrd/sens_update_rrd (written in bash, not installed)
+  This script gathers sensors data and adds it to a round robin database
+  (RRD). RRD is then used to display the graphed data in web pages.
+  See prog/rrd/README for more information.
+
+* prog/sensord/sensord (written in C, installed by `make install-prog-sensord')
+  This daemon can be used to periodically log sensor readings from
+  the sensors hardware to syslog, and to alert (using syslog level ALERT)
+  when a sensor alarm is signalled; for example, if a fan fails, a temperature
+  limit is exceeded, etc.
+
+* prog/sensors/sensors (written in C, installed by `make install')
+  This pretty-prints the information provided by the modules. It is a normal
+  console application, using stdout. It is essential, because
+  unlike the raw sysfs files, it takes your configuration file into account.
+
+* prog/tellerstats/gather.sh, tellerstats.sh (written in bash, not installed)
+  These scripts gather sensors data and graph the data for web pages.
+  See prog/tellerstats/README for more information.
Index: /lm-sensors/tags/V3-3-0/doc/donations
===================================================================
--- /lm-sensors/tags/V3-3-0/doc/donations	(revision 5912)
+++ /lm-sensors/tags/V3-3-0/doc/donations	(revision 5912)
@@ -0,0 +1,126 @@
+The lm_sensors team gratefully acknowledges the following donations.
+
+Drivers are very difficult to develop without actual hardware.
+Generally, the work goes quickly once we have the hardware.
+
+Evaluation boards are fine. Real computer hardware is much better though,
+because it lets us do real-world testing over a longer period of time.
+In many cases, the way a chipset is integrated in systems must be taken
+into account in the driver, and evaluation boards don't help much there.
+Donation of usable computer hardware also supports the project in general,
+so hardware which isn't directly related to the development of a hardware
+monitoring driver, but can be used to build test systems, is welcome too.
+
+If you would like to make a donation, please contact us. See
+http://www.lm-sensors.org/wiki/AuthorsAndContributors. Thanks!
+
+
+Donations are listed in chronological order. Note that the person mentioned
+is the one who holds the device now, which is not necessarily the one to whom
+the donation was sent in the first place.
+
+
+Donation from	Donation and subsequent development work	Date & Person
+-------------	----------------------------------------	-------------
+
+TI		i810 motherboard w/ 366 Celeron			1999-07
+		Drivers developed: i801, i810, w83627hf		??
+
+AMD		various sensor evaluation boards		2000
+		Drivers developed: various			??
+
+SiS		SiS 540/630 motherboard w/ 5595			2000-01
+		Drivers developed: sis5595 improvements		Phil
+
+Cendio Systems 	Asus P3B-F w/ AS99127F				2000-05
+		Drivers developed: as99127f improvements	MDS
+
+Atipa		SuperMicro 370DLE w/ Serverworks, LM87		2000-09
+		Drivers developed: serverworks support added	Jean Delvare
+		to i2c-piix4; lm87 testing and improvements
+		(on-board NIC broken)
+
+Fujitsu		Complete system for general project support	2001-11
+Siemens		(broken)					Phil
+Computers
+
+HardData Ltd.	Tyan 2466 w/ 1G Duron; W83627HF and W83782D	2002-05
+		Drivers developed: Tyan 2466 initialization;	MDS
+		DDR recognition
+
+Intel		SMBus 2.0 system				2002-06
+		Drivers developed: SMBus 2.0 support in		MDS
+		i2c-core and i2c-i801; ADM1025 improvements;
+		82801 improvements; smsc47m1 driver
+
+VIA		8231 and two 8233 motherboards			2002-06
+		Drivers developed: VT8231 support in via686a;	MDS
+		vt1211; VT8233A and VT8231 support in
+		i2c-viapro
+
+HP		IPMI system					2002-10
+		Drivers developed: bmcsensors, i2c-ipmi		MDS
+
+Analog Devices	ADM1032 evaluation board			2003-10
+		Drivers developed: ADM1032 support in lm90;	Jean Delvare
+		lm90 ported to Linux 2.6
+
+National	LM83 evaluation board				2003-12
+Semiconductor	Drivers developed: lm83 testing and		Jean Delvare
+		improvements; lm83 ported to Linux 2.6
+
+Analog Devices	ADM1025 evaluation board			2004-01
+		Drivers developed: adm1025 testing and		Jean Delvare
+		improvements; adm1025 ported to Linux 2.6
+
+Analog Devices	ADT7467/8 evaluation board			2004-01
+		Drivers developed: nothing yet			Jean Delvare
+
+Analog Devices	ADM1031 evaluation board			2004-04
+		Drivers developed: adm1031 for Linux 2.4 and	Jean Delvare
+		2.6
+		(broken)
+
+Wincor Nixdorf	Seagate Barracuda 7200.7 160GB harddisk drive	2004-10
+		(dead)						Jean Delvare
+
+Aweta		Intel Server system				2005-09
+		Drivers developed: PC87431 (IPMI SMB)		MDS
+
+Barracuda	Jetway K8M8MS, Sempron 2600+, 256 MB RAM	2005-10
+Networks	Drivers developed: f71805f			Jean Delvare
+
+Winbond		Celeron CPU, 512MB RAM				2006-03
+		W83792D watchdog and VID support		Rudolf Marek
+
+ASUSTeK		ASUS P5MT-S					2006-03
+		Platform for W83792D related development	Rudolf Marek
+
+Winbond		Asus P4P800-X, Pentium 4 2.8 GHz, 512 MB RAM	2006-08
+		This board has an (hidden) Intel 82801EB	Jean Delvare
+		(ICH5) SMBus with SPD EEPROMs connected, and
+		a Winbond W83627THF hardware monitoring chip.
+
+Observit	Intel D865GSA (dead), Celeron D-352,		2007-06
+		128 MB RAM					Jean Delvare
+		Resulted in: backport of the w83627ehf driver
+		to Linux 2.4, improvements to the Linux 2.6
+		w83627ehf driver.
+
+Observit	Intel D865GBF, Celeron 2.4 GHz, 512 MB RAM	2007-06
+		Resulted in: improvements to the lm85 driver.	Jean Delvare
+
+Nuvoton		2 x Asus P5QL Pro, 1 GB RAM,			2009-07
+		Pentium D-945, video card (dead)		Jean Delvare
+		This board has an ATK0110 ACPI device backed
+		by a Winbond W83667HG.
+
+ASUSTeK		ASUS Z8NA-D6, 2 x Xeon E5520, 2 x 1 GB RAM	2010-04
+		This board has a Winbond W83795ADG, SMBus is	Jean Delvare
+		multiplexed with GPIOs.
+		Drivers developed: w83795
+
+lleo19		Intel DH57JG with i3-540 CPU, 4GB RAM, chassis,	2011-01
+		and power supply.				Guenter Roeck
+		Used to add support for NCT6775F (W83677HG-I)
+		to w83627ehf driver.
Index: /lm-sensors/tags/V3-3-0/INSTALL
===================================================================
--- /lm-sensors/tags/V3-3-0/INSTALL	(revision 5072)
+++ /lm-sensors/tags/V3-3-0/INSTALL	(revision 5072)
@@ -0,0 +1,83 @@
+INSTALLATION INSTRUCTIONS
+=========================
+
+The lm-sensors package, version 3, provides user-space support for the
+hardware monitoring drivers in Linux 2.6.5 and later. For older kernel
+versions, you have to use lm-sensors version 2.
+
+
+Dependencies
+============
+
+Build-time dependencies:
+* GNU make
+* gcc
+* bison
+* flex
+* rrd header files (optional, for sensord)
+
+Run-time dependencies:
+* perl (for sensors-detect)
+* rrdtool >= 1.2.1 (optional, for sensord)
+* proper kernel configuration (see below)
+
+
+Compilation
+===========
+
+At the top of the Makefile are a couple of configuration variables that
+you may want to change. There's a description of what each variable does
+in the Makefile itself.
+
+Compilation is done by `make all'. You will get a lot of warnings about
+files which are not found, all ending in `.*d'. You can safely ignore
+this; they contain dependency information, which is regenerated on the
+spot.
+
+`make install' installs the package (to /usr/local by default).
+
+
+Kernel configuration
+====================
+
+This package assumes that you have a properly configured kernel. If
+you run a distribution kernel, that should be the case. If you're
+building your own kernel, here are some recommendations:
+* Enable "I2C support" (CONFIG_I2C=y or m). On many motherboards, the
+  sensor chip is connected to the SMBus, which is supported by I2C
+  in the Linux kernel.
+* Enable "I2C device interface" (CONFIG_I2C_CHARDEV=m). sensors-detect
+  needs this to probe for SMBus hardware monitoring chips.
+* In I2C Hardware Bus support, enable all drivers you might need,
+  preferably as modules. If you're not sure, select them all.
+* Enable "Hardware Monitoring support" (CONFIG_HWMON=y or m).
+* Enable all hardware monitoring drivers you might need, preferably
+  as modules. If you're not sure, select them all.
+
+
+Using the sensors package
+=========================
+
+There is a scanning program installed called sensors-detect. It
+will scan all available I2C and SMBus adapters for all known sensor
+devices, and will also look for ISA, PCI and Super-I/O chips with
+sensors, and give you a list of what kernel drivers you need to load
+(using modprobe).
+
+After loading the suggested drivers, you can use the installed sensors
+program to get a report of all detected sensor devices. Check the manual
+page for available options.
+
+The initial output of `sensors' will not be perfect. You have to adjust
+the configuration file (/etc/sensors3.conf) to match your motherboard.
+This includes (re)labelling inputs, ignoring unused inputs, changing
+voltage compute lines and setting limits. Write down all the sensor
+information your BIOS displays as a hint to what you are supposed to
+obtain in the end. Make sure you modify the right chip section.
+
+Once you are done with editing the configuration file, calling
+`sensors -s' will set the new limits. Then the output of `sensors'
+should look much better.
+
+There are many auxiliary programs not installed. You can find them under
+the prog subdirectory. A list can be found in doc/progs.
Index: /lm-sensors/tags/V3-3-0/lib/test/test-scanner.pl
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/test-scanner.pl	(revision 5163)
+++ /lm-sensors/tags/V3-3-0/lib/test/test-scanner.pl	(revision 5163)
@@ -0,0 +1,101 @@
+#!/usr/bin/perl -w
+
+# test-scanner.pl - test script for the libsensors config-file scanner
+# Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston,
+#    MA 02110-1301 USA.
+#
+
+require 5.004;
+
+use strict;
+use Test::More;
+use Test::Cmd;
+
+my ($scenario, $test);
+my @scenarios = (
+	{ base => 'empty', status => 0,
+		desc => 'empty file' },
+	{ base => 'comment', status => 0,
+		desc => 'one comment line' },
+	{ base => 'comment-without-eol', status => 0,
+		desc => 'one comment line, without a trailing newline' },
+	{ base => 'keywords', status => 0,
+		desc => 'keywords with various whitespace variations' },
+	{ base => 'non-keywords', status => 0,
+		desc => 'various invalid keyword scenarios' },
+	{ base => 'names', status => 0,
+		desc => 'normal, unquoted names' },
+	{ base => 'names-errors', status => 0,
+		desc => 'invalid, unquoted names' },
+	{ base => 'names-quoted', status => 0,
+		desc => 'normal, quoted names' },
+	{ base => 'names-quoted-errors', status => 0,
+		desc => 'invalid, quoted names' },
+);
+
+plan tests => ($#scenarios + 1) * 3;
+
+chomp(my $valgrind = `which valgrind 2>/dev/null`);
+
+if ($valgrind) {
+	$test = Test::Cmd->new(prog => "$valgrind --tool=memcheck --show-reachable=yes --leak-check=full --quiet ./test-scanner", workdir => '');
+} else {
+	diag("Couldn't find valgrind(1), running tests without it...");
+	$test = Test::Cmd->new(prog => "test-scanner", workdir => '');
+}
+
+foreach $scenario (@scenarios) {
+	my ($filename, @stdin, @stdout, @expout, @stderr, @experr, @diff);
+
+	$filename = $scenario->{"base"} . ".conf";
+	open INPUT, "< $filename" or die "Cannot open $filename: $!";
+	@stdin = <INPUT>;
+	close INPUT or die "Cannot close $filename: $!";
+
+	$filename = $scenario->{"base"} . ".conf.stdout";
+	open OUTPUT, "< $filename" or die "Cannot open $filename: $!";
+	@expout = <OUTPUT>;
+	close OUTPUT or die "Cannot close $filename: $!";
+
+	# if stderr file is not present, assume none is expected
+	$filename = $scenario->{"base"} . ".conf.stderr";
+	if (open ERROR, "< $filename") {
+		@experr = <ERROR>;
+		close ERROR or die "Cannot close $filename: $!";
+	} else {
+		@experr = ();
+	}
+
+	$test->string($scenario->{"desc"});
+	$test->run(stdin => \@stdin);
+
+	# test return status
+	ok($scenario->{"status"} == $?, "status: " . $scenario->{"desc"});
+
+	# force the captured outputs into an array - for some reason, the
+	# 'standard invocation' of diff_exact() chokes without this
+	@stdout = $test->stdout;
+	@stderr = $test->stderr;
+
+	# test stdout
+	ok($test->diff_exact(\@stdout, \@expout, \@diff),
+		"stdout: " . $scenario->{"desc"}) or print @diff;
+
+	# test stderr
+	ok($test->diff_exact(\@stderr, \@experr, \@diff),
+		"stderr: " . $scenario->{"desc"}) or print @diff;
+}
+
Index: /lm-sensors/tags/V3-3-0/lib/test/names-quoted-errors.conf.stdout
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/names-quoted-errors.conf.stdout	(revision 4234)
+++ /lm-sensors/tags/V3-3-0/lib/test/names-quoted-errors.conf.stdout	(revision 4234)
@@ -0,0 +1,19 @@
+1: CHIP
+1: NAME: blah-*
+2: EOL
+4: LABEL
+4: ERROR
+5: EOL
+7: LABEL
+7: NAME: in1
+7: ERROR
+8: EOL
+10: LABEL
+10: NAME: in2
+10: NAME: baz
+11: EOL
+13: LABEL
+13: NAME: in3
+13: ERROR
+13: EOL
+13: EOF
Index: /lm-sensors/tags/V3-3-0/lib/test/names-errors.conf.stdout
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/names-errors.conf.stdout	(revision 4222)
+++ /lm-sensors/tags/V3-3-0/lib/test/names-errors.conf.stdout	(revision 4222)
@@ -0,0 +1,15 @@
+2: LABEL
+2: ERROR
+3: EOL
+3: LABEL
+3: NAME: bar
+3: ERROR
+4: EOL
+4: LABEL
+4: NAME: baz
+4: ERROR
+5: EOL
+5: LABEL
+5: ERROR
+6: EOL
+6: EOF
Index: /lm-sensors/tags/V3-3-0/lib/test/non-keywords.conf
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/non-keywords.conf	(revision 4222)
+++ /lm-sensors/tags/V3-3-0/lib/test/non-keywords.conf	(revision 4222)
@@ -0,0 +1,8 @@
+# these are not keywords
+ignored
+
+labs
+
+extra
+stuff
+
Index: /lm-sensors/tags/V3-3-0/lib/test/comment-without-eol.conf.stdout
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/comment-without-eol.conf.stdout	(revision 4222)
+++ /lm-sensors/tags/V3-3-0/lib/test/comment-without-eol.conf.stdout	(revision 4222)
@@ -0,0 +1,1 @@
+1: EOF
Index: /lm-sensors/tags/V3-3-0/lib/test/comment.conf
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/comment.conf	(revision 4222)
+++ /lm-sensors/tags/V3-3-0/lib/test/comment.conf	(revision 4222)
@@ -0,0 +1,1 @@
+# this is a comment
Index: /lm-sensors/tags/V3-3-0/lib/test/names-quoted.conf
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/names-quoted.conf	(revision 4233)
+++ /lm-sensors/tags/V3-3-0/lib/test/names-quoted.conf	(revision 4233)
@@ -0,0 +1,28 @@
+chip "blah-*"
+
+# keyword with some quoted names
+label "foo" "bar"
+
+# keyword with escaped newline, names, and whitespace
+label		\
+ "foo" "bar"
+
+# quoted names with full range of alpha-numeric and underscores
+label "abcdefg" "hijklmnop"
+label "qrs" "tuv"
+label "wx" "yz"
+label "a0123456789" "982lksdf"
+label "_abcd" "1234_"
+label "_" "foo_bar_baz" 
+label "liajesiajef82197fjadf" "blah"
+
+# quoted names with escaped characters, like C
+# bell, backspace, formfeed, newline, carriage return, h-tab, v-tab, \, ?, ', "
+label escapes "\a\b\f\n\r\t\v\\\?\'\""
+
+# quoted names with escaped chars that have no special significance
+# i.e. the second string below should collapse to "hello"
+label more "\h\e\l\l\o"
+
+# keyword with quoted names, immediate EOF
+label "blue" "green"
Index: /lm-sensors/tags/V3-3-0/lib/test/test-scanner.c
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/test-scanner.c	(revision 5648)
+++ /lm-sensors/tags/V3-3-0/lib/test/test-scanner.c	(revision 5648)
@@ -0,0 +1,106 @@
+/*
+    test-scanner.c - Regression test driver for the libsensors config file scanner.
+    Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston,
+    MA 02110-1301 USA.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../data.h"
+#include "../conf.h"
+#include "../conf-parse.h"
+#include "../scanner.h"
+
+YYSTYPE sensors_yylval;
+
+int main(void)
+{
+	int result;
+
+	/* init the scanner */
+	if ((result = sensors_scanner_init(stdin, NULL)))
+		return result;
+
+	do {
+		result = sensors_yylex();
+
+		printf("%d: ", sensors_yylineno);
+
+		switch (result) {
+
+			case 0:
+				printf("EOF\n");
+				break;
+
+			case NEG:
+				printf("NEG\n");
+				break;
+	
+			case EOL:
+				printf("EOL\n");
+				break;
+	
+			case BUS:
+				printf("BUS\n");
+				break;
+	
+			case LABEL:
+				printf("LABEL\n");
+				break;
+	
+			case SET:
+				printf("SET\n");
+				break;
+	
+			case CHIP:
+				printf("CHIP\n");
+				break;
+	
+			case COMPUTE:
+				printf("COMPUTE\n");
+				break;
+	
+			case IGNORE:
+				printf("IGNORE\n");
+				break;
+	
+			case FLOAT:
+				printf("FLOAT: %f\n", sensors_yylval.value);
+				break;
+	
+			case NAME:
+				printf("NAME: %s\n", sensors_yylval.name);
+				free(sensors_yylval.name);
+				break;
+	
+			case ERROR:
+				printf("ERROR\n");
+				break;
+
+			default:
+				printf("%c\n", (char)result);
+				break;
+		}
+
+	} while (result);
+
+	/* clean up the scanner */
+	sensors_scanner_exit();
+
+	return 0;
+}
+
Index: /lm-sensors/tags/V3-3-0/lib/test/names.conf
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/names.conf	(revision 4222)
+++ /lm-sensors/tags/V3-3-0/lib/test/names.conf	(revision 4222)
@@ -0,0 +1,25 @@
+# keyword with some unquoted names
+label foo bar
+
+# keyword with escaped newline, names, and whitespace
+label		\
+ foo bar
+
+# names with full range of alpha-numeric and underscores
+label \
+	abcdefg \
+	hijklmnop \
+	qrs \
+	tuv \
+	wx \
+	yz \
+	a0123456789 \
+	982lksdf \
+	_abcd \
+	1234_ \
+	_ \
+	foo_bar_baz \
+	liajesiajef82197fjadf
+
+# keyword with names, immediate EOF
+label foo bar
Index: /lm-sensors/tags/V3-3-0/lib/test/Module.mk
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/Module.mk	(revision 4655)
+++ /lm-sensors/tags/V3-3-0/lib/test/Module.mk	(revision 4655)
@@ -0,0 +1,24 @@
+LIB_DIR		:= lib
+LIB_TEST_DIR	:= lib/test
+
+LIB_TEST_TARGETS := $(LIB_TEST_DIR)/test-scanner
+LIB_TEST_SOURCES := $(LIB_TEST_DIR)/test-scanner.c
+
+LIB_TEST_SCANNER_OBJS := \
+	$(LIB_TEST_DIR)/test-scanner.ro \
+	$(LIB_DIR)/conf-lex.ao \
+	$(LIB_DIR)/error.ao \
+	$(LIB_DIR)/general.ao
+
+$(LIB_TEST_DIR)/test-scanner: $(LIB_TEST_SCANNER_OBJS)
+	$(CC) $(EXLDFLAGS) -o $@ $(LIB_TEST_SCANNER_OBJS) -Llib
+
+all-lib-test: $(LIB_TEST_TARGETS)
+user :: all-lib-test
+
+$(LIB_TEST_DIR)/test-scanner.ro: $(LIB_DIR)/data.h $(LIB_DIR)/conf.h $(LIB_DIR)/conf-parse.h $(LIB_DIR)/scanner.h
+
+clean-lib-test:
+	$(RM) $(LIB_TEST_DIR)/*.rd $(LIB_TEST_DIR)/*.ro 
+	$(RM) $(LIB_TEST_TARGETS)
+clean :: clean-lib-test
Index: /lm-sensors/tags/V3-3-0/lib/test/keywords.conf
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/keywords.conf	(revision 4222)
+++ /lm-sensors/tags/V3-3-0/lib/test/keywords.conf	(revision 4222)
@@ -0,0 +1,42 @@
+# chip keyword on a line by itself
+chip
+
+# preceded by whitespace
+	 chip
+
+# with trailing whitespace
+chip	 
+
+# ditto for the rest of the keywords
+label
+
+	  label
+
+label	 	
+
+set
+
+ set
+
+set 
+
+compute
+
+	compute
+
+compute	
+
+bus
+
+	  	bus
+
+bus	  	
+
+ignore
+
+	ignore
+
+ignore	
+
+# keyword followed by EOL/EOF
+chip
Index: /lm-sensors/tags/V3-3-0/lib/test/names-quoted-errors.conf
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/names-quoted-errors.conf	(revision 4234)
+++ /lm-sensors/tags/V3-3-0/lib/test/names-quoted-errors.conf	(revision 4234)
@@ -0,0 +1,13 @@
+chip "blah-*"
+
+# keyword with two quoted names, no intermediate whitespace
+label "in0""foo"
+
+# missing closing quote before EOL
+label "in1" "bar
+
+# (correct label statement, just for separation)
+label "in2" "baz"
+
+# missing closing quote before immediate EOF
+label "in3" "baz
Index: /lm-sensors/tags/V3-3-0/lib/test/names-errors.conf
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/names-errors.conf	(revision 4222)
+++ /lm-sensors/tags/V3-3-0/lib/test/names-errors.conf	(revision 4222)
@@ -0,0 +1,5 @@
+# keyword with bogus names
+label ?foo
+label bar%
+label baz$foo
+label !
Index: /lm-sensors/tags/V3-3-0/lib/test/empty.conf.stdout
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/empty.conf.stdout	(revision 4222)
+++ /lm-sensors/tags/V3-3-0/lib/test/empty.conf.stdout	(revision 4222)
@@ -0,0 +1,1 @@
+1: EOF
Index: /lm-sensors/tags/V3-3-0/lib/test/comment-without-eol.conf
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/comment-without-eol.conf	(revision 4222)
+++ /lm-sensors/tags/V3-3-0/lib/test/comment-without-eol.conf	(revision 4222)
@@ -0,0 +1,1 @@
+# this is a comment without a trailing newline 
Index: /lm-sensors/tags/V3-3-0/lib/test/non-keywords.conf.stdout
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/non-keywords.conf.stdout	(revision 4222)
+++ /lm-sensors/tags/V3-3-0/lib/test/non-keywords.conf.stdout	(revision 4222)
@@ -0,0 +1,9 @@
+2: ERROR
+3: EOL
+4: ERROR
+5: EOL
+6: ERROR
+7: EOL
+7: ERROR
+8: EOL
+9: EOF
Index: /lm-sensors/tags/V3-3-0/lib/test/names-quoted.conf.stdout
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/names-quoted.conf.stdout	(revision 4233)
+++ /lm-sensors/tags/V3-3-0/lib/test/names-quoted.conf.stdout	(revision 4233)
@@ -0,0 +1,54 @@
+1: CHIP
+1: NAME: blah-*
+2: EOL
+4: LABEL
+4: NAME: foo
+4: NAME: bar
+5: EOL
+7: LABEL
+8: NAME: foo
+8: NAME: bar
+9: EOL
+11: LABEL
+11: NAME: abcdefg
+11: NAME: hijklmnop
+12: EOL
+12: LABEL
+12: NAME: qrs
+12: NAME: tuv
+13: EOL
+13: LABEL
+13: NAME: wx
+13: NAME: yz
+14: EOL
+14: LABEL
+14: NAME: a0123456789
+14: NAME: 982lksdf
+15: EOL
+15: LABEL
+15: NAME: _abcd
+15: NAME: 1234_
+16: EOL
+16: LABEL
+16: NAME: _
+16: NAME: foo_bar_baz
+17: EOL
+17: LABEL
+17: NAME: liajesiajef82197fjadf
+17: NAME: blah
+18: EOL
+21: LABEL
+21: NAME: escapes
+21: NAME: 
+
+	\?'"
+22: EOL
+25: LABEL
+25: NAME: more
+25: NAME: hello
+26: EOL
+28: LABEL
+28: NAME: blue
+28: NAME: green
+28: EOL
+28: EOF
Index: /lm-sensors/tags/V3-3-0/lib/test/comment.conf.stdout
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/comment.conf.stdout	(revision 4222)
+++ /lm-sensors/tags/V3-3-0/lib/test/comment.conf.stdout	(revision 4222)
@@ -0,0 +1,1 @@
+2: EOF
Index: /lm-sensors/tags/V3-3-0/lib/test/names.conf.stdout
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/names.conf.stdout	(revision 4222)
+++ /lm-sensors/tags/V3-3-0/lib/test/names.conf.stdout	(revision 4222)
@@ -0,0 +1,28 @@
+2: LABEL
+2: NAME: foo
+2: NAME: bar
+3: EOL
+5: LABEL
+6: NAME: foo
+6: NAME: bar
+7: EOL
+9: LABEL
+10: NAME: abcdefg
+11: NAME: hijklmnop
+12: NAME: qrs
+13: NAME: tuv
+14: NAME: wx
+15: NAME: yz
+16: NAME: a0123456789
+17: NAME: 982lksdf
+18: NAME: _abcd
+19: NAME: 1234_
+20: NAME: _
+21: NAME: foo_bar_baz
+22: NAME: liajesiajef82197fjadf
+23: EOL
+25: LABEL
+25: NAME: foo
+25: NAME: bar
+25: EOL
+25: EOF
Index: /lm-sensors/tags/V3-3-0/lib/test/keywords.conf.stdout
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/test/keywords.conf.stdout	(revision 4222)
+++ /lm-sensors/tags/V3-3-0/lib/test/keywords.conf.stdout	(revision 4222)
@@ -0,0 +1,39 @@
+2: CHIP
+3: EOL
+5: CHIP
+6: EOL
+8: CHIP
+9: EOL
+11: LABEL
+12: EOL
+13: LABEL
+14: EOL
+15: LABEL
+16: EOL
+17: SET
+18: EOL
+19: SET
+20: EOL
+21: SET
+22: EOL
+23: COMPUTE
+24: EOL
+25: COMPUTE
+26: EOL
+27: COMPUTE
+28: EOL
+29: BUS
+30: EOL
+31: BUS
+32: EOL
+33: BUS
+34: EOL
+35: IGNORE
+36: EOL
+37: IGNORE
+38: EOL
+39: IGNORE
+40: EOL
+42: CHIP
+43: EOL
+43: EOF
Index: /lm-sensors/tags/V3-3-0/lib/scanner.h
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/scanner.h	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/scanner.h	(revision 5844)
@@ -0,0 +1,28 @@
+/*
+    scanner.h - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (c) 2006 Mark M. Hoffman <mhoffman@lightlink.com>
+
+    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 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_SENSORS_SCANNER_H
+#define LIB_SENSORS_SCANNER_H
+
+int sensors_scanner_init(FILE *input, const char *filename);
+void sensors_scanner_exit(void);
+
+#endif
+
Index: /lm-sensors/tags/V3-3-0/lib/error.h
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/error.h	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/error.h	(revision 5844)
@@ -0,0 +1,74 @@
+/*
+    error.h - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    Copyright (C) 2007-2009   Jean Delvare <khali@linux-fr.org>
+
+    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 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_SENSORS_ERROR_H
+#define LIB_SENSORS_ERROR_H
+
+#define SENSORS_ERR_WILDCARDS	1 /* Wildcard found in chip name */
+#define SENSORS_ERR_NO_ENTRY	2 /* No such subfeature known */
+#define SENSORS_ERR_ACCESS_R	3 /* Can't read */
+#define SENSORS_ERR_KERNEL	4 /* Kernel interface error */
+#define SENSORS_ERR_DIV_ZERO	5 /* Divide by zero */
+#define SENSORS_ERR_CHIP_NAME	6 /* Can't parse chip name */
+#define SENSORS_ERR_BUS_NAME	7 /* Can't parse bus name */
+#define SENSORS_ERR_PARSE	8 /* General parse error */
+#define SENSORS_ERR_ACCESS_W	9 /* Can't write */
+#define SENSORS_ERR_IO		10 /* I/O error */
+#define SENSORS_ERR_RECURSION	11 /* Evaluation recurses too deep */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/* This function returns a pointer to a string which describes the error.
+   errnum may be negative (the corresponding positive error is returned).
+   You may not modify the result! */
+const char *sensors_strerror(int errnum);
+
+/* These functions are called when a parse error is detected. Give them new
+   values, and your own functions are called instead of the default (which
+   print to stderr). These functions may terminate the program, but they
+   usually output an error and return. The first function is the original
+   one, the second one was added later when support for multiple
+   configuration files was added.
+   The library code now only calls the second function. However, for
+   backwards compatibility, if an application provides a custom handling
+   function for the first function but not the second, then all parse
+   errors will be reported using the first function (that is, the filename
+   is never reported.)
+   Note that filename can be NULL (if filename isn't known) and lineno
+   can be 0 (if the error occurs before the actual parsing starts.) */
+extern void (*sensors_parse_error) (const char *err, int lineno);
+extern void (*sensors_parse_error_wfn) (const char *err,
+					const char *filename, int lineno);
+
+/* This function is called when an immediately fatal error (like no
+   memory left) is detected. Give it a new value, and your own function
+   is called instead of the default (which prints to stderr and ends
+   the program). Never let it return! */
+extern void (*sensors_fatal_error) (const char *proc, const char *err);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* def LIB_SENSORS_ERROR_H */
Index: /lm-sensors/tags/V3-3-0/lib/sensors.h
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/sensors.h	(revision 5949)
+++ /lm-sensors/tags/V3-3-0/lib/sensors.h	(revision 5949)
@@ -0,0 +1,296 @@
+/*
+    sensors.h - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    Copyright (C) 2007, 2010  Jean Delvare <khali@linux-fr.org>
+
+    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 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_SENSORS_SENSORS_H
+#define LIB_SENSORS_SENSORS_H
+
+#include <stdio.h>
+#include <limits.h>
+
+/* Publicly accessible library functions */
+
+/* libsensors API version define, first digit is the major version (changed
+   when the API + ABI breaks), the third digit is incremented to track small
+   API additions like new flags / enum values. The second digit is for tracking
+   larger additions like new methods. */
+#define SENSORS_API_VERSION		0x431
+
+#define SENSORS_CHIP_NAME_PREFIX_ANY	NULL
+#define SENSORS_CHIP_NAME_ADDR_ANY	(-1)
+
+#define SENSORS_BUS_TYPE_ANY		(-1)
+#define SENSORS_BUS_TYPE_I2C		0
+#define SENSORS_BUS_TYPE_ISA		1
+#define SENSORS_BUS_TYPE_PCI		2
+#define SENSORS_BUS_TYPE_SPI		3
+#define SENSORS_BUS_TYPE_VIRTUAL	4
+#define SENSORS_BUS_TYPE_ACPI		5
+#define SENSORS_BUS_TYPE_HID		6
+#define SENSORS_BUS_NR_ANY		(-1)
+#define SENSORS_BUS_NR_IGNORE		(-2)
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+extern const char *libsensors_version;
+
+typedef struct sensors_bus_id {
+	short type;
+	short nr;
+} sensors_bus_id;
+
+/* A chip name is encoded in this structure */
+typedef struct sensors_chip_name {
+	char *prefix;
+	sensors_bus_id bus;
+	int addr;
+	char *path;
+} sensors_chip_name;
+
+/* Load the configuration file and the detected chips list. If this
+   returns a value unequal to zero, you are in trouble; you can not
+   assume anything will be initialized properly. If you want to
+   reload the configuration file, call sensors_cleanup() below before
+   calling sensors_init() again. */
+int sensors_init(FILE *input);
+
+/* Clean-up function: You can't access anything after
+   this, until the next sensors_init() call! */
+void sensors_cleanup(void);
+
+/* Parse a chip name to the internal representation. Return 0 on success, <0
+   on error. */
+int sensors_parse_chip_name(const char *orig_name, sensors_chip_name *res);
+
+/* Free memory allocated for the internal representation of a chip name. */
+void sensors_free_chip_name(sensors_chip_name *chip);
+
+/* Print a chip name from its internal representation. Note that chip should
+   not contain wildcard values! Return the number of characters printed on
+   success (same as snprintf), <0 on error. */
+int sensors_snprintf_chip_name(char *str, size_t size,
+			       const sensors_chip_name *chip);
+
+/* This function returns the adapter name of a bus,
+   as used within the sensors_chip_name structure. If it could not be found,
+   it returns NULL */
+const char *sensors_get_adapter_name(const sensors_bus_id *bus);
+
+typedef struct sensors_feature sensors_feature;
+
+/* Look up the label for a given feature. Note that chip should not
+   contain wildcard values! The returned string is newly allocated (free it
+   yourself). On failure, NULL is returned.
+   If no label exists for this feature, its name is returned itself. */
+char *sensors_get_label(const sensors_chip_name *name,
+			const sensors_feature *feature);
+
+/* Read the value of a subfeature of a certain chip. Note that chip should not
+   contain wildcard values! This function will return 0 on success, and <0
+   on failure.  */
+int sensors_get_value(const sensors_chip_name *name, int subfeat_nr,
+		      double *value);
+
+/* Set the value of a subfeature of a certain chip. Note that chip should not
+   contain wildcard values! This function will return 0 on success, and <0
+   on failure. */
+int sensors_set_value(const sensors_chip_name *name, int subfeat_nr,
+		      double value);
+
+/* Execute all set statements for this particular chip. The chip may contain
+   wildcards!  This function will return 0 on success, and <0 on failure. */
+int sensors_do_chip_sets(const sensors_chip_name *name);
+
+/* This function returns all detected chips that match a given chip name,
+   one by one. If no chip name is provided, all detected chips are returned.
+   To start at the beginning of the list, use 0 for nr; NULL is returned if
+   we are at the end of the list. Do not try to change these chip names, as
+   they point to internal structures! */
+const sensors_chip_name *sensors_get_detected_chips(const sensors_chip_name
+						    *match, int *nr);
+
+/* These defines are used in the flags field of sensors_subfeature */
+#define SENSORS_MODE_R			1
+#define SENSORS_MODE_W			2
+#define SENSORS_COMPUTE_MAPPING		4
+
+typedef enum sensors_feature_type {
+	SENSORS_FEATURE_IN		= 0x00,
+	SENSORS_FEATURE_FAN		= 0x01,
+	SENSORS_FEATURE_TEMP		= 0x02,
+	SENSORS_FEATURE_POWER		= 0x03,
+	SENSORS_FEATURE_ENERGY		= 0x04,
+	SENSORS_FEATURE_CURR		= 0x05,
+	SENSORS_FEATURE_HUMIDITY	= 0x06,
+	SENSORS_FEATURE_MAX_MAIN,
+	SENSORS_FEATURE_VID		= 0x10,
+	SENSORS_FEATURE_INTRUSION	= 0x11,
+	SENSORS_FEATURE_MAX_OTHER,
+	SENSORS_FEATURE_BEEP_ENABLE	= 0x18,
+	SENSORS_FEATURE_UNKNOWN		= INT_MAX,
+} sensors_feature_type;
+
+/* All the sensor types (in, fan, temp, vid) are a multiple of 0x100 apart,
+   and sensor subfeatures which have no compute mapping have bit 7 set. */
+typedef enum sensors_subfeature_type {
+	SENSORS_SUBFEATURE_IN_INPUT = SENSORS_FEATURE_IN << 8,
+	SENSORS_SUBFEATURE_IN_MIN,
+	SENSORS_SUBFEATURE_IN_MAX,
+	SENSORS_SUBFEATURE_IN_LCRIT,
+	SENSORS_SUBFEATURE_IN_CRIT,
+	SENSORS_SUBFEATURE_IN_ALARM = (SENSORS_FEATURE_IN << 8) | 0x80,
+	SENSORS_SUBFEATURE_IN_MIN_ALARM,
+	SENSORS_SUBFEATURE_IN_MAX_ALARM,
+	SENSORS_SUBFEATURE_IN_BEEP,
+	SENSORS_SUBFEATURE_IN_LCRIT_ALARM,
+	SENSORS_SUBFEATURE_IN_CRIT_ALARM,
+
+	SENSORS_SUBFEATURE_FAN_INPUT = SENSORS_FEATURE_FAN << 8,
+	SENSORS_SUBFEATURE_FAN_MIN,
+	SENSORS_SUBFEATURE_FAN_ALARM = (SENSORS_FEATURE_FAN << 8) | 0x80,
+	SENSORS_SUBFEATURE_FAN_FAULT,
+	SENSORS_SUBFEATURE_FAN_DIV,
+	SENSORS_SUBFEATURE_FAN_BEEP,
+	SENSORS_SUBFEATURE_FAN_PULSES,
+
+	SENSORS_SUBFEATURE_TEMP_INPUT = SENSORS_FEATURE_TEMP << 8,
+	SENSORS_SUBFEATURE_TEMP_MAX,
+	SENSORS_SUBFEATURE_TEMP_MAX_HYST,
+	SENSORS_SUBFEATURE_TEMP_MIN,
+	SENSORS_SUBFEATURE_TEMP_CRIT,
+	SENSORS_SUBFEATURE_TEMP_CRIT_HYST,
+	SENSORS_SUBFEATURE_TEMP_LCRIT,
+	SENSORS_SUBFEATURE_TEMP_EMERGENCY,
+	SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST,
+	SENSORS_SUBFEATURE_TEMP_ALARM = (SENSORS_FEATURE_TEMP << 8) | 0x80,
+	SENSORS_SUBFEATURE_TEMP_MAX_ALARM,
+	SENSORS_SUBFEATURE_TEMP_MIN_ALARM,
+	SENSORS_SUBFEATURE_TEMP_CRIT_ALARM,
+	SENSORS_SUBFEATURE_TEMP_FAULT,
+	SENSORS_SUBFEATURE_TEMP_TYPE,
+	SENSORS_SUBFEATURE_TEMP_OFFSET,
+	SENSORS_SUBFEATURE_TEMP_BEEP,
+	SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM,
+	SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM,
+
+	SENSORS_SUBFEATURE_POWER_AVERAGE = SENSORS_FEATURE_POWER << 8,
+	SENSORS_SUBFEATURE_POWER_AVERAGE_HIGHEST,
+	SENSORS_SUBFEATURE_POWER_AVERAGE_LOWEST,
+	SENSORS_SUBFEATURE_POWER_INPUT,
+	SENSORS_SUBFEATURE_POWER_INPUT_HIGHEST,
+	SENSORS_SUBFEATURE_POWER_INPUT_LOWEST,
+	SENSORS_SUBFEATURE_POWER_CAP,
+	SENSORS_SUBFEATURE_POWER_CAP_HYST,
+	SENSORS_SUBFEATURE_POWER_MAX,
+	SENSORS_SUBFEATURE_POWER_CRIT,
+	SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL = (SENSORS_FEATURE_POWER << 8) | 0x80,
+	SENSORS_SUBFEATURE_POWER_ALARM,
+	SENSORS_SUBFEATURE_POWER_CAP_ALARM,
+	SENSORS_SUBFEATURE_POWER_MAX_ALARM,
+	SENSORS_SUBFEATURE_POWER_CRIT_ALARM,
+
+	SENSORS_SUBFEATURE_ENERGY_INPUT = SENSORS_FEATURE_ENERGY << 8,
+
+	SENSORS_SUBFEATURE_CURR_INPUT = SENSORS_FEATURE_CURR << 8,
+	SENSORS_SUBFEATURE_CURR_MIN,
+	SENSORS_SUBFEATURE_CURR_MAX,
+	SENSORS_SUBFEATURE_CURR_LCRIT,
+	SENSORS_SUBFEATURE_CURR_CRIT,
+	SENSORS_SUBFEATURE_CURR_ALARM = (SENSORS_FEATURE_CURR << 8) | 0x80,
+	SENSORS_SUBFEATURE_CURR_MIN_ALARM,
+	SENSORS_SUBFEATURE_CURR_MAX_ALARM,
+	SENSORS_SUBFEATURE_CURR_BEEP,
+	SENSORS_SUBFEATURE_CURR_LCRIT_ALARM,
+	SENSORS_SUBFEATURE_CURR_CRIT_ALARM,
+
+	SENSORS_SUBFEATURE_HUMIDITY_INPUT = SENSORS_FEATURE_HUMIDITY << 8,
+
+	SENSORS_SUBFEATURE_VID = SENSORS_FEATURE_VID << 8,
+
+	SENSORS_SUBFEATURE_INTRUSION_ALARM = SENSORS_FEATURE_INTRUSION << 8,
+	SENSORS_SUBFEATURE_INTRUSION_BEEP,
+
+	SENSORS_SUBFEATURE_BEEP_ENABLE = SENSORS_FEATURE_BEEP_ENABLE << 8,
+
+	SENSORS_SUBFEATURE_UNKNOWN = INT_MAX,
+} sensors_subfeature_type;
+
+/* Data about a single chip feature (or category leader) */
+struct sensors_feature {
+	char *name;
+	int number;
+	sensors_feature_type type;
+	/* Members below are for libsensors internal use only */
+	int first_subfeature;
+	int padding1;
+};
+
+/* Data about a single chip subfeature:
+   name is the string name used to refer to this subfeature (in config files)
+   number is the internal subfeature number, used in many functions to refer
+     to this subfeature
+   type is the subfeature type
+   mapping is the number of a main feature this subfeature belongs to
+     (for example subfeatures fan1_input, fan1_min, fan1_div and fan1_alarm
+      are mapped to main feature fan1)
+   flags is a bitfield, its value is a combination of SENSORS_MODE_R (readable),
+     SENSORS_MODE_W (writable) and SENSORS_COMPUTE_MAPPING (affected by the
+     computation rules of the main feature) */
+typedef struct sensors_subfeature {
+	char *name;
+	int number;
+	sensors_subfeature_type type;
+	int mapping;
+	unsigned int flags;
+} sensors_subfeature;
+
+/* This returns all main features of a specific chip. nr is an internally
+   used variable. Set it to zero to start at the begin of the list. If no
+   more features are found NULL is returned.
+   Do not try to change the returned structure; you will corrupt internal
+   data structures. */
+const sensors_feature *
+sensors_get_features(const sensors_chip_name *name, int *nr);
+
+/* This returns all subfeatures of a given main feature. nr is an internally
+   used variable. Set it to zero to start at the begin of the list. If no
+   more features are found NULL is returned.
+   Do not try to change the returned structure; you will corrupt internal
+   data structures. */
+const sensors_subfeature *
+sensors_get_all_subfeatures(const sensors_chip_name *name,
+			    const sensors_feature *feature, int *nr);
+
+/* This returns the subfeature of the given type for a given main feature,
+   if it exists, NULL otherwise.
+   Do not try to change the returned structure; you will corrupt internal
+   data structures. */
+const sensors_subfeature *
+sensors_get_subfeature(const sensors_chip_name *name,
+		       const sensors_feature *feature,
+		       sensors_subfeature_type type);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* def LIB_SENSORS_ERROR_H */
Index: /lm-sensors/tags/V3-3-0/lib/access.c
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/access.c	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/access.c	(revision 5844)
@@ -0,0 +1,561 @@
+/*
+    access.c - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    Copyright (C) 2007-2009   Jean Delvare <khali@linux-fr.org>
+
+    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 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 <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "access.h"
+#include "sensors.h"
+#include "data.h"
+#include "error.h"
+#include "sysfs.h"
+
+/* We watch the recursion depth for variables only, as an easy way to
+   detect cycles. */
+#define DEPTH_MAX	8
+
+static int sensors_eval_expr(const sensors_chip_features *chip_features,
+			     const sensors_expr *expr,
+			     double val, int depth, double *result);
+
+/* Compare two chips name descriptions, to see whether they could match.
+   Return 0 if it does not match, return 1 if it does match. */
+static int sensors_match_chip(const sensors_chip_name *chip1,
+		       const sensors_chip_name *chip2)
+{
+	if ((chip1->prefix != SENSORS_CHIP_NAME_PREFIX_ANY) &&
+	    (chip2->prefix != SENSORS_CHIP_NAME_PREFIX_ANY) &&
+	    strcmp(chip1->prefix, chip2->prefix))
+		return 0;
+
+	if ((chip1->bus.type != SENSORS_BUS_TYPE_ANY) &&
+	    (chip2->bus.type != SENSORS_BUS_TYPE_ANY) &&
+	    (chip1->bus.type != chip2->bus.type))
+		return 0;
+
+	if ((chip1->bus.nr != SENSORS_BUS_NR_ANY) &&
+	    (chip2->bus.nr != SENSORS_BUS_NR_ANY) &&
+	    (chip1->bus.nr != chip2->bus.nr))
+		return 0;
+
+	if ((chip1->addr != chip2->addr) &&
+	    (chip1->addr != SENSORS_CHIP_NAME_ADDR_ANY) &&
+	    (chip2->addr != SENSORS_CHIP_NAME_ADDR_ANY))
+		return 0;
+
+	return 1;
+}
+
+/* Returns, one by one, a pointer to all sensor_chip structs of the
+   config file which match with the given chip name. Last should be
+   the value returned by the last call, or NULL if this is the first
+   call. Returns NULL if no more matches are found. Do not modify
+   the struct the return value points to! 
+   Note that this visits the list of chips from last to first. Usually,
+   you want the match that was latest in the config file. */
+static sensors_chip *
+sensors_for_all_config_chips(const sensors_chip_name *name,
+			     const sensors_chip *last)
+{
+	int nr, i;
+	sensors_chip_name_list chips;
+
+	for (nr = last ? last - sensors_config_chips - 1 :
+			 sensors_config_chips_count - 1; nr >= 0; nr--) {
+
+		chips = sensors_config_chips[nr].chips;
+		for (i = 0; i < chips.fits_count; i++) {
+			if (sensors_match_chip(&chips.fits[i], name))
+				return sensors_config_chips + nr;
+		}
+	}
+	return NULL;
+}
+
+/* Look up a chip in the intern chip list, and return a pointer to it.
+   Do not modify the struct the return value points to! Returns NULL if
+   not found.*/
+static const sensors_chip_features *
+sensors_lookup_chip(const sensors_chip_name *name)
+{
+	int i;
+
+	for (i = 0; i < sensors_proc_chips_count; i++)
+		if (sensors_match_chip(&sensors_proc_chips[i].chip, name))
+			return &sensors_proc_chips[i];
+
+	return NULL;
+}
+
+/* Look up a subfeature of the given chip, and return a pointer to it.
+   Do not modify the struct the return value points to! Returns NULL if
+   not found.*/
+static const sensors_subfeature *
+sensors_lookup_subfeature_nr(const sensors_chip_features *chip,
+			     int subfeat_nr)
+{
+	if (subfeat_nr < 0 ||
+	    subfeat_nr >= chip->subfeature_count)
+		return NULL;
+	return chip->subfeature + subfeat_nr;
+}
+
+/* Look up a feature of the given chip, and return a pointer to it.
+   Do not modify the struct the return value points to! Returns NULL if
+   not found.*/
+static const sensors_feature *
+sensors_lookup_feature_nr(const sensors_chip_features *chip, int feat_nr)
+{
+	if (feat_nr < 0 ||
+	    feat_nr >= chip->feature_count)
+		return NULL;
+	return chip->feature + feat_nr;
+}
+
+/* Look up a subfeature by name, and return a pointer to it.
+   Do not modify the struct the return value points to! Returns NULL if 
+   not found.*/
+static const sensors_subfeature *
+sensors_lookup_subfeature_name(const sensors_chip_features *chip,
+			       const char *name)
+{
+	int j;
+
+	for (j = 0; j < chip->subfeature_count; j++)
+		if (!strcmp(chip->subfeature[j].name, name))
+			return chip->subfeature + j;
+	return NULL;
+}
+
+/* Check whether the chip name is an 'absolute' name, which can only match
+   one chip, or whether it has wildcards. Returns 0 if it is absolute, 1
+   if there are wildcards. */
+int sensors_chip_name_has_wildcards(const sensors_chip_name *chip)
+{
+	if ((chip->prefix == SENSORS_CHIP_NAME_PREFIX_ANY) ||
+	    (chip->bus.type == SENSORS_BUS_TYPE_ANY) ||
+	    (chip->bus.nr == SENSORS_BUS_NR_ANY) ||
+	    (chip->addr == SENSORS_CHIP_NAME_ADDR_ANY))
+		return 1;
+	else
+		return 0;
+}
+
+/* Look up the label for a given feature. Note that chip should not
+   contain wildcard values! The returned string is newly allocated (free it
+   yourself). On failure, NULL is returned.
+   If no label exists for this feature, its name is returned itself. */
+char *sensors_get_label(const sensors_chip_name *name,
+			const sensors_feature *feature)
+{
+	char *label;
+	const sensors_chip *chip;
+	char buf[PATH_MAX];
+	FILE *f;
+	int i;
+
+	if (sensors_chip_name_has_wildcards(name))
+		return NULL;
+
+	for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));)
+		for (i = 0; i < chip->labels_count; i++)
+			if (!strcmp(feature->name, chip->labels[i].name)) {
+				label = chip->labels[i].value;
+				goto sensors_get_label_exit;
+			}
+
+	/* No user specified label, check for a _label sysfs file */
+	snprintf(buf, PATH_MAX, "%s/%s_label", name->path, feature->name);
+	
+	if ((f = fopen(buf, "r"))) {
+		i = fread(buf, 1, sizeof(buf), f);
+		fclose(f);
+		if (i > 0) {
+			/* i - 1 to strip the '\n' at the end */
+			buf[i - 1] = 0;
+			label = buf;
+			goto sensors_get_label_exit;
+		}
+	}
+
+	/* No label, return the feature name instead */
+	label = feature->name;
+	
+sensors_get_label_exit:
+	label = strdup(label);
+	if (!label)
+		sensors_fatal_error(__func__, "Allocating label text");
+	return label;
+}
+
+/* Looks up whether a feature should be ignored. Returns
+   1 if it should be ignored, 0 if not. */
+static int sensors_get_ignored(const sensors_chip_name *name,
+			       const sensors_feature *feature)
+{
+	const sensors_chip *chip;
+	int i;
+
+	for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));)
+		for (i = 0; i < chip->ignores_count; i++)
+			if (!strcmp(feature->name, chip->ignores[i].name))
+				return 1;
+	return 0;
+}
+
+/* Read the value of a subfeature of a certain chip. Note that chip should not
+   contain wildcard values! This function will return 0 on success, and <0
+   on failure. */
+static int __sensors_get_value(const sensors_chip_name *name, int subfeat_nr,
+			       int depth, double *result)
+{
+	const sensors_chip_features *chip_features;
+	const sensors_subfeature *subfeature;
+	const sensors_expr *expr = NULL;
+	double val;
+	int res, i;
+
+	if (depth >= DEPTH_MAX)
+		return -SENSORS_ERR_RECURSION;
+	if (sensors_chip_name_has_wildcards(name))
+		return -SENSORS_ERR_WILDCARDS;
+	if (!(chip_features = sensors_lookup_chip(name)))
+		return -SENSORS_ERR_NO_ENTRY;
+	if (!(subfeature = sensors_lookup_subfeature_nr(chip_features,
+							subfeat_nr)))
+		return -SENSORS_ERR_NO_ENTRY;
+	if (!(subfeature->flags & SENSORS_MODE_R))
+		return -SENSORS_ERR_ACCESS_R;
+
+	/* Apply compute statement if it exists */
+	if (subfeature->flags & SENSORS_COMPUTE_MAPPING) {
+		const sensors_feature *feature;
+		const sensors_chip *chip;
+
+		feature = sensors_lookup_feature_nr(chip_features,
+					subfeature->mapping);
+
+		chip = NULL;
+		while (!expr &&
+		       (chip = sensors_for_all_config_chips(name, chip)))
+			for (i = 0; i < chip->computes_count; i++) {
+				if (!strcmp(feature->name,
+					    chip->computes[i].name)) {
+					expr = chip->computes[i].from_proc;
+					break;
+				}
+			}
+	}
+
+	res = sensors_read_sysfs_attr(name, subfeature, &val);
+	if (res)
+		return res;
+	if (!expr)
+		*result = val;
+	else if ((res = sensors_eval_expr(chip_features, expr, val, depth,
+					  result)))
+		return res;
+	return 0;
+}
+
+int sensors_get_value(const sensors_chip_name *name, int subfeat_nr,
+		      double *result)
+{
+	return __sensors_get_value(name, subfeat_nr, 0, result);
+}
+
+/* Set the value of a subfeature of a certain chip. Note that chip should not
+   contain wildcard values! This function will return 0 on success, and <0
+   on failure. */
+int sensors_set_value(const sensors_chip_name *name, int subfeat_nr,
+		      double value)
+{
+	const sensors_chip_features *chip_features;
+	const sensors_subfeature *subfeature;
+	const sensors_expr *expr = NULL;
+	int i, res;
+	double to_write;
+
+	if (sensors_chip_name_has_wildcards(name))
+		return -SENSORS_ERR_WILDCARDS;
+	if (!(chip_features = sensors_lookup_chip(name)))
+		return -SENSORS_ERR_NO_ENTRY;
+	if (!(subfeature = sensors_lookup_subfeature_nr(chip_features,
+							subfeat_nr)))
+		return -SENSORS_ERR_NO_ENTRY;
+	if (!(subfeature->flags & SENSORS_MODE_W))
+		return -SENSORS_ERR_ACCESS_W;
+
+	/* Apply compute statement if it exists */
+	if (subfeature->flags & SENSORS_COMPUTE_MAPPING) {
+		const sensors_feature *feature;
+		const sensors_chip *chip;
+
+		feature = sensors_lookup_feature_nr(chip_features,
+					subfeature->mapping);
+
+		chip = NULL;
+		while (!expr &&
+		       (chip = sensors_for_all_config_chips(name, chip)))
+			for (i = 0; i < chip->computes_count; i++) {
+				if (!strcmp(feature->name,
+					    chip->computes[i].name)) {
+					expr = chip->computes[i].to_proc;
+					break;
+				}
+			}
+	}
+
+	to_write = value;
+	if (expr)
+		if ((res = sensors_eval_expr(chip_features, expr,
+					     value, 0, &to_write)))
+			return res;
+	return sensors_write_sysfs_attr(name, subfeature, to_write);
+}
+
+const sensors_chip_name *sensors_get_detected_chips(const sensors_chip_name
+						    *match, int *nr)
+{
+	const sensors_chip_name *res;
+
+	while (*nr < sensors_proc_chips_count) {
+		res = &sensors_proc_chips[(*nr)++].chip;
+		if (!match || sensors_match_chip(res, match))
+			return res;
+	}
+	return NULL;
+}
+
+const char *sensors_get_adapter_name(const sensors_bus_id *bus)
+{
+	int i;
+
+	/* bus types with a single instance */
+	switch (bus->type) {
+	case SENSORS_BUS_TYPE_ISA:
+		return "ISA adapter";
+	case SENSORS_BUS_TYPE_PCI:
+		return "PCI adapter";
+	/* SPI should not be here, but for now SPI adapters have no name
+	   so we don't have any custom string to return. */
+	case SENSORS_BUS_TYPE_SPI:
+		return "SPI adapter";
+	case SENSORS_BUS_TYPE_VIRTUAL:
+		return "Virtual device";
+	case SENSORS_BUS_TYPE_ACPI:
+		return "ACPI interface";
+	/* HID should probably not be there either, but I don't know if
+	   HID buses have a name nor where to find it. */
+	case SENSORS_BUS_TYPE_HID:
+		return "HID adapter";
+	}
+
+	/* bus types with several instances */
+	for (i = 0; i < sensors_proc_bus_count; i++)
+		if (sensors_proc_bus[i].bus.type == bus->type &&
+		    sensors_proc_bus[i].bus.nr == bus->nr)
+			return sensors_proc_bus[i].adapter;
+	return NULL;
+}
+
+const sensors_feature *
+sensors_get_features(const sensors_chip_name *name, int *nr)
+{
+	const sensors_chip_features *chip;
+
+	if (!(chip = sensors_lookup_chip(name)))
+		return NULL;	/* No such chip */
+
+	while (*nr < chip->feature_count
+	    && sensors_get_ignored(name, &chip->feature[*nr]))
+		(*nr)++;
+	if (*nr >= chip->feature_count)
+		return NULL;
+	return &chip->feature[(*nr)++];
+}
+
+const sensors_subfeature *
+sensors_get_all_subfeatures(const sensors_chip_name *name,
+			const sensors_feature *feature, int *nr)
+{
+	const sensors_chip_features *chip;
+	const sensors_subfeature *subfeature;
+
+	if (!(chip = sensors_lookup_chip(name)))
+		return NULL;	/* No such chip */
+
+	/* Seek directly to the first subfeature */
+	if (*nr < feature->first_subfeature)
+		*nr = feature->first_subfeature;
+
+	if (*nr >= chip->subfeature_count)
+		return NULL;	/* end of list */
+	subfeature = &chip->subfeature[(*nr)++];
+	if (subfeature->mapping == feature->number)
+		return subfeature;
+	return NULL;	/* end of subfeature list */
+}
+
+const sensors_subfeature *
+sensors_get_subfeature(const sensors_chip_name *name,
+		       const sensors_feature *feature,
+		       sensors_subfeature_type type)
+{
+	const sensors_chip_features *chip;
+	int i;
+
+	if (!(chip = sensors_lookup_chip(name)))
+		return NULL;	/* No such chip */
+
+	for (i = feature->first_subfeature; i < chip->subfeature_count &&
+	     chip->subfeature[i].mapping == feature->number; i++) {
+		if (chip->subfeature[i].type == type)
+			return &chip->subfeature[i];
+	}
+	return NULL;	/* No such subfeature */
+}
+
+/* Evaluate an expression */
+int sensors_eval_expr(const sensors_chip_features *chip_features,
+		      const sensors_expr *expr,
+		      double val, int depth, double *result)
+{
+	double res1, res2;
+	int res;
+	const sensors_subfeature *subfeature;
+
+	if (expr->kind == sensors_kind_val) {
+		*result = expr->data.val;
+		return 0;
+	}
+	if (expr->kind == sensors_kind_source) {
+		*result = val;
+		return 0;
+	}
+	if (expr->kind == sensors_kind_var) {
+		if (!(subfeature = sensors_lookup_subfeature_name(chip_features,
+							    expr->data.var)))
+			return -SENSORS_ERR_NO_ENTRY;
+		return __sensors_get_value(&chip_features->chip,
+					   subfeature->number, depth + 1,
+					   result);
+	}
+	if ((res = sensors_eval_expr(chip_features, expr->data.subexpr.sub1,
+				     val, depth, &res1)))
+		return res;
+	if (expr->data.subexpr.sub2 &&
+	    (res = sensors_eval_expr(chip_features, expr->data.subexpr.sub2,
+				     val, depth, &res2)))
+		return res;
+	switch (expr->data.subexpr.op) {
+	case sensors_add:
+		*result = res1 + res2;
+		return 0;
+	case sensors_sub:
+		*result = res1 - res2;
+		return 0;
+	case sensors_multiply:
+		*result = res1 * res2;
+		return 0;
+	case sensors_divide:
+		if (res2 == 0.0)
+			return -SENSORS_ERR_DIV_ZERO;
+		*result = res1 / res2;
+		return 0;
+	case sensors_negate:
+		*result = -res1;
+		return 0;
+	case sensors_exp:
+		*result = exp(res1);
+		return 0;
+	case sensors_log:
+		if (res1 < 0.0)
+			return -SENSORS_ERR_DIV_ZERO;
+		*result = log(res1);
+		return 0;
+	}
+	return 0;
+}
+
+/* Execute all set statements for this particular chip. The chip may not 
+   contain wildcards!  This function will return 0 on success, and <0 on 
+   failure. */
+static int sensors_do_this_chip_sets(const sensors_chip_name *name)
+{
+	const sensors_chip_features *chip_features;
+	sensors_chip *chip;
+	double value;
+	int i;
+	int err = 0, res;
+	const sensors_subfeature *subfeature;
+
+	chip_features = sensors_lookup_chip(name);	/* Can't fail */
+
+	for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));)
+		for (i = 0; i < chip->sets_count; i++) {
+			subfeature = sensors_lookup_subfeature_name(chip_features,
+							chip->sets[i].name);
+			if (!subfeature) {
+				sensors_parse_error_wfn("Unknown feature name",
+						    chip->sets[i].line.filename,
+						    chip->sets[i].line.lineno);
+				err = -SENSORS_ERR_NO_ENTRY;
+				continue;
+			}
+
+			res = sensors_eval_expr(chip_features,
+						chip->sets[i].value, 0,
+						0, &value);
+			if (res) {
+				sensors_parse_error_wfn("Error parsing expression",
+						    chip->sets[i].line.filename,
+						    chip->sets[i].line.lineno);
+				err = res;
+				continue;
+			}
+			if ((res = sensors_set_value(name, subfeature->number,
+						     value))) {
+				sensors_parse_error_wfn("Failed to set value",
+						chip->sets[i].line.filename,
+						chip->sets[i].line.lineno);
+				err = res;
+				continue;
+			}
+		}
+	return err;
+}
+
+/* Execute all set statements for this particular chip. The chip may contain
+   wildcards!  This function will return 0 on success, and <0 on failure. */
+int sensors_do_chip_sets(const sensors_chip_name *name)
+{
+	int nr, this_res;
+	const sensors_chip_name *found_name;
+	int res = 0;
+
+	for (nr = 0; (found_name = sensors_get_detected_chips(name, &nr));) {
+		this_res = sensors_do_this_chip_sets(found_name);
+		if (this_res)
+			res = this_res;
+	}
+	return res;
+}
Index: /lm-sensors/tags/V3-3-0/lib/sensors.conf.5
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/sensors.conf.5	(revision 5887)
+++ /lm-sensors/tags/V3-3-0/lib/sensors.conf.5	(revision 5887)
@@ -0,0 +1,561 @@
+.\" Copyright (C) 1998, 1999 Adrian Baugh <adrian.baugh@keble.ox.ac.uk> and
+.\"                          Frodo Looijaard <frodol@dds.nl>
+.\" Copyright (C) 2008, 2009 Jean Delvare <khali@linux-fr.org>
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one.
+.\"
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date.  The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein.
+.\"
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+.\"
+.\" References consulted:
+.\"     sensors.conf.eg by Frodo Looijaard
+.TH sensors.conf 5  "February 2009" "lm-sensors 3" "Linux User's Manual"
+.SH NAME
+sensors.conf \- libsensors configuration file
+
+.SH DESCRIPTION
+sensors.conf describes how libsensors, and so all programs using it, should
+translate the raw readings from the kernel modules to real\-world values.
+
+.SH SEMANTICS
+
+On a given system, there may be one or more hardware monitoring chips.
+Each chip may have several features. For example, the LM78 monitors 7
+voltage inputs, 3 fans and one temperature. Feature names are
+standardized. Typical feature names are in0, in1, in2... for voltage
+inputs, fan1, fan2, fan3... for fans and temp1, temp2, temp3... for
+temperature inputs.
+
+Each feature may in turn have one or more sub\-features, each
+representing an attribute of the feature: input value, low limit, high
+limit, alarm, etc. Sub\-feature names are standardized as well. For
+example, the first voltage input (in0) would typically have
+sub\-features in0_input (measured value), in0_min (low limit), in0_max
+(high limit) and in0_alarm (alarm flag). Which sub\-features are
+actually present depend on the exact chip type.
+
+The
+.I sensors.conf
+configuration file will let you configure each chip, feature and
+sub\-feature in a way that makes sense for your system.
+
+The rest of this section describes the meaning of each configuration
+statement.
+
+.SS CHIP STATEMENT
+
+A
+.I chip
+statement selects for which chips all following
+.IR compute ,
+.IR label ,
+.I ignore
+and
+.I set
+statements are meant. A chip
+selection remains valid until the next
+.I chip
+statement. Example:
+
+.RS
+chip "lm78\-*" "lm79\-*"
+.RE
+
+If a chip matches at least one of the chip descriptions, the following
+configuration lines are examined for it, otherwise they are ignored.
+
+A chip description is built from several elements, separated by
+dashes. The first element is the chip type, the second element is
+the name of the bus, and the third element is the hexadecimal address
+of the chip. Such chip descriptions are printed by sensors(1) as the
+first line for every chip.
+
+The name of the bus is either
+.IR isa ,
+.IR pci ,
+.IR virtual ,
+.I spi-*
+or
+.I i2c-N
+with
+.I N
+being a bus number as bound with a
+.I bus
+statement. This list isn't necessarily exhaustive as support for other
+bus types may be added in the future.
+
+You may substitute the wildcard operator
+.I *
+for every element. Note however that it wouldn't make any sense to specify
+the address without the bus type, so the address part is plain omitted
+when the bus type isn't specified.
+Here is how you would express the following matches:
+
+.TS
+l l.
+LM78 chip at address 0x2d on I2C bus 1	lm78\-i2c\-1\-2d
+LM78 chip at address 0x2d on any I2C bus	lm78\-i2c\-*\-2d
+LM78 chip at address 0x290 on the ISA bus	lm78\-isa\-0290
+Any LM78 chip on I2C bus 1	lm78\-i2c\-1\-*
+Any LM78 on any I2C bus	lm78\-i2c\-*\-*
+Any LM78 chip on the ISA bus	lm78\-isa\-*
+Any LM78 chip	lm78\-*
+Any chip at address 0x2d on I2C bus 1	*\-i2c\-1\-2d
+Any chip at address 0x290 on the ISA bus	*\-isa\-0290
+.TE
+
+If several chip statements match a specific chip, they are all considered.
+
+.SS LABEL STATEMENT
+
+A
+.I label
+statement describes how a feature should be called. Features without a
+.I label
+statement are just called by their feature name. Applications can use this
+to label the readings they present. Example:
+
+.RS
+label in3 "+5V"
+.RE
+
+The first argument is the feature name. The second argument is the feature
+description.
+
+.SS IGNORE STATEMENT
+
+An
+.I ignore
+statement is a hint that a specific feature should be ignored - probably
+because it returns bogus values (for example, because a fan or temperature
+sensor is not connected). Example:
+
+.RS
+ignore fan1
+.RE
+
+The only argument is the feature name. Please note that this does not disable
+anything in the actual sensor chip; it simply hides the feature in question
+from libsensors users.
+
+.SS COMPUTE STATEMENT
+
+A
+.I compute
+statement describes how a feature's raw value should be translated to a
+real\-world value, and how a real\-world value should be translated back
+to a raw value again. This is most useful for voltage sensors, because
+in general sensor chips have a limited range and voltages outside this
+range must be divided (using resistors) before they can be monitored.
+Example:
+
+.RS
+compute in3 ((6.8/10)+1)*@, @/((6.8/10)+1)
+.RE
+
+The example above expresses the fact that the voltage input is divided
+using two resistors of values 6.8 Ohm and 10 Ohm, respectively. See the
+.B VOLTAGE COMPUTATION DETAILS
+section below for details.
+
+The first argument is the feature name. The second argument is an expression
+which specifies how a raw value must be translated to a real\-world value;
+`@' stands here for the raw value. This is the formula which will be applied
+when reading values from the chip. The third argument is an expression that
+specifies how a real\-world value should be translated back to a raw value;
+`@' stands here for the real\-world value. This is the formula which will be
+applied when writing values to the chip. The two formulas are obviously
+related, and are separated by a comma.
+
+A
+.I compute
+statement applies to all sub\-features of the target feature for which
+it makes sense. For example, the above example would affect sub\-features
+in3_min and in3_max (which are voltage values) but not in3_alarm
+(which is a boolean flag.)
+
+The following operators are supported in
+.I compute
+statements:
+.RS
++ \- * / ( ) ^ `
+.RE
+^x means exp(x) and `x means ln(x).
+
+You may use the name of sub\-features in these expressions; current readings
+are substituted. You should be careful though to avoid circular references.
+
+If at any moment a translation between a raw and a real\-world value is
+called for, but no
+.I compute
+statement applies, a one\-on\-one translation is used instead.
+
+.SS SET STATEMENT
+
+A
+.I set
+statement is used to write a sub\-feature value to the chip. Of course not
+all sub\-feature values can be set that way, in particular input values
+and alarm flags can not. Valid sub\-features are usually min/max limits.
+Example:
+
+.RS
+set in3_min  5 * 0.95
+.RE
+.RS
+set in3_max  5 * 1.05
+.RE
+
+The example above basically configures the chip to allow a 5% deviance
+for the +5V power input.
+
+The first argument is the feature name. The second argument is an expression
+which determines the written value. If there is an applying
+.I compute
+statement, this value is fed to its third argument to translate it to a
+raw value.
+
+You may use the name of sub\-features in these expressions; current readings
+are substituted. You should be careful though to avoid circular references.
+
+Please note that
+.I set
+statements are only executed by sensors(1) when you use the
+.B \-s
+option. Typical graphical sensors applications do not care about these
+statements at all.
+
+.SS BUS STATEMENT
+
+A
+.I bus
+statement binds the description of an I2C or SMBus adapter to a bus number.
+This makes it possible to refer to an adapter in the configuration file,
+independent of the actual correspondence of bus numbers and actual
+adapters (which may change from moment to moment). Example:
+
+.RS
+bus "i2c\-0" "SMBus PIIX4 adapter at e800"
+.RE
+
+The first argument is the bus number. It is the literal text
+.IR i2c\- ,
+followed by a number. As there is a dash in this argument, it must
+always be quoted.
+
+The second argument is the adapter name, it must match exactly the
+adapter name as it appears in
+.IR /sys/class/i2c\-adapter/i2c\-*/name .
+It should always be quoted as well as it will most certainly contain
+spaces or dashes.
+
+The
+.I bus
+statements may be scattered randomly throughout the configuration file;
+there is no need to place the bus line before the place where its binding
+is referred to. Still, as a matter of good style, we suggest you place
+all
+.I bus
+statements together at the top of your configuration file.
+
+Running
+.B sensors --bus-list
+will generate these lines for you.
+
+In the case where multiple configuration files are used, the scope
+of each
+.I bus
+statement is the configuration file it was defined in. This makes it
+possible to have bus statements in all configuration files which will
+not unexpectedly interfere with each other.
+
+.SS STATEMENT ORDER
+
+Statements can go in any order, however it is recommended to put
+`set fanX_div' statements before `set fanX_min' statements, in case
+a driver doesn't preserve the fanX_min setting when the fanX_div
+value is changed. Even if the driver does, it's still better to put
+the statements in this order to avoid accuracy loss.
+
+.SH VOLTAGE COMPUTATION DETAILS
+
+Most voltage sensors in sensor chips have a range of 0 to 4.08 V.
+This is generally sufficient for the +3.3V and CPU supply voltages, so
+the sensor chip reading is the actual voltage.
+
+Other supply voltages must be scaled with an external resistor network.
+The driver reports the value at the chip's pin (0 \- 4.08 V), and the
+userspace application must convert this raw value to an actual voltage.
+The
+.I compute
+statements provide this facility.
+
+Unfortunately the resistor values vary among motherboard types.
+Therefore you have to figure out the correct resistor values for your
+own motherboard.
+
+For positive voltages (typically +5V and +12V), two resistors are used,
+with the following formula:
+        R1 = R2 * (Vs/Vin \- 1)
+
+where:
+        R1 and R2 are the resistor values
+        Vs is the actual voltage being monitored
+        Vin is the voltage at the pin
+
+This leads to the following compute formula:
+        compute inX @*((R1/R2)+1),  @/(((R1/R2)+1)
+
+Real\-world formula for +5V and +12V would look like:
+        compute in3 @*((6.8/10)+1), @/((6.8/10)+1)
+        compute in4 @*((28/10)+1),  @/((28/10)+1)
+
+For negative voltages (typically \-5V and \-12V), two resistors are used
+as well, but different boards use different strategies to bring the
+voltage value into the 0 \- 4.08 V range. Some use an inverting
+amplifier, others use a positive reference voltage. This leads to
+different computation formulas. Note that most users won't have to care
+because most modern motherboards make little use of \-12V and no use of
+\-5V so they do not bother monitoring these voltage inputs.
+
+Real\-world examples for the inverting amplifier case:
+        compute in5 \-@*(240/60), \-@/(240/60)
+        compute in6 \-@*(100/60), \-@/(100/60)
+
+Real\-world examples for the positive voltage reference case:
+        compute in5 @*(1+232/56) \- 4.096*232/56, (@ + 4.096*232/56)/(1+232/56)
+        compute in6 @*(1+120/56) \- 4.096*120/56, (@ + 4.096*120/56)/(1+120/56)
+
+Many recent monitoring chips have a 0 \- 2.04 V range, so scaling resistors
+are even more needed, and resistor values are different.
+
+There are also a few chips out there which have internal scaling
+resistors, meaning that their value is known and doesn't change from
+one motherboard to the next. For these chips, the driver usually
+handles the scaling so it is transparent to the user and no
+.I compute
+statements are needed.
+
+.SH TEMPERATURE CONFIGURATION
+
+On top of the usual features, temperatures can have two specific
+sub\-features: temperature sensor type (tempX_type) and hysteresis
+values (tempX_max_hyst and tempX_crit_hyst).
+
+.SS THERMAL SENSOR TYPES
+
+Available thermal sensor types:
+.TS
+r l.
+1	PII/Celeron Diode
+2	3904 transistor
+3	thermal diode
+4	thermistor
+5	AMD AMDSI
+6	Intel PECI
+.TE
+
+For example, to set temp1 to thermistor type, use:
+
+.RS
+set temp1_type 4
+.RE
+
+Only certain chips support thermal sensor type change, and even these
+usually only support some of the types above. Please refer to the
+specific driver documentation to find out which types are supported
+by your chip.
+
+In theory, the BIOS should have configured the sensor types correctly,
+so you shouldn't have to touch them, but sometimes it isn't the case.
+
+.SS THERMAL HYSTERESIS MECHANISM
+
+Many monitoring chips do not handle the high and critical temperature
+limits as simple limits. Instead, they have two values for each
+limit, one which triggers an alarm when the temperature rises and another
+one which clears the alarm when the temperature falls. The latter is
+typically a few degrees below the former. This mechanism is known as
+hysteresis.
+
+The reason for implementing things that way is that high temperature
+alarms typically trigger an action to attempt to cool the system down,
+either by scaling down the CPU frequency, or by kicking in an extra
+fan. This should normally let the temperature fall in a timely manner.
+If this was clearing the alarm immediately, then the system would be
+back to its original state where the temperature rises and the alarm
+would immediately trigger again, causing an undesirable tight fan on,
+fan off loop. The hysteresis mechanism ensures that the system is
+really cool before the fan stops, so that it will not have to kick in
+again immediately.
+
+So, in addition to tempX_max, many chips have a tempX_max_hyst
+sub-feature. Likewise, tempX_crit often comes with tempX_crit_hyst.
+Example:
+
+.RS
+set temp1_max      60
+.RE
+.RS
+set temp1_max_hyst 56
+.RE
+
+The hysteresis mechanism can be disabled by giving both limits the same
+value.
+
+.SH BEEPS
+
+Some chips support alarms with beep warnings. When an alarm is triggered
+you can be warned by a beeping signal through your computer speaker. On
+top of per\-feature beep flags, there is usually a master beep control
+switch to enable or disable beeping globally. Enable beeping using:
+
+.RS
+set beep_enable 1
+.RE
+
+or disable it using:
+
+.RS
+set beep_enable 0
+.RE
+
+.SH WHICH STATEMENT APPLIES
+
+If more than one statement of the same kind applies at a certain moment,
+the last one in the configuration file is used. So usually, you should
+put more general
+.I chip
+statements at the top, so you can overrule them below.
+
+.SH SYNTAX
+Comments are introduced by hash marks. A comment continues to the end of the
+line. Empty lines, and lines containing only whitespace or comments are
+ignored.  Other lines have one of the below forms. There must be whitespace
+between each element, but the amount of whitespace is unimportant. A line
+may be continued on the next line by ending it with a backslash; this does
+not work within a comment,
+.B NAME
+or
+.BR NUMBER .
+
+.RS
+bus
+.B NAME NAME NAME
+.sp 0
+chip
+.B NAME\-LIST
+.sp 0
+label
+.B NAME NAME
+.sp 0
+compute
+.B NAME EXPR
+,
+.B EXPR
+.sp 0
+ignore
+.B NAME
+.sp 0
+set
+.B NAME EXPR
+.RE
+.sp
+A
+.B NAME
+is a string. If it only contains letters, digits and underscores, it does not
+have to be quoted; in all other cases, you must use double quotes around it.
+Within quotes, you can use the normal escape\-codes from C.
+
+A
+.B NAME\-LIST
+is one or more
+.B NAME
+items behind each other, separated by whitespace.
+
+A
+.B EXPR
+is of one of the below forms:
+
+.RS
+.B NUMBER
+.sp 0
+.B NAME
+.sp 0
+@
+.sp 0
+.B EXPR
++
+.B EXPR
+.sp 0
+.B EXPR
+\-
+.B EXPR
+.sp 0
+.B EXPR
+*
+.B EXPR
+.sp 0
+.B EXPR
+/
+.B EXPR
+.sp 0
+\-
+.B EXPR
+.sp 0
+^
+.B EXPR
+.sp 0
+`
+.B EXPR
+.sp 0
+(
+.B EXPR
+)
+.RE
+
+A
+.B NUMBER
+is a floating\-point number. `10', `10.4' and `.4' are examples of valid
+floating\-point numbers; `10.' or `10E4' are not valid.
+
+.SH FILES
+.I /etc/sensors3.conf
+.br
+.I /etc/sensors.conf
+.RS
+The system-wide
+.BR libsensors (3)
+configuration file. /etc/sensors3.conf is tried first, and if it doesn't exist,
+/etc/sensors.conf is used instead.
+.RE
+
+.I /etc/sensors.d
+.RS
+A directory where you can put additional libsensors configuration files.
+Files found in this directory will be processed in alphabetical order after
+the default configuration file. Files with names that start with a dot are
+ignored.
+.RE
+
+.SH SEE ALSO
+libsensors(3)
+
+.SH AUTHOR
+Frodo Looijaard and the lm_sensors group
+http://www.lm-sensors.org/
+
+
+
Index: /lm-sensors/tags/V3-3-0/lib/init.c
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/init.c	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/init.c	(revision 5844)
@@ -0,0 +1,339 @@
+/*
+    init.c - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    Copyright (C) 2007, 2009  Jean Delvare <khali@linux-fr.org>
+
+    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 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.
+*/
+
+/* Needed for scandir() and alphasort() */
+#define _BSD_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include "sensors.h"
+#include "data.h"
+#include "error.h"
+#include "access.h"
+#include "conf.h"
+#include "sysfs.h"
+#include "scanner.h"
+#include "init.h"
+
+#define DEFAULT_CONFIG_FILE	ETCDIR "/sensors3.conf"
+#define ALT_CONFIG_FILE		ETCDIR "/sensors.conf"
+#define DEFAULT_CONFIG_DIR	ETCDIR "/sensors.d"
+
+/* Wrapper around sensors_yyparse(), which clears the locale so that
+   the decimal numbers are always parsed properly. */
+static int sensors_parse(void)
+{
+	int res;
+	char *locale;
+
+	/* Remember the current locale and clear it */
+	locale = setlocale(LC_ALL, NULL);
+	if (locale) {
+		locale = strdup(locale);
+		if (!locale)
+			sensors_fatal_error(__func__, "Out of memory");
+
+		setlocale(LC_ALL, "C");
+	}
+
+	res = sensors_yyparse();
+
+	/* Restore the old locale */
+	if (locale) {
+		setlocale(LC_ALL, locale);
+		free(locale);
+	}
+
+	return res;
+}
+
+static void free_bus(sensors_bus *bus)
+{
+	free(bus->adapter);
+}
+
+static void free_config_busses(void)
+{
+	int i;
+
+	for (i = 0; i < sensors_config_busses_count; i++)
+		free_bus(&sensors_config_busses[i]);
+	free(sensors_config_busses);
+	sensors_config_busses = NULL;
+	sensors_config_busses_count = sensors_config_busses_max = 0;
+}
+
+static int parse_config(FILE *input, const char *name)
+{
+	int err;
+	char *name_copy;
+
+	if (name) {
+		/* Record configuration file name for error reporting */
+		name_copy = strdup(name);
+		if (!name_copy)
+			sensors_fatal_error(__func__, "Out of memory");
+		sensors_add_config_files(&name_copy);
+	} else
+		name_copy = NULL;
+
+	if (sensors_scanner_init(input, name_copy)) {
+		err = -SENSORS_ERR_PARSE;
+		goto exit_cleanup;
+	}
+	err = sensors_parse();
+	sensors_scanner_exit();
+	if (err) {
+		err = -SENSORS_ERR_PARSE;
+		goto exit_cleanup;
+	}
+
+	err = sensors_substitute_busses();
+
+exit_cleanup:
+	free_config_busses();
+	return err;
+}
+
+static int config_file_filter(const struct dirent *entry)
+{
+	return entry->d_name[0] != '.';		/* Skip hidden files */
+}
+
+static int add_config_from_dir(const char *dir)
+{
+	int count, res, i;
+	struct dirent **namelist;
+
+	count = scandir(dir, &namelist, config_file_filter, alphasort);
+	if (count < 0) {
+		/* Do not return an error if directory does not exist */
+		if (errno == ENOENT)
+			return 0;
+		
+		sensors_parse_error_wfn(strerror(errno), NULL, 0);
+		return -SENSORS_ERR_PARSE;
+	}
+
+	for (res = 0, i = 0; !res && i < count; i++) {
+		int len;
+		char path[PATH_MAX];
+		FILE *input;
+		struct stat st;
+
+		len = snprintf(path, sizeof(path), "%s/%s", dir,
+			       namelist[i]->d_name);
+		if (len < 0 || len >= (int)sizeof(path)) {
+			res = -SENSORS_ERR_PARSE;
+			continue;
+		}
+
+		/* Only accept regular files */
+		if (stat(path, &st) < 0 || !S_ISREG(st.st_mode))
+			continue;
+
+		input = fopen(path, "r");
+		if (input) {
+			res = parse_config(input, path);
+			fclose(input);
+		} else {
+			res = -SENSORS_ERR_PARSE;
+			sensors_parse_error_wfn(strerror(errno), path, 0);
+		}
+	}
+
+	/* Free memory allocated by scandir() */
+	for (i = 0; i < count; i++)
+		free(namelist[i]);
+	free(namelist);
+
+	return res;
+}
+
+int sensors_init(FILE *input)
+{
+	int res;
+
+	if (!sensors_init_sysfs())
+		return -SENSORS_ERR_KERNEL;
+	if ((res = sensors_read_sysfs_bus()) ||
+	    (res = sensors_read_sysfs_chips()))
+		goto exit_cleanup;
+
+	if (input) {
+		res = parse_config(input, NULL);
+		if (res)
+			goto exit_cleanup;
+	} else {
+		const char* name;
+
+		/* No configuration provided, use default */
+		input = fopen(name = DEFAULT_CONFIG_FILE, "r");
+		if (!input && errno == ENOENT)
+			input = fopen(name = ALT_CONFIG_FILE, "r");
+		if (input) {
+			res = parse_config(input, name);
+			fclose(input);
+			if (res)
+				goto exit_cleanup;
+
+		} else if (errno != ENOENT) {
+			sensors_parse_error_wfn(strerror(errno), name, 0);
+			res = -SENSORS_ERR_PARSE;
+			goto exit_cleanup;
+		}
+
+		/* Also check for files in default directory */
+		res = add_config_from_dir(DEFAULT_CONFIG_DIR);
+		if (res)
+			goto exit_cleanup;
+	}
+
+	return 0;
+
+exit_cleanup:
+	sensors_cleanup();
+	return res;
+}
+
+static void free_chip_name(sensors_chip_name *name)
+{
+	free(name->prefix);
+	free(name->path);
+}
+
+static void free_chip_features(sensors_chip_features *features)
+{
+	int i;
+
+	for (i = 0; i < features->subfeature_count; i++)
+		free(features->subfeature[i].name);
+	free(features->subfeature);
+	for (i = 0; i < features->feature_count; i++)
+		free(features->feature[i].name);
+	free(features->feature);
+}
+
+static void free_label(sensors_label *label)
+{
+	free(label->name);
+	free(label->value);
+}
+
+void sensors_free_expr(sensors_expr *expr)
+{
+	if (expr->kind == sensors_kind_var)
+		free(expr->data.var);
+	else if (expr->kind == sensors_kind_sub) {
+		if (expr->data.subexpr.sub1)
+			sensors_free_expr(expr->data.subexpr.sub1);
+		if (expr->data.subexpr.sub2)
+			sensors_free_expr(expr->data.subexpr.sub2);
+	}
+	free(expr);
+}
+
+static void free_set(sensors_set *set)
+{
+	free(set->name);
+	sensors_free_expr(set->value);
+}
+
+static void free_compute(sensors_compute *compute)
+{
+	free(compute->name);
+	sensors_free_expr(compute->from_proc);
+	sensors_free_expr(compute->to_proc);
+}
+
+static void free_ignore(sensors_ignore *ignore)
+{
+	free(ignore->name);
+}
+
+static void free_chip(sensors_chip *chip)
+{
+	int i;
+
+	for (i = 0; i < chip->chips.fits_count; i++)
+		free_chip_name(&chip->chips.fits[i]);
+	free(chip->chips.fits);
+	chip->chips.fits_count = chip->chips.fits_max = 0;
+
+	for (i = 0; i < chip->labels_count; i++)
+		free_label(&chip->labels[i]);
+	free(chip->labels);
+	chip->labels_count = chip->labels_max = 0;
+
+	for (i = 0; i < chip->sets_count; i++)
+		free_set(&chip->sets[i]);
+	free(chip->sets);
+	chip->sets_count = chip->sets_max = 0;
+
+	for (i = 0; i < chip->computes_count; i++)
+		free_compute(&chip->computes[i]);
+	free(chip->computes);
+	chip->computes_count = chip->computes_max = 0;
+
+	for (i = 0; i < chip->ignores_count; i++)
+		free_ignore(&chip->ignores[i]);
+	free(chip->ignores);
+	chip->ignores_count = chip->ignores_max = 0;
+}
+
+void sensors_cleanup(void)
+{
+	int i;
+
+	for (i = 0; i < sensors_proc_chips_count; i++) {
+		free_chip_name(&sensors_proc_chips[i].chip);
+		free_chip_features(&sensors_proc_chips[i]);
+	}
+	free(sensors_proc_chips);
+	sensors_proc_chips = NULL;
+	sensors_proc_chips_count = sensors_proc_chips_max = 0;
+
+	for (i = 0; i < sensors_config_chips_count; i++)
+		free_chip(&sensors_config_chips[i]);
+	free(sensors_config_chips);
+	sensors_config_chips = NULL;
+	sensors_config_chips_count = sensors_config_chips_max = 0;
+	sensors_config_chips_subst = 0;
+
+	for (i = 0; i < sensors_proc_bus_count; i++)
+		free_bus(&sensors_proc_bus[i]);
+	free(sensors_proc_bus);
+	sensors_proc_bus = NULL;
+	sensors_proc_bus_count = sensors_proc_bus_max = 0;
+
+	for (i = 0; i < sensors_config_files_count; i++)
+		free(sensors_config_files[i]);
+	free(sensors_config_files);
+	sensors_config_files = NULL;
+	sensors_config_files_count = sensors_config_files_max = 0;
+}
Index: /lm-sensors/tags/V3-3-0/lib/libsensors.3
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/libsensors.3	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/libsensors.3	(revision 5844)
@@ -0,0 +1,263 @@
+.\" Copyright (C) 1998, 1999  Adrian Baugh <adrian.baugh@keble.ox.ac.uk>
+.\" Copyright (C) 2007, 2009  Jean Delvare <khali@linux-fr.org>
+.\" based on sensors.h, part of libsensors by Frodo Looijaard
+.\" libsensors is distributed under the LGPL
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one
+.\" 
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date.  The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein.  The author(s) may not
+.\" have taken the same level of care in the production of this manual,
+.\" which is licensed free of charge, as they might when working
+.\" professionally.
+.\" 
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+.\"
+.\" References consulted:
+.\"     libsensors source code
+.TH libsensors 3  "February 2009" "lm-sensors 3" "Linux Programmer's Manual"
+
+.SH NAME
+libsensors \- publicly accessible functions provided by the sensors library
+
+.SH SYNOPSIS
+.nf
+.B #include <sensors/sensors.h>
+
+/* Library initialization and clean-up */
+.BI "int sensors_init(FILE *" input ");"
+.B void sensors_cleanup(void);
+.BI "const char *" libsensors_version ";"
+
+/* Chip name handling */
+.BI "int sensors_parse_chip_name(const char *" orig_name ","
+.BI "                            sensors_chip_name *" res ");"
+.BI "void sensors_free_chip_name(sensors_chip_name *" chip ");"
+.BI "int sensors_snprintf_chip_name(char *" str ", size_t " size ","
+.BI "                               const sensors_chip_name *" chip ");"
+.BI "const char *sensors_get_adapter_name(const sensors_bus_id *" bus ");"
+
+/* Chips and features enumeration */
+.B const sensors_chip_name *
+.BI "sensors_get_detected_chips(const sensors_chip_name *" match ","
+.BI "                           int *" nr ");"
+.B const sensors_feature *
+.BI "sensors_get_features(const sensors_chip_name *" name ","
+.BI "                     int *" nr ");"
+.B const sensors_subfeature *
+.BI "sensors_get_all_subfeatures(const sensors_chip_name *" name ","
+.BI "                            const sensors_feature *" feature ","
+.BI "                            int *" nr ");"
+.B const sensors_subfeature *
+.BI "sensors_get_subfeature(const sensors_chip_name *" name ","
+.BI "                       const sensors_feature *" feature ","
+.BI "                       sensors_subfeature_type " type ");"
+
+/* Features access */
+.BI "char *sensors_get_label(const sensors_chip_name *" name ","
+.BI "                        const sensors_feature *" feature ");"
+.BI "int sensors_get_value(const sensors_chip_name *" name ", int " subfeat_nr ","
+.BI "                      double *" value ");"
+.BI "int sensors_set_value(const sensors_chip_name *" name ", int " subfeat_nr ","
+.BI "                      double " value ");"
+.BI "int sensors_do_chip_sets(const sensors_chip_name *" name ");"
+
+.B #include <sensors/error.h>
+
+/* Error decoding */
+.BI "const char *sensors_strerror(int " errnum ");"
+
+/* Error handlers */
+.BI "void (*sensors_parse_error) (const char *" err ", int " lineno ");"
+.BI "void (*sensors_parse_error_wfn) (const char *" err ","
+.BI "                                 const char *" filename ", int " lineno ");"
+.BI "void (*sensors_fatal_error) (const char *" proc ", const char *" err ");"
+.fi
+
+.SH DESCRIPTION
+.B sensors_init()
+loads the configuration file and the detected chips list. If this returns a
+value unequal to zero, you are in trouble; you can not assume anything will
+be initialized properly. If you want to reload the configuration file, call
+sensors_cleanup() below before calling sensors_init() again.
+
+If FILE is NULL, the default configuration files are used (see the FILES
+section below). Most applications will want to do that.
+
+.B sensors_cleanup()
+cleans everything up: you can't access anything after this, until the next sensors_init() call!
+
+.B libsensors_version
+is a string representing the version of libsensors.
+
+.B sensors_parse_chip_name()
+parses a chip name to the internal representation. Return 0 on success,
+<0 on error. Make sure to call sensors_free_chip_name() when you're done
+with the data.
+
+.B sensors_free_chip_name()
+frees the memory that may have been allocated for the internal
+representation of a chip name. You only have to call this for chip
+names which do not originate from libsensors itself (that is, chip
+names which were generated by sensors_parse_chip_name()).
+
+.B sensors_snprintf_chip_name()
+prints a chip name from its internal representation. Note that chip should
+not contain wildcard values! Return the number of characters printed on
+success (same as snprintf), <0 on error.
+
+.B sensors_get_adapter_name()
+returns the adapter name of a bus number, as used within the
+sensors_chip_name structure. If it could not be found, it returns NULL.
+
+.B sensors_get_detected_chips()
+returns all detected chips that match a given chip name,
+one by one. If no chip name is provided, all detected chips are returned.
+To start at the beginning of the list, use 0 for nr; NULL is returned if
+we are at the end of the list. Do not try to change these chip names, as
+they point to internal structures!
+
+.B sensors_get_features()
+returns all main features of a specific chip. nr is an internally
+used variable. Set it to zero to start at the begin of the list. If no
+more features are found NULL is returned.
+Do not try to change the returned structure; you will corrupt internal
+data structures.
+
+.B sensors_get_all_subfeatures()
+returns all subfeatures of a given main feature. nr is an internally
+used variable. Set it to zero to start at the begin of the list. If no
+more subfeatures are found NULL is returned.
+Do not try to change the returned structure; you will corrupt internal
+data structures.
+
+.B sensors_get_subfeature()
+returns the subfeature of the given type for a given main feature,
+if it exists, NULL otherwise.
+Do not try to change the returned structure; you will corrupt internal
+data structures.
+
+.B sensors_get_label()
+looks up the label which belongs to this chip. Note that chip should not
+contain wildcard values! The returned string is newly allocated (free it
+yourself). On failure, NULL is returned.
+If no label exists for this feature, its name is returned itself.
+
+.B sensors_get_value()
+Reads the value of a subfeature of a certain chip. Note that chip should not
+contain wildcard values! This function will return 0 on success, and <0 on
+failure.
+
+.B sensors_set_value()
+sets the value of a subfeature of a certain chip. Note that chip should not
+contain wildcard values! This function will return 0 on success, and <0 on
+failure.
+
+.B sensors_do_chip_sets()
+executes all set statements for this particular chip. The chip may contain
+wildcards!  This function will return 0 on success, and <0 on failure.
+
+.B sensors_strerror()
+returns a pointer to a string which describes the error.
+errnum may be negative (the corresponding positive error is returned).
+You may not modify the result!
+
+.B sensors_parse_error()
+and
+.B sensors_parse_error_wfn()
+are functions which are called when a parse error is detected. Give them
+new values, and your own functions are called instead of the default (which
+print to stderr). These functions may terminate the program, but they
+usually output an error and return. The first function is the original
+one, the second one was added later when support for multiple
+configuration files was added.
+The library code now only calls the second function. However, for
+backwards compatibility, if an application provides a custom handling
+function for the first function but not the second, then all parse
+errors will be reported using the first function (that is, the filename
+is never reported.)
+Note that filename can be NULL (if filename isn't known) and lineno
+can be 0 (if the error occurs before the actual parsing starts.)
+
+.B sensors_fatal_error()
+Is a function which is called when an immediately fatal error (like no
+memory left) is detected. Give it a new value, and your own function
+is called instead of the default (which prints to stderr and ends
+the program). Never let it return!
+
+.SH DATA STRUCTURES
+
+Structure \fBsensors_feature\fR contains information related to a given
+feature of a specific chip:
+
+\fBtypedef struct sensors_feature {
+.br
+	const char *name;
+.br
+	int number;
+.br
+	sensors_feature_type type;
+.br
+} sensors_feature;\fP
+
+There are other members not documented here, which are only meant for
+libsensors internal use.
+
+Structure \fBsensors_subfeature\fR contains information related to a given
+subfeature of a specific chip feature:
+
+\fBtypedef struct sensors_subfeature {
+.br
+	const char *name;
+.br
+	int number;
+.br
+	sensors_subfeature_type type;
+.br
+	int mapping;
+.br
+	unsigned int flags;
+.br
+} sensors_subfeature;\fP
+
+The flags field is a bitfield, its value is a combination of
+\fBSENSORS_MODE_R\fR (readable), \fBSENSORS_MODE_W\fR (writable) and
+\fBSENSORS_COMPUTE_MAPPING\fR (affected by the computation rules of the
+main feature).
+
+.SH FILES
+.I /etc/sensors3.conf
+.br
+.I /etc/sensors.conf
+.RS
+The system-wide
+.BR libsensors (3)
+configuration file. /etc/sensors3.conf is tried first, and if it doesn't exist,
+/etc/sensors.conf is used instead.
+.RE
+
+.I /etc/sensors.d
+.RS
+A directory where you can put additional libsensors configuration files.
+Files found in this directory will be processed in alphabetical order after
+the default configuration file. Files with names that start with a dot are
+ignored.
+.RE
+
+.SH SEE ALSO
+sensors.conf(5)
+
+.SH AUTHOR
+Frodo Looijaard and the lm_sensors group
+http://www.lm-sensors.org/
+
Index: /lm-sensors/tags/V3-3-0/lib/access.h
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/access.h	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/access.h	(revision 5844)
@@ -0,0 +1,33 @@
+/*
+    access.h - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    Copyright (C) 2007        Jean Delvare <khali@linux-fr.org>
+
+    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 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_SENSORS_ACCESS_H
+#define LIB_SENSORS_ACCESS_H
+
+#include "sensors.h"
+#include "data.h"
+
+/* Check whether the chip name is an 'absolute' name, which can only match
+   one chip, or whether it has wildcards. Returns 0 if it is absolute, 1
+   if there are wildcards. */
+int sensors_chip_name_has_wildcards(const sensors_chip_name *chip);
+
+#endif /* def LIB_SENSORS_ACCESS_H */
Index: /lm-sensors/tags/V3-3-0/lib/sysfs.c
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/sysfs.c	(revision 5938)
+++ /lm-sensors/tags/V3-3-0/lib/sysfs.c	(revision 5938)
@@ -0,0 +1,895 @@
+/*
+    sysfs.c - Part of libsensors, a library for reading Linux sensor data
+    Copyright (c) 2005 Mark M. Hoffman <mhoffman@lightlink.com>
+    Copyright (C) 2007-2010 Jean Delvare <khali@linux-fr.org>
+
+    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 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 define needed for strndup() */
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include <dirent.h>
+#include "data.h"
+#include "error.h"
+#include "access.h"
+#include "general.h"
+#include "sysfs.h"
+
+
+/****************************************************************************/
+
+#define ATTR_MAX	128
+
+/*
+ * Read an attribute from sysfs
+ * Returns a pointer to a freshly allocated string; free it yourself.
+ * If the file doesn't exist or can't be read, NULL is returned.
+ */
+static char *sysfs_read_attr(const char *device, const char *attr)
+{
+	char path[NAME_MAX];
+	char buf[ATTR_MAX], *p;
+	FILE *f;
+
+	snprintf(path, NAME_MAX, "%s/%s", device, attr);
+
+	if (!(f = fopen(path, "r")))
+		return NULL;
+	p = fgets(buf, ATTR_MAX, f);
+	fclose(f);
+	if (!p)
+		return NULL;
+
+	/* Last byte is a '\n'; chop that off */
+	p = strndup(buf, strlen(buf) - 1);
+	if (!p)
+		sensors_fatal_error(__func__, "Out of memory");
+	return p;
+}
+
+/*
+ * Call an arbitrary function for each class device of the given class
+ * Returns 0 on success (all calls returned 0), a positive errno for
+ * local errors, or a negative error value if any call fails.
+ */
+static int sysfs_foreach_classdev(const char *class_name,
+				   int (*func)(const char *, const char *))
+{
+	char path[NAME_MAX];
+	int path_off, ret;
+	DIR *dir;
+	struct dirent *ent;
+
+	path_off = snprintf(path, NAME_MAX, "%s/class/%s",
+			    sensors_sysfs_mount, class_name);
+	if (!(dir = opendir(path)))
+		return errno;
+
+	ret = 0;
+	while (!ret && (ent = readdir(dir))) {
+		if (ent->d_name[0] == '.')	/* skip hidden entries */
+			continue;
+
+		snprintf(path + path_off, NAME_MAX - path_off, "/%s",
+			 ent->d_name);
+		ret = func(path, ent->d_name);
+	}
+
+	closedir(dir);
+	return ret;
+}
+
+/*
+ * Call an arbitrary function for each device of the given bus type
+ * Returns 0 on success (all calls returned 0), a positive errno for
+ * local errors, or a negative error value if any call fails.
+ */
+static int sysfs_foreach_busdev(const char *bus_type,
+				 int (*func)(const char *, const char *))
+{
+	char path[NAME_MAX];
+	int path_off, ret;
+	DIR *dir;
+	struct dirent *ent;
+
+	path_off = snprintf(path, NAME_MAX, "%s/bus/%s/devices",
+			    sensors_sysfs_mount, bus_type);
+	if (!(dir = opendir(path)))
+		return errno;
+
+	ret = 0;
+	while (!ret && (ent = readdir(dir))) {
+		if (ent->d_name[0] == '.')	/* skip hidden entries */
+			continue;
+
+		snprintf(path + path_off, NAME_MAX - path_off, "/%s",
+			 ent->d_name);
+		ret = func(path, ent->d_name);
+	}
+
+	closedir(dir);
+	return ret;
+}
+
+/****************************************************************************/
+
+char sensors_sysfs_mount[NAME_MAX];
+
+#define MAX_MAIN_SENSOR_TYPES	(SENSORS_FEATURE_MAX_MAIN - SENSORS_FEATURE_IN)
+#define MAX_OTHER_SENSOR_TYPES	(SENSORS_FEATURE_MAX_OTHER - SENSORS_FEATURE_VID)
+#define MAX_SENSORS_PER_TYPE	24
+/* max_subfeatures is now computed dynamically */
+#define FEATURE_SIZE		(max_subfeatures * 2)
+#define FEATURE_TYPE_SIZE	(MAX_SENSORS_PER_TYPE * FEATURE_SIZE)
+
+/*
+ * Room for all 7 main types (in, fan, temp, power, energy, current, humidity)
+ * and 2 other types (VID, intrusion) with all their subfeatures + misc features
+ */
+#define SUB_OFFSET_OTHER	(MAX_MAIN_SENSOR_TYPES * FEATURE_TYPE_SIZE)
+#define SUB_OFFSET_MISC		(SUB_OFFSET_OTHER + \
+				 MAX_OTHER_SENSOR_TYPES * FEATURE_TYPE_SIZE)
+#define ALL_POSSIBLE_SUBFEATURES	(SUB_OFFSET_MISC + 1)
+
+static
+int get_type_scaling(sensors_subfeature_type type)
+{
+	/* Multipliers for subfeatures */
+	switch (type & 0xFF80) {
+	case SENSORS_SUBFEATURE_IN_INPUT:
+	case SENSORS_SUBFEATURE_TEMP_INPUT:
+	case SENSORS_SUBFEATURE_CURR_INPUT:
+	case SENSORS_SUBFEATURE_HUMIDITY_INPUT:
+		return 1000;
+	case SENSORS_SUBFEATURE_FAN_INPUT:
+		return 1;
+	case SENSORS_SUBFEATURE_POWER_AVERAGE:
+	case SENSORS_SUBFEATURE_ENERGY_INPUT:
+		return 1000000;
+	}
+
+	/* Multipliers for second class subfeatures
+	   that need their own multiplier */
+	switch (type) {
+	case SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL:
+	case SENSORS_SUBFEATURE_VID:
+	case SENSORS_SUBFEATURE_TEMP_OFFSET:
+		return 1000;
+	default:
+		return 1;
+	}
+}
+
+static
+char *get_feature_name(sensors_feature_type ftype, char *sfname)
+{
+	char *name, *underscore;
+
+	switch (ftype) {
+	case SENSORS_FEATURE_IN:
+	case SENSORS_FEATURE_FAN:
+	case SENSORS_FEATURE_TEMP:
+	case SENSORS_FEATURE_POWER:
+	case SENSORS_FEATURE_ENERGY:
+	case SENSORS_FEATURE_CURR:
+	case SENSORS_FEATURE_HUMIDITY:
+	case SENSORS_FEATURE_INTRUSION:
+		underscore = strchr(sfname, '_');
+		name = strndup(sfname, underscore - sfname);
+		if (!name)
+			sensors_fatal_error(__func__, "Out of memory");
+
+		break;
+	default:
+		name = strdup(sfname);
+		if (!name)
+			sensors_fatal_error(__func__, "Out of memory");
+	}
+
+	return name;
+}
+
+/* Static mappings for use by sensors_subfeature_get_type() */
+struct subfeature_type_match
+{
+	const char *name;
+	sensors_subfeature_type type;
+};
+
+struct feature_type_match
+{
+	const char *name;
+	const struct subfeature_type_match *submatches;
+};
+
+static const struct subfeature_type_match temp_matches[] = {
+	{ "input", SENSORS_SUBFEATURE_TEMP_INPUT },
+	{ "max", SENSORS_SUBFEATURE_TEMP_MAX },
+	{ "max_hyst", SENSORS_SUBFEATURE_TEMP_MAX_HYST },
+	{ "min", SENSORS_SUBFEATURE_TEMP_MIN },
+	{ "crit", SENSORS_SUBFEATURE_TEMP_CRIT },
+	{ "crit_hyst", SENSORS_SUBFEATURE_TEMP_CRIT_HYST },
+	{ "lcrit", SENSORS_SUBFEATURE_TEMP_LCRIT },
+	{ "emergency", SENSORS_SUBFEATURE_TEMP_EMERGENCY },
+	{ "emergency_hyst", SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST },
+	{ "alarm", SENSORS_SUBFEATURE_TEMP_ALARM },
+	{ "min_alarm", SENSORS_SUBFEATURE_TEMP_MIN_ALARM },
+	{ "max_alarm", SENSORS_SUBFEATURE_TEMP_MAX_ALARM },
+	{ "crit_alarm", SENSORS_SUBFEATURE_TEMP_CRIT_ALARM },
+	{ "emergency_alarm", SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM },
+	{ "lcrit_alarm", SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM },
+	{ "fault", SENSORS_SUBFEATURE_TEMP_FAULT },
+	{ "type", SENSORS_SUBFEATURE_TEMP_TYPE },
+	{ "offset", SENSORS_SUBFEATURE_TEMP_OFFSET },
+	{ "beep", SENSORS_SUBFEATURE_TEMP_BEEP },
+	{ NULL, 0 }
+};
+
+static const struct subfeature_type_match in_matches[] = {
+	{ "input", SENSORS_SUBFEATURE_IN_INPUT },
+	{ "min", SENSORS_SUBFEATURE_IN_MIN },
+	{ "max", SENSORS_SUBFEATURE_IN_MAX },
+	{ "lcrit", SENSORS_SUBFEATURE_IN_LCRIT },
+	{ "crit", SENSORS_SUBFEATURE_IN_CRIT },
+	{ "alarm", SENSORS_SUBFEATURE_IN_ALARM },
+	{ "min_alarm", SENSORS_SUBFEATURE_IN_MIN_ALARM },
+	{ "max_alarm", SENSORS_SUBFEATURE_IN_MAX_ALARM },
+	{ "lcrit_alarm", SENSORS_SUBFEATURE_IN_LCRIT_ALARM },
+	{ "crit_alarm", SENSORS_SUBFEATURE_IN_CRIT_ALARM },
+	{ "beep", SENSORS_SUBFEATURE_IN_BEEP },
+	{ NULL, 0 }
+};
+
+static const struct subfeature_type_match fan_matches[] = {
+	{ "input", SENSORS_SUBFEATURE_FAN_INPUT },
+	{ "min", SENSORS_SUBFEATURE_FAN_MIN },
+	{ "div", SENSORS_SUBFEATURE_FAN_DIV },
+	{ "pulses", SENSORS_SUBFEATURE_FAN_PULSES },
+	{ "alarm", SENSORS_SUBFEATURE_FAN_ALARM },
+	{ "fault", SENSORS_SUBFEATURE_FAN_FAULT },
+	{ "beep", SENSORS_SUBFEATURE_FAN_BEEP },
+	{ NULL, 0 }
+};
+
+static const struct subfeature_type_match power_matches[] = {
+	{ "average", SENSORS_SUBFEATURE_POWER_AVERAGE },
+	{ "average_highest", SENSORS_SUBFEATURE_POWER_AVERAGE_HIGHEST },
+	{ "average_lowest", SENSORS_SUBFEATURE_POWER_AVERAGE_LOWEST },
+	{ "input", SENSORS_SUBFEATURE_POWER_INPUT },
+	{ "input_highest", SENSORS_SUBFEATURE_POWER_INPUT_HIGHEST },
+	{ "input_lowest", SENSORS_SUBFEATURE_POWER_INPUT_LOWEST },
+	{ "cap", SENSORS_SUBFEATURE_POWER_CAP },
+	{ "cap_hyst", SENSORS_SUBFEATURE_POWER_CAP_HYST },
+	{ "cap_alarm", SENSORS_SUBFEATURE_POWER_CAP_ALARM },
+	{ "alarm", SENSORS_SUBFEATURE_POWER_ALARM },
+	{ "max", SENSORS_SUBFEATURE_POWER_MAX },
+	{ "max_alarm", SENSORS_SUBFEATURE_POWER_MAX_ALARM },
+	{ "crit", SENSORS_SUBFEATURE_POWER_CRIT },
+	{ "crit_alarm", SENSORS_SUBFEATURE_POWER_CRIT_ALARM },
+	{ "average_interval", SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL },
+	{ NULL, 0 }
+};
+
+static const struct subfeature_type_match energy_matches[] = {
+	{ "input", SENSORS_SUBFEATURE_ENERGY_INPUT },
+	{ NULL, 0 }
+};
+
+static const struct subfeature_type_match curr_matches[] = {
+	{ "input", SENSORS_SUBFEATURE_CURR_INPUT },
+	{ "min", SENSORS_SUBFEATURE_CURR_MIN },
+	{ "max", SENSORS_SUBFEATURE_CURR_MAX },
+	{ "lcrit", SENSORS_SUBFEATURE_CURR_LCRIT },
+	{ "crit", SENSORS_SUBFEATURE_CURR_CRIT },
+	{ "alarm", SENSORS_SUBFEATURE_CURR_ALARM },
+	{ "min_alarm", SENSORS_SUBFEATURE_CURR_MIN_ALARM },
+	{ "max_alarm", SENSORS_SUBFEATURE_CURR_MAX_ALARM },
+	{ "lcrit_alarm", SENSORS_SUBFEATURE_CURR_LCRIT_ALARM },
+	{ "crit_alarm", SENSORS_SUBFEATURE_CURR_CRIT_ALARM },
+	{ "beep", SENSORS_SUBFEATURE_CURR_BEEP },
+	{ NULL, 0 }
+};
+
+static const struct subfeature_type_match humidity_matches[] = {
+	{ "input", SENSORS_SUBFEATURE_HUMIDITY_INPUT },
+	{ NULL, 0 }
+};
+
+static const struct subfeature_type_match cpu_matches[] = {
+	{ "vid", SENSORS_SUBFEATURE_VID },
+	{ NULL, 0 }
+};
+
+static const struct subfeature_type_match intrusion_matches[] = {
+	{ "alarm", SENSORS_SUBFEATURE_INTRUSION_ALARM },
+	{ "beep", SENSORS_SUBFEATURE_INTRUSION_BEEP },
+	{ NULL, 0 }
+};
+static struct feature_type_match matches[] = {
+	{ "temp%d%c", temp_matches },
+	{ "in%d%c", in_matches },
+	{ "fan%d%c", fan_matches },
+	{ "cpu%d%c", cpu_matches },
+	{ "power%d%c", power_matches },
+	{ "curr%d%c", curr_matches },
+	{ "energy%d%c", energy_matches },
+	{ "intrusion%d%c", intrusion_matches },
+	{ "humidity%d%c", humidity_matches },
+};
+
+/* Return the subfeature type and channel number based on the subfeature
+   name */
+static
+sensors_subfeature_type sensors_subfeature_get_type(const char *name, int *nr)
+{
+	char c;
+	int i, count;
+	const struct subfeature_type_match *submatches;
+
+	/* Special case */
+	if (!strcmp(name, "beep_enable")) {
+		*nr = 0;
+		return SENSORS_SUBFEATURE_BEEP_ENABLE;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(matches); i++)
+		if ((count = sscanf(name, matches[i].name, nr, &c)))
+			break;
+
+	if (i == ARRAY_SIZE(matches) || count != 2 || c != '_')
+		return SENSORS_SUBFEATURE_UNKNOWN;  /* no match */
+
+	submatches = matches[i].submatches;
+	name = strchr(name + 3, '_') + 1;
+	for (i = 0; submatches[i].name != NULL; i++)
+		if (!strcmp(name, submatches[i].name))
+			return submatches[i].type;
+
+	return SENSORS_SUBFEATURE_UNKNOWN;
+}
+
+static int sensors_compute_max(void)
+{
+	int i, j, max, offset;
+	const struct subfeature_type_match *submatches;
+	sensors_feature_type ftype;
+
+	max = 0;
+	for (i = 0; i < ARRAY_SIZE(matches); i++) {
+		submatches = matches[i].submatches;
+		for (j = 0; submatches[j].name != NULL; j++) {
+			ftype = submatches[j].type >> 8;
+
+			if (ftype < SENSORS_FEATURE_VID) {
+				offset = submatches[j].type & 0x7F;
+				if (offset >= max)
+					max = offset + 1;
+			} else {
+				offset = submatches[j].type & 0xFF;
+				if (offset >= max * 2)
+					max = ((offset + 1) + 1) / 2;
+			}
+		}
+	}
+
+	return max;
+}
+
+static int sensors_get_attr_mode(const char *device, const char *attr)
+{
+	char path[NAME_MAX];
+	struct stat st;
+	int mode = 0;
+
+	snprintf(path, NAME_MAX, "%s/%s", device, attr);
+	if (!stat(path, &st)) {
+		if (st.st_mode & S_IRUSR)
+			mode |= SENSORS_MODE_R;
+		if (st.st_mode & S_IWUSR)
+			mode |= SENSORS_MODE_W;
+	}
+	return mode;
+}
+
+static int sensors_read_dynamic_chip(sensors_chip_features *chip,
+				     const char *dev_path)
+{
+	int i, fnum = 0, sfnum = 0, prev_slot;
+	static int max_subfeatures;
+	DIR *dir;
+	struct dirent *ent;
+	sensors_subfeature *all_subfeatures;
+	sensors_subfeature *dyn_subfeatures;
+	sensors_feature *dyn_features;
+	sensors_feature_type ftype;
+	sensors_subfeature_type sftype;
+
+	if (!(dir = opendir(dev_path)))
+		return -errno;
+
+	/* Dynamically figure out the max number of subfeatures */
+	if (!max_subfeatures)
+		max_subfeatures = sensors_compute_max();
+
+	/* We use a large sparse table at first to store all found
+	   subfeatures, so that we can store them sorted at type and index
+	   and then later create a dense sorted table. */
+	all_subfeatures = calloc(ALL_POSSIBLE_SUBFEATURES,
+				 sizeof(sensors_subfeature));
+	if (!all_subfeatures)
+		sensors_fatal_error(__func__, "Out of memory");
+
+	while ((ent = readdir(dir))) {
+		char *name;
+		int nr;
+
+		/* Skip directories and symlinks */
+		if (ent->d_type != DT_REG)
+			continue;
+
+		name = ent->d_name;
+
+		sftype = sensors_subfeature_get_type(name, &nr);
+		if (sftype == SENSORS_SUBFEATURE_UNKNOWN)
+			continue;
+		ftype = sftype >> 8;
+
+		/* Adjust the channel number */
+		switch (ftype) {
+		case SENSORS_FEATURE_FAN:
+		case SENSORS_FEATURE_TEMP:
+		case SENSORS_FEATURE_POWER:
+		case SENSORS_FEATURE_ENERGY:
+		case SENSORS_FEATURE_CURR:
+		case SENSORS_FEATURE_HUMIDITY:
+			nr--;
+			break;
+		default:
+			break;
+		}
+
+		if (nr < 0 || nr >= MAX_SENSORS_PER_TYPE) {
+			/* More sensors of one type than MAX_SENSORS_PER_TYPE,
+			   we have to ignore it */
+#ifdef DEBUG
+			sensors_fatal_error(__func__,
+					    "Increase MAX_SENSORS_PER_TYPE!");
+#endif
+			continue;
+		}
+
+		/* "calculate" a place to store the subfeature in our sparse,
+		   sorted table */
+		switch (ftype) {
+		case SENSORS_FEATURE_VID:
+		case SENSORS_FEATURE_INTRUSION:
+			i = SUB_OFFSET_OTHER +
+			    (ftype - SENSORS_FEATURE_VID) * FEATURE_TYPE_SIZE +
+			    nr * FEATURE_SIZE + (sftype & 0xFF);
+			break;
+		case SENSORS_FEATURE_BEEP_ENABLE:
+			i = SUB_OFFSET_MISC +
+			    (ftype - SENSORS_FEATURE_BEEP_ENABLE);
+			break;
+		default:
+			i = ftype * FEATURE_TYPE_SIZE +
+			    nr * FEATURE_SIZE +
+			    ((sftype & 0x80) >> 7) * max_subfeatures +
+			    (sftype & 0x7F);
+		}
+
+		if (all_subfeatures[i].name) {
+#ifdef DEBUG
+			sensors_fatal_error(__func__, "Duplicate subfeature");
+#endif
+			continue;
+		}
+
+		/* fill in the subfeature members */
+		all_subfeatures[i].type = sftype;
+		all_subfeatures[i].name = strdup(name);
+		if (!all_subfeatures[i].name)
+			sensors_fatal_error(__func__, "Out of memory");
+
+		/* Other and misc subfeatures are never scaled */
+		if (sftype < SENSORS_SUBFEATURE_VID && !(sftype & 0x80))
+			all_subfeatures[i].flags |= SENSORS_COMPUTE_MAPPING;
+		all_subfeatures[i].flags |= sensors_get_attr_mode(dev_path, name);
+
+		sfnum++;
+	}
+	closedir(dir);
+
+	if (!sfnum) { /* No subfeature */
+		chip->subfeature = NULL;
+		goto exit_free;
+	}
+
+	/* How many main features? */
+	prev_slot = -1;
+	for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) {
+		if (!all_subfeatures[i].name)
+			continue;
+
+		if (i >= SUB_OFFSET_MISC || i / FEATURE_SIZE != prev_slot) {
+			fnum++;
+			prev_slot = i / FEATURE_SIZE;
+		}
+	}
+
+	dyn_subfeatures = calloc(sfnum, sizeof(sensors_subfeature));
+	dyn_features = calloc(fnum, sizeof(sensors_feature));
+	if (!dyn_subfeatures || !dyn_features)
+		sensors_fatal_error(__func__, "Out of memory");
+
+	/* Copy from the sparse array to the compact array */
+	sfnum = 0;
+	fnum = -1;
+	prev_slot = -1;
+	for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) {
+		if (!all_subfeatures[i].name)
+			continue;
+
+		/* New main feature? */
+		if (i >= SUB_OFFSET_MISC || i / FEATURE_SIZE != prev_slot) {
+			ftype = all_subfeatures[i].type >> 8;
+			fnum++;
+			prev_slot = i / FEATURE_SIZE;
+
+			dyn_features[fnum].name = get_feature_name(ftype,
+						all_subfeatures[i].name);
+			dyn_features[fnum].number = fnum;
+			dyn_features[fnum].first_subfeature = sfnum;
+			dyn_features[fnum].type = ftype;
+		}
+
+		dyn_subfeatures[sfnum] = all_subfeatures[i];
+		dyn_subfeatures[sfnum].number = sfnum;
+		/* Back to the feature */
+		dyn_subfeatures[sfnum].mapping = fnum;
+
+		sfnum++;
+	}
+
+	chip->subfeature = dyn_subfeatures;
+	chip->subfeature_count = sfnum;
+	chip->feature = dyn_features;
+	chip->feature_count = ++fnum;
+
+exit_free:
+	free(all_subfeatures);
+	return 0;
+}
+
+/* returns !0 if sysfs filesystem was found, 0 otherwise */
+int sensors_init_sysfs(void)
+{
+	struct stat statbuf;
+
+	snprintf(sensors_sysfs_mount, NAME_MAX, "%s", "/sys");
+	if (stat(sensors_sysfs_mount, &statbuf) < 0
+	 || statbuf.st_nlink <= 2)	/* Empty directory */
+		return 0;
+
+	return 1;
+}
+
+/* returns: number of devices added (0 or 1) if successful, <0 otherwise */
+static int sensors_read_one_sysfs_chip(const char *dev_path,
+				       const char *dev_name,
+				       const char *hwmon_path)
+{
+	int domain, bus, slot, fn, vendor, product, id;
+	int err = -SENSORS_ERR_KERNEL;
+	char *bus_attr;
+	char bus_path[NAME_MAX];
+	char linkpath[NAME_MAX];
+	char subsys_path[NAME_MAX], *subsys;
+	int sub_len;
+	sensors_chip_features entry;
+
+	/* ignore any device without name attribute */
+	if (!(entry.chip.prefix = sysfs_read_attr(hwmon_path, "name")))
+		return 0;
+
+	entry.chip.path = strdup(hwmon_path);
+	if (!entry.chip.path)
+		sensors_fatal_error(__func__, "Out of memory");
+
+	if (dev_path == NULL) {
+		/* Virtual device */
+		entry.chip.bus.type = SENSORS_BUS_TYPE_VIRTUAL;
+		entry.chip.bus.nr = 0;
+		/* For now we assume that virtual devices are unique */
+		entry.chip.addr = 0;
+		goto done;
+	}
+
+	/* Find bus type */
+	snprintf(linkpath, NAME_MAX, "%s/subsystem", dev_path);
+	sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1);
+	if (sub_len < 0 && errno == ENOENT) {
+		/* Fallback to "bus" link for kernels <= 2.6.17 */
+		snprintf(linkpath, NAME_MAX, "%s/bus", dev_path);
+		sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1);
+	}
+	if (sub_len < 0) {
+		/* Older kernels (<= 2.6.11) have neither the subsystem
+		   symlink nor the bus symlink */
+		if (errno == ENOENT)
+			subsys = NULL;
+		else
+			goto exit_free;
+	} else {
+		subsys_path[sub_len] = '\0';
+		subsys = strrchr(subsys_path, '/') + 1;
+	}
+
+	if ((!subsys || !strcmp(subsys, "i2c")) &&
+	    sscanf(dev_name, "%hd-%x", &entry.chip.bus.nr,
+		   &entry.chip.addr) == 2) {
+		/* find out if legacy ISA or not */
+		if (entry.chip.bus.nr == 9191) {
+			entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
+			entry.chip.bus.nr = 0;
+		} else {
+			entry.chip.bus.type = SENSORS_BUS_TYPE_I2C;
+			snprintf(bus_path, sizeof(bus_path),
+				"%s/class/i2c-adapter/i2c-%d/device",
+				sensors_sysfs_mount, entry.chip.bus.nr);
+
+			if ((bus_attr = sysfs_read_attr(bus_path, "name"))) {
+				if (!strncmp(bus_attr, "ISA ", 4)) {
+					entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
+					entry.chip.bus.nr = 0;
+				}
+
+				free(bus_attr);
+			}
+		}
+	} else
+	if ((!subsys || !strcmp(subsys, "spi")) &&
+	    sscanf(dev_name, "spi%hd.%d", &entry.chip.bus.nr,
+		   &entry.chip.addr) == 2) {
+		/* SPI */
+		entry.chip.bus.type = SENSORS_BUS_TYPE_SPI;
+	} else
+	if ((!subsys || !strcmp(subsys, "pci")) &&
+	    sscanf(dev_name, "%x:%x:%x.%x", &domain, &bus, &slot, &fn) == 4) {
+		/* PCI */
+		entry.chip.addr = (domain << 16) + (bus << 8) + (slot << 3) + fn;
+		entry.chip.bus.type = SENSORS_BUS_TYPE_PCI;
+		entry.chip.bus.nr = 0;
+	} else
+	if ((!subsys || !strcmp(subsys, "platform") ||
+			!strcmp(subsys, "of_platform"))) {
+		/* must be new ISA (platform driver) */
+		if (sscanf(dev_name, "%*[a-z0-9_].%d", &entry.chip.addr) != 1)
+			entry.chip.addr = 0;
+		entry.chip.bus.type = SENSORS_BUS_TYPE_ISA;
+		entry.chip.bus.nr = 0;
+	} else if (subsys && !strcmp(subsys, "acpi")) {
+		entry.chip.bus.type = SENSORS_BUS_TYPE_ACPI;
+		/* For now we assume that acpi devices are unique */
+		entry.chip.bus.nr = 0;
+		entry.chip.addr = 0;
+	} else
+	if (subsys && !strcmp(subsys, "hid") &&
+	    sscanf(dev_name, "%x:%x:%x.%x", &bus, &vendor, &product, &id) == 4) {
+		entry.chip.bus.type = SENSORS_BUS_TYPE_HID;
+		/* As of kernel 2.6.32, the hid device names don't look good */
+		entry.chip.bus.nr = bus;
+		entry.chip.addr = id;
+	} else {
+		/* Ignore unknown device */
+		err = 0;
+		goto exit_free;
+	}
+
+done:
+	if (sensors_read_dynamic_chip(&entry, hwmon_path) < 0)
+		goto exit_free;
+	if (!entry.subfeature) { /* No subfeature, discard chip */
+		err = 0;
+		goto exit_free;
+	}
+	sensors_add_proc_chips(&entry);
+
+	return 1;
+
+exit_free:
+	free(entry.chip.prefix);
+	free(entry.chip.path);
+	return err;
+}
+
+static int sensors_add_hwmon_device_compat(const char *path,
+					   const char *dev_name)
+{
+	int err;
+
+	err = sensors_read_one_sysfs_chip(path, dev_name, path);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+/* returns 0 if successful, !0 otherwise */
+static int sensors_read_sysfs_chips_compat(void)
+{
+	int ret;
+
+	ret = sysfs_foreach_busdev("i2c", sensors_add_hwmon_device_compat);
+	if (ret && ret != ENOENT)
+		return -SENSORS_ERR_KERNEL;
+
+	return 0;
+}
+
+static int sensors_add_hwmon_device(const char *path, const char *classdev)
+{
+	char linkpath[NAME_MAX];
+	char device[NAME_MAX], *device_p;
+	int dev_len, err;
+	(void)classdev; /* hide warning */
+
+	snprintf(linkpath, NAME_MAX, "%s/device", path);
+	dev_len = readlink(linkpath, device, NAME_MAX - 1);
+	if (dev_len < 0) {
+		/* No device link? Treat as virtual */
+		err = sensors_read_one_sysfs_chip(NULL, NULL, path);
+	} else {
+		device[dev_len] = '\0';
+		device_p = strrchr(device, '/') + 1;
+
+		/* The attributes we want might be those of the hwmon class
+		   device, or those of the device itself. */
+		err = sensors_read_one_sysfs_chip(linkpath, device_p, path);
+		if (err == 0)
+			err = sensors_read_one_sysfs_chip(linkpath, device_p,
+							  linkpath);
+	}
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+/* returns 0 if successful, !0 otherwise */
+int sensors_read_sysfs_chips(void)
+{
+	int ret;
+
+	ret = sysfs_foreach_classdev("hwmon", sensors_add_hwmon_device);
+	if (ret == ENOENT) {
+		/* compatibility function for kernel 2.6.n where n <= 13 */
+		return sensors_read_sysfs_chips_compat();
+	}
+
+	if (ret > 0)
+		ret = -SENSORS_ERR_KERNEL;
+	return ret;
+}
+
+/* returns 0 if successful, !0 otherwise */
+static int sensors_add_i2c_bus(const char *path, const char *classdev)
+{
+	sensors_bus entry;
+
+	if (sscanf(classdev, "i2c-%hd", &entry.bus.nr) != 1 ||
+	    entry.bus.nr == 9191) /* legacy ISA */
+		return 0;
+	entry.bus.type = SENSORS_BUS_TYPE_I2C;
+
+	/* Get the adapter name from the classdev "name" attribute
+	 * (Linux 2.6.20 and later). If it fails, fall back to
+	 * the device "name" attribute (for older kernels). */
+	entry.adapter = sysfs_read_attr(path, "name");
+	if (!entry.adapter)
+		entry.adapter = sysfs_read_attr(path, "device/name");
+	if (entry.adapter)
+		sensors_add_proc_bus(&entry);
+
+	return 0;
+}
+
+/* returns 0 if successful, !0 otherwise */
+int sensors_read_sysfs_bus(void)
+{
+	int ret;
+
+	ret = sysfs_foreach_classdev("i2c-adapter", sensors_add_i2c_bus);
+	if (ret == ENOENT)
+		ret = sysfs_foreach_busdev("i2c", sensors_add_i2c_bus);
+	if (ret && ret != ENOENT)
+		return -SENSORS_ERR_KERNEL;
+
+	return 0;
+}
+
+int sensors_read_sysfs_attr(const sensors_chip_name *name,
+			    const sensors_subfeature *subfeature,
+			    double *value)
+{
+	char n[NAME_MAX];
+	FILE *f;
+
+	snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name);
+	if ((f = fopen(n, "r"))) {
+		int res, err = 0;
+
+		errno = 0;
+		res = fscanf(f, "%lf", value);
+		if (res == EOF && errno == EIO)
+			err = -SENSORS_ERR_IO;
+		else if (res != 1)
+			err = -SENSORS_ERR_ACCESS_R;
+		res = fclose(f);
+		if (err)
+			return err;
+
+		if (res == EOF) {
+			if (errno == EIO)
+				return -SENSORS_ERR_IO;
+			else 
+				return -SENSORS_ERR_ACCESS_R;
+		}
+		*value /= get_type_scaling(subfeature->type);
+	} else
+		return -SENSORS_ERR_KERNEL;
+
+	return 0;
+}
+
+int sensors_write_sysfs_attr(const sensors_chip_name *name,
+			     const sensors_subfeature *subfeature,
+			     double value)
+{
+	char n[NAME_MAX];
+	FILE *f;
+
+	snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name);
+	if ((f = fopen(n, "w"))) {
+		int res, err = 0;
+
+		value *= get_type_scaling(subfeature->type);
+		res = fprintf(f, "%d", (int) value);
+		if (res == -EIO)
+			err = -SENSORS_ERR_IO;
+		else if (res < 0)
+			err = -SENSORS_ERR_ACCESS_W;
+		res = fclose(f);
+		if (err)
+			return err;
+
+		if (res == EOF) {
+			if (errno == EIO)
+				return -SENSORS_ERR_IO;
+			else 
+				return -SENSORS_ERR_ACCESS_W;
+		}
+	} else
+		return -SENSORS_ERR_KERNEL;
+
+	return 0;
+}
Index: /lm-sensors/tags/V3-3-0/lib/init.h
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/init.h	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/init.h	(revision 5844)
@@ -0,0 +1,28 @@
+/*
+    init.h - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (C) 2007  Jean Delvare <khali@linux-fr.org>
+
+    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 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_SENSORS_INIT_H
+#define LIB_SENSORS_INIT_H
+
+#include "data.h"
+
+void sensors_free_expr(sensors_expr *expr);
+
+#endif /* def LIB_SENSORS_INIT_H */
Index: /lm-sensors/tags/V3-3-0/lib/data.c
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/data.c	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/data.c	(revision 5844)
@@ -0,0 +1,276 @@
+/*
+    data.c - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    Copyright (C) 2007, 2009  Jean Delvare <khali@linux-fr.org>
+
+    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 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 define needed for strndup() */
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "access.h"
+#include "error.h"
+#include "data.h"
+#include "sensors.h"
+#include "../version.h"
+
+const char *libsensors_version = LM_VERSION;
+
+char **sensors_config_files = NULL;
+int sensors_config_files_count = 0;
+int sensors_config_files_max = 0;
+
+sensors_chip *sensors_config_chips = NULL;
+int sensors_config_chips_count = 0;
+int sensors_config_chips_subst = 0;
+int sensors_config_chips_max = 0;
+
+sensors_bus *sensors_config_busses = NULL;
+int sensors_config_busses_count = 0;
+int sensors_config_busses_max = 0;
+
+sensors_chip_features *sensors_proc_chips = NULL;
+int sensors_proc_chips_count = 0;
+int sensors_proc_chips_max = 0;
+
+sensors_bus *sensors_proc_bus = NULL;
+int sensors_proc_bus_count = 0;
+int sensors_proc_bus_max = 0;
+
+void sensors_free_chip_name(sensors_chip_name *chip)
+{
+	free(chip->prefix);
+}
+
+/*
+   Parse a chip name to the internal representation. These are valid names:
+
+     lm78-i2c-10-5e		*-i2c-10-5e
+     lm78-i2c-10-*		*-i2c-10-*
+     lm78-i2c-*-5e		*-i2c-*-5e
+     lm78-i2c-*-*		*-i2c-*-*
+     lm78-isa-10dd		*-isa-10dd
+     lm78-isa-*			*-isa-*
+     lm78-*			*-*
+
+   Here 'lm78' can be any prefix. 'i2c' and 'isa' are
+   literal strings, just like all dashes '-' and wildcards '*'. '10' can
+   be any decimal i2c bus number. '5e' can be any hexadecimal i2c device
+   address, and '10dd' any hexadecimal isa address.
+
+   The 'prefix' part in the result is freshly allocated. All old contents
+   of res is overwritten. res itself is not allocated. In case of an error
+   return (ie. != 0), res is undefined, but all allocations are undone.
+*/
+
+int sensors_parse_chip_name(const char *name, sensors_chip_name *res)
+{
+	char *dash;
+
+	/* First, the prefix. It's either "*" or a real chip name. */
+	if (!strncmp(name, "*-", 2)) {
+		res->prefix = SENSORS_CHIP_NAME_PREFIX_ANY;
+		name += 2;
+	} else {
+		if (!(dash = strchr(name, '-')))
+			return -SENSORS_ERR_CHIP_NAME;
+		res->prefix = strndup(name, dash - name);
+		if (!res->prefix)
+			sensors_fatal_error(__func__,
+					    "Allocating name prefix");
+		name = dash + 1;
+	}
+
+	/* Then we have either a sole "*" (all chips with this name) or a bus
+	   type and an address. */
+	if (!strcmp(name, "*")) {
+		res->bus.type = SENSORS_BUS_TYPE_ANY;
+		res->bus.nr = SENSORS_BUS_NR_ANY;
+		res->addr = SENSORS_CHIP_NAME_ADDR_ANY;
+		return 0;
+	}
+
+	if (!(dash = strchr(name, '-')))
+		goto ERROR;
+	if (!strncmp(name, "i2c", dash - name))
+		res->bus.type = SENSORS_BUS_TYPE_I2C;
+	else if (!strncmp(name, "isa", dash - name))
+		res->bus.type = SENSORS_BUS_TYPE_ISA;
+	else if (!strncmp(name, "pci", dash - name))
+		res->bus.type = SENSORS_BUS_TYPE_PCI;
+	else if (!strncmp(name, "spi", dash - name))
+		res->bus.type = SENSORS_BUS_TYPE_SPI;
+	else if (!strncmp(name, "virtual", dash - name))
+		res->bus.type = SENSORS_BUS_TYPE_VIRTUAL;
+	else if (!strncmp(name, "acpi", dash - name))
+		res->bus.type = SENSORS_BUS_TYPE_ACPI;
+	else if (!strncmp(name, "hid", dash - name))
+		res->bus.type = SENSORS_BUS_TYPE_HID;
+	else
+		goto ERROR;
+	name = dash + 1;
+
+	/* Some bus types (i2c, spi) have an additional bus number.
+	   For these, the next part is either a "*" (any bus of that type)
+	   or a decimal number. */
+	switch (res->bus.type) {
+	case SENSORS_BUS_TYPE_I2C:
+	case SENSORS_BUS_TYPE_SPI:
+	case SENSORS_BUS_TYPE_HID:
+		if (!strncmp(name, "*-", 2)) {
+			res->bus.nr = SENSORS_BUS_NR_ANY;
+			name += 2;
+			break;
+		}
+
+		res->bus.nr = strtoul(name, &dash, 10);
+		if (*name == '\0' || *dash != '-' || res->bus.nr < 0)
+			goto ERROR;
+		name = dash + 1;
+		break;
+	default:
+		res->bus.nr = SENSORS_BUS_NR_ANY;
+	}
+
+	/* Last part is the chip address, or "*" for any address. */
+	if (!strcmp(name, "*")) {
+		res->addr = SENSORS_CHIP_NAME_ADDR_ANY;
+	} else {
+		res->addr = strtoul(name, &dash, 16);
+		if (*name == '\0' || *dash != '\0' || res->addr < 0)
+			goto ERROR;
+	}
+
+	return 0;
+
+ERROR:
+	free(res->prefix);
+	return -SENSORS_ERR_CHIP_NAME;
+}
+
+int sensors_snprintf_chip_name(char *str, size_t size,
+			       const sensors_chip_name *chip)
+{
+	if (sensors_chip_name_has_wildcards(chip))
+		return -SENSORS_ERR_WILDCARDS;
+
+	switch (chip->bus.type) {
+	case SENSORS_BUS_TYPE_ISA:
+		return snprintf(str, size, "%s-isa-%04x", chip->prefix,
+				chip->addr);
+	case SENSORS_BUS_TYPE_PCI:
+		return snprintf(str, size, "%s-pci-%04x", chip->prefix,
+				chip->addr);
+	case SENSORS_BUS_TYPE_I2C:
+		return snprintf(str, size, "%s-i2c-%hd-%02x", chip->prefix,
+				chip->bus.nr, chip->addr);
+	case SENSORS_BUS_TYPE_SPI:
+		return snprintf(str, size, "%s-spi-%hd-%x", chip->prefix,
+				chip->bus.nr, chip->addr);
+	case SENSORS_BUS_TYPE_VIRTUAL:
+		return snprintf(str, size, "%s-virtual-%x", chip->prefix,
+				chip->addr);
+	case SENSORS_BUS_TYPE_ACPI:
+		return snprintf(str, size, "%s-acpi-%x", chip->prefix,
+				chip->addr);
+	case SENSORS_BUS_TYPE_HID:
+		return snprintf(str, size, "%s-hid-%hd-%x", chip->prefix,
+				chip->bus.nr, chip->addr);
+	}
+
+	return -SENSORS_ERR_CHIP_NAME;
+}
+
+int sensors_parse_bus_id(const char *name, sensors_bus_id *bus)
+{
+	char *endptr;
+
+	if (strncmp(name, "i2c-", 4)) {
+		return -SENSORS_ERR_BUS_NAME;
+	}
+	name += 4;
+	bus->type = SENSORS_BUS_TYPE_I2C;
+	bus->nr = strtoul(name, &endptr, 10);
+	if (*name == '\0' || *endptr != '\0' || bus->nr < 0)
+		return -SENSORS_ERR_BUS_NAME;
+	return 0;
+}
+
+static int sensors_substitute_chip(sensors_chip_name *name,
+				   const char *filename, int lineno)
+{
+	int i, j;
+	for (i = 0; i < sensors_config_busses_count; i++)
+		if (sensors_config_busses[i].bus.type == name->bus.type &&
+		    sensors_config_busses[i].bus.nr == name->bus.nr)
+			break;
+
+	if (i == sensors_config_busses_count) {
+		sensors_parse_error_wfn("Undeclared bus id referenced",
+					filename, lineno);
+		name->bus.nr = SENSORS_BUS_NR_IGNORE;
+		return -SENSORS_ERR_BUS_NAME;
+	}
+
+	/* Compare the adapter names */
+	for (j = 0; j < sensors_proc_bus_count; j++) {
+		if (!strcmp(sensors_config_busses[i].adapter,
+			    sensors_proc_bus[j].adapter)) {
+			name->bus.nr = sensors_proc_bus[j].bus.nr;
+			return 0;
+		}
+	}
+
+	/* We did not find a matching bus name, simply ignore this chip
+	   config entry. */
+	name->bus.nr = SENSORS_BUS_NR_IGNORE;
+	return 0;
+}
+
+/* Bus substitution is on a per-configuration file basis, so we keep
+   memory (in sensors_config_chips_subst) of which chip entries have been
+   already substituted. */
+int sensors_substitute_busses(void)
+{
+	int err, i, j, lineno;
+	sensors_chip_name_list *chips;
+	const char *filename;
+	int res = 0;
+
+	for (i = sensors_config_chips_subst;
+	     i < sensors_config_chips_count; i++) {
+		filename = sensors_config_chips[i].line.filename;
+		lineno = sensors_config_chips[i].line.lineno;
+		chips = &sensors_config_chips[i].chips;
+		for (j = 0; j < chips->fits_count; j++) {
+			/* We can only substitute if a specific bus number
+			   is given. */
+			if (chips->fits[j].bus.nr == SENSORS_BUS_NR_ANY)
+				continue;
+
+			err = sensors_substitute_chip(&chips->fits[j],
+						      filename, lineno);
+			if (err)
+				res = err;
+		}
+	}
+	sensors_config_chips_subst = sensors_config_chips_count;
+	return res;
+}
Index: /lm-sensors/tags/V3-3-0/lib/Module.mk
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/Module.mk	(revision 5939)
+++ /lm-sensors/tags/V3-3-0/lib/Module.mk	(revision 5939)
@@ -0,0 +1,179 @@
+#  Module.mk - Makefile for a Linux module for reading sensor data.
+#  Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+#
+#  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 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.
+
+# Note that MODULE_DIR (the directory in which this file resides) is a
+# 'simply expanded variable'. That means that its value is substituted
+# verbatim in the rules, until it is redefined. 
+MODULE_DIR := lib
+LIB_DIR := $(MODULE_DIR)
+
+# The manual dirs and files
+LIBMAN3DIR := $(MANDIR)/man3
+LIBMAN3FILES := $(MODULE_DIR)/libsensors.3
+LIBMAN5DIR := $(MANDIR)/man5
+LIBMAN5FILES := $(MODULE_DIR)/sensors.conf.5
+
+# 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 error.h and sensors.h.
+LIBMAINVER := 4
+LIBMINORVER := 3.1
+LIBVER := $(LIBMAINVER).$(LIBMINORVER)
+
+# The static lib name, the shared lib name, and the internal ('so') name of
+# the shared lib.
+LIBSHBASENAME := libsensors.so
+LIBSHLIBNAME := libsensors.so.$(LIBVER)
+LIBSTLIBNAME := libsensors.a
+LIBSHSONAME := libsensors.so.$(LIBMAINVER)
+
+LIBTARGETS := $(MODULE_DIR)/$(LIBSHLIBNAME) \
+              $(MODULE_DIR)/$(LIBSHSONAME) $(MODULE_DIR)/$(LIBSHBASENAME)
+ifeq ($(BUILD_STATIC_LIB),1)
+LIBTARGETS += $(MODULE_DIR)/$(LIBSTLIBNAME)
+endif
+
+LIBCSOURCES := $(MODULE_DIR)/data.c $(MODULE_DIR)/general.c \
+               $(MODULE_DIR)/error.c $(MODULE_DIR)/access.c \
+               $(MODULE_DIR)/init.c $(MODULE_DIR)/sysfs.c
+
+LIBOTHEROBJECTS := $(MODULE_DIR)/conf-parse.o $(MODULE_DIR)/conf-lex.o
+LIBSHOBJECTS := $(LIBCSOURCES:.c=.lo) $(LIBOTHEROBJECTS:.o=.lo)
+LIBSTOBJECTS := $(LIBCSOURCES:.c=.ao) $(LIBOTHEROBJECTS:.o=.ao)
+LIBEXTRACLEAN := $(MODULE_DIR)/conf-parse.h $(MODULE_DIR)/conf-parse.c \
+                 $(MODULE_DIR)/conf-lex.c
+
+LIBHEADERFILES := $(MODULE_DIR)/error.h $(MODULE_DIR)/sensors.h
+
+# How to create the shared library
+$(MODULE_DIR)/$(LIBSHLIBNAME): $(LIBSHOBJECTS)
+	$(CC) -shared $(LDFLAGS) -Wl,--version-script=$(LIB_DIR)/libsensors.map -Wl,-soname,$(LIBSHSONAME) -o $@ $^ -lc -lm
+
+$(MODULE_DIR)/$(LIBSHSONAME): $(MODULE_DIR)/$(LIBSHLIBNAME)
+	$(RM) $@
+	$(LN) $(LIBSHLIBNAME) $@
+
+$(MODULE_DIR)/$(LIBSHBASENAME): $(MODULE_DIR)/$(LIBSHLIBNAME)
+	$(RM) $@ 
+	$(LN) $(LIBSHLIBNAME) $@
+
+# And the static library
+$(MODULE_DIR)/$(LIBSTLIBNAME): $(LIBSTOBJECTS)
+	$(RM) $@
+	$(AR) rcvs $@ $^
+
+# Depencies for non-C sources
+$(MODULE_DIR)/conf-lex.c: $(MODULE_DIR)/conf-lex.l $(MODULE_DIR)/general.h \
+                          $(MODULE_DIR)/data.h $(MODULE_DIR)/conf-parse.h
+$(MODULE_DIR)/conf-parse.c: $(MODULE_DIR)/conf-parse.y $(MODULE_DIR)/general.h \
+                            $(MODULE_DIR)/data.h
+$(MODULE_DIR)/conf-parse.h: $(MODULE_DIR)/conf-parse.c
+
+# Include all dependency files
+INCLUDEFILES += $(LIBSHOBJECTS:.lo=.ld) $(LIBSTOBJECTS:.ao=.ad)
+
+# Special warning prevention for flex-generated files
+FLEXNOWARN:=-Wno-shadow -Wno-undef -Wno-unused -Wno-missing-prototypes -Wno-sign-compare
+$(MODULE_DIR)/conf-lex.ao: $(MODULE_DIR)/conf-lex.c
+	$(CC) $(ARCPPFLAGS) $(ARCFLAGS) $(FLEXNOWARN) -c $< -o $@
+$(MODULE_DIR)/conf-lex.lo: $(MODULE_DIR)/conf-lex.c
+	$(CC) $(LIBCPPFLAGS) $(LIBCFLAGS) $(FLEXNOWARN) -c $< -o $@
+
+# Special warning prevention for bison-generated files
+YACCNOWARN:=-Wno-undef
+$(MODULE_DIR)/conf-parse.ao: $(MODULE_DIR)/conf-parse.c
+	$(CC) $(ARCPPFLAGS) $(ARCFLAGS) $(YACCNOWARN) -c $< -o $@
+$(MODULE_DIR)/conf-parse.lo: $(MODULE_DIR)/conf-parse.c
+	$(CC) $(LIBCPPFLAGS) $(LIBCFLAGS) $(YACCNOWARN) -c $< -o $@
+
+REMOVELIBST := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(LIBDIR)/%,$(LIB_DIR)/$(LIBSTLIBNAME))
+REMOVELIBSH := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(LIBDIR)/%,$(LIB_DIR)/$(LIBSHLIBNAME))
+REMOVELNSO  := $(DESTDIR)$(LIBDIR)/$(LIBSHSONAME)
+REMOVELNBS  := $(DESTDIR)$(LIBDIR)/$(LIBSHBASENAME)
+REMOVELIBHF := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(LIBINCLUDEDIR)/%,$(LIBHEADERFILES))
+REMOVEMAN3  := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(LIBMAN3DIR)/%,$(LIBMAN3FILES))
+REMOVEMAN5  := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(LIBMAN5DIR)/%,$(LIBMAN5FILES))
+
+all-lib: $(LIBTARGETS)
+user :: all-lib
+
+# Generate warnings if the install directory isn't in /etc/ld.so.conf
+# or if the library wasn't there before (which means ldconfig must be run).
+# Note that some ld.so's put /usr/lib and /lib first, others put them last,
+# so we can't make any assumptions.
+install-lib: all-lib
+	$(MKDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(LIBINCLUDEDIR) $(DESTDIR)$(LIBMAN3DIR) $(DESTDIR)$(LIBMAN5DIR)
+	@if [ -z "$(DESTDIR)" -a ! -e "$(LIBDIR)/$(LIBSHSONAME)" ] ; then \
+	     echo '******************************************************************************' ; \
+	     echo 'Warning: This is the first installation of the $(LIBSHSONAME)*' ; \
+	     echo '         library files in $(LIBDIR)!' ; \
+	     echo '         You must update the library cache or the userspace tools may fail' ; \
+	     echo '         or have unpredictable results!' ; \
+	     echo '         Run the following command: /sbin/ldconfig' ; \
+	     echo '******************************************************************************' ; \
+	fi
+ifeq ($(BUILD_STATIC_LIB),1)
+	$(INSTALL) -m 644 $(LIB_DIR)/$(LIBSTLIBNAME) $(DESTDIR)$(LIBDIR)
+endif
+	$(INSTALL) -m 755 $(LIB_DIR)/$(LIBSHLIBNAME) $(DESTDIR)$(LIBDIR)
+	$(LN) $(LIBSHLIBNAME) $(DESTDIR)$(LIBDIR)/$(LIBSHSONAME)
+	$(LN) $(LIBSHSONAME) $(DESTDIR)$(LIBDIR)/$(LIBSHBASENAME)
+	@if [ -z "$(DESTDIR)" -a "$(LIBDIR)" != "/usr/lib" -a "$(LIBDIR)" != "/lib" ] ; then \
+	   if [ -e "/usr/lib/$(LIBSHSONAME)" -o -e "/usr/lib/$(LIBSHBASENAME)" ] ; then \
+	     echo '******************************************************************************' ; \
+	     echo 'Warning: You have at least one $(LIBSHBASENAME) library file in /usr/lib' ; \
+	     echo '         and the new library files are in $(LIBDIR)!' ; \
+	     echo '         These old files must be removed or the userspace tools may fail' ; \
+	     echo '         or have unpredictable results!' ; \
+	     echo '         Run the following command: rm /usr/lib/$(LIBSHBASENAME)*' ; \
+	     echo '******************************************************************************' ; \
+	   fi ; \
+	   grep -q '^$(LIBDIR)$$' /etc/ld.so.conf || \
+	   grep -q '^$(LIBDIR)[[:space:]:,=]' /etc/ld.so.conf || \
+	   grep -q '[[:space:]:,]$(LIBDIR)$$' /etc/ld.so.conf || \
+	   grep -q '[[:space:]:,]$(LIBDIR)[[:space:]:,=]' /etc/ld.so.conf || \
+		( echo '******************************************************************************' ; \
+		  echo 'Warning: Library directory $(LIBDIR) is not in /etc/ld.so.conf!' ; \
+		  echo '         Add it and run /sbin/ldconfig for the userspace tools to work.' ; \
+		  echo '******************************************************************************' ) ; \
+	fi
+	$(INSTALL) -m 644 $(LIBHEADERFILES) $(DESTDIR)$(LIBINCLUDEDIR)
+	$(INSTALL) -m 644 $(LIBMAN3FILES) $(DESTDIR)$(LIBMAN3DIR)
+	$(INSTALL) -m 644 $(LIBMAN5FILES) $(DESTDIR)$(LIBMAN5DIR)
+	$(LN) sensors.conf.5 $(DESTDIR)$(LIBMAN5DIR)/sensors3.conf.5
+
+
+user_install :: install-lib
+
+user_uninstall::
+	$(RM) $(REMOVELIBSH) $(REMOVELNSO) $(REMOVELNBS)
+ifeq ($(BUILD_STATIC_LIB),1)
+	$(RM) $(REMOVELIBST)
+endif
+	$(RM) $(REMOVELIBHF) $(REMOVEMAN3) $(REMOVEMAN5)
+# Remove directory if empty, ignore failure
+	$(RMDIR) $(DESTDIR)$(LIBINCLUDEDIR) 2> /dev/null || true
+
+clean-lib:
+	$(RM) $(LIB_DIR)/*.ld $(LIB_DIR)/*.ad
+	$(RM) $(LIB_DIR)/*.lo $(LIB_DIR)/*.ao
+	$(RM) $(LIBTARGETS) $(LIBEXTRACLEAN)
+# old versions
+	$(RM) $(LIB_DIR)/$(LIBSHBASENAME).*
+clean :: clean-lib
Index: /lm-sensors/tags/V3-3-0/lib/sysfs.h
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/sysfs.h	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/sysfs.h	(revision 5844)
@@ -0,0 +1,43 @@
+/*
+    sysfs.h - part of libsensors, a library for reading Linux sensor data
+    Copyright (C)             Mark M. Hoffman <mhoffman@lightlink.com>
+    Copyright (C) 2007        Jean Delvare <khali@linux-fr.org>
+
+    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 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 SENSORS_LIB_SYSFS_H
+#define SENSORS_LIB_SYSFS_H
+
+extern char sensors_sysfs_mount[];
+
+int sensors_init_sysfs(void);
+
+int sensors_read_sysfs_chips(void);
+
+int sensors_read_sysfs_bus(void);
+
+/* Read a value out of a sysfs attribute file */
+int sensors_read_sysfs_attr(const sensors_chip_name *name,
+			    const sensors_subfeature *subfeature,
+			    double *value);
+
+/* Write a value to a sysfs attribute file */
+int sensors_write_sysfs_attr(const sensors_chip_name *name,
+			     const sensors_subfeature *subfeature,
+			     double value);
+
+#endif /* !SENSORS_LIB_SYSFS_H */
Index: /lm-sensors/tags/V3-3-0/lib/general.c
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/general.c	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/general.c	(revision 5844)
@@ -0,0 +1,85 @@
+/*
+    general.c - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+
+    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 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 "error.h"
+#include "general.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+#define A_BUNCH 16
+
+void sensors_malloc_array(void *list, int *num_el, int *max_el, int el_size)
+{
+	void **my_list = (void **)list;
+
+	*my_list = malloc(el_size*A_BUNCH);
+	if (! *my_list)
+		sensors_fatal_error(__func__, "Allocating new elements");
+	*max_el = A_BUNCH;
+	*num_el = 0;
+}
+
+void sensors_free_array(void *list, int *num_el, int *max_el)
+{
+	void **my_list = (void **)list;
+
+	free(*my_list);
+	*my_list = NULL;
+	*num_el = 0;
+	*max_el = 0;
+}
+
+void sensors_add_array_el(const void *el, void *list, int *num_el,
+			  int *max_el, int el_size)
+{
+	int new_max_el;
+	void **my_list = (void *)list;
+	if (*num_el + 1 > *max_el) {
+		new_max_el = *max_el + A_BUNCH;
+		*my_list = realloc(*my_list, new_max_el * el_size);
+		if (! *my_list)
+			sensors_fatal_error(__func__,
+					    "Allocating new elements");
+		*max_el = new_max_el;
+	}
+	memcpy(((char *) *my_list) + *num_el * el_size, el, el_size);
+	(*num_el) ++;
+}
+
+void sensors_add_array_els(const void *els, int nr_els, void *list,
+			   int *num_el, int *max_el, int el_size)
+{
+	int new_max_el;
+	void **my_list = (void *)list;
+	if (*num_el + nr_els > *max_el) {
+		new_max_el = (*max_el + nr_els + A_BUNCH);
+		new_max_el -= new_max_el % A_BUNCH;
+		*my_list = realloc(*my_list, new_max_el * el_size);
+		if (! *my_list)
+			sensors_fatal_error(__func__,
+					    "Allocating new elements");
+		*max_el = new_max_el;
+	}
+	memcpy(((char *)*my_list) + *num_el * el_size, els, el_size * nr_els);
+	*num_el += nr_els;
+}
Index: /lm-sensors/tags/V3-3-0/lib/libsensors.map
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/libsensors.map	(revision 5939)
+++ /lm-sensors/tags/V3-3-0/lib/libsensors.map	(revision 5939)
@@ -0,0 +1,23 @@
+{
+global:
+  libsensors_version;
+  sensors_cleanup;
+  sensors_do_chip_sets;
+  sensors_free_chip_name;
+  sensors_get_adapter_name;
+  sensors_get_all_subfeatures;
+  sensors_get_detected_chips;
+  sensors_get_features;
+  sensors_get_label;
+  sensors_get_subfeature;
+  sensors_get_value;
+  sensors_init;
+  sensors_parse_chip_name;
+  sensors_set_value;
+  sensors_snprintf_chip_name;
+  sensors_strerror;
+  sensors_parse_error;
+  sensors_parse_error_wfn;
+  sensors_fatal_error;
+local: *;
+ };
Index: /lm-sensors/tags/V3-3-0/lib/data.h
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/data.h	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/data.h	(revision 5844)
@@ -0,0 +1,184 @@
+/*
+    data.h - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    Copyright (C) 2007, 2009  Jean Delvare <khali@linux-fr.org>
+
+    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 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_SENSORS_DATA_H
+#define LIB_SENSORS_DATA_H
+
+#include "sensors.h"
+#include "general.h"
+
+/* This header file contains all kinds of data structures which are used
+   for the representation of the config file data and the sensors
+   data. */
+
+/* Kinds of expression operators recognized */
+typedef enum sensors_operation {
+	sensors_add, sensors_sub, sensors_multiply, sensors_divide,
+	sensors_negate, sensors_exp, sensors_log,
+} sensors_operation;
+
+/* An expression can have several forms */
+typedef enum sensors_expr_kind {
+	sensors_kind_val, sensors_kind_source, sensors_kind_var,
+	sensors_kind_sub
+} sensors_expr_kind;
+
+/* An expression. It is either a floating point value, a variable name,
+   an operation on subexpressions, or the special value 'sub' } */
+struct sensors_expr;
+
+typedef struct sensors_subexpr {
+	sensors_operation op;
+	struct sensors_expr *sub1;
+	struct sensors_expr *sub2;
+} sensors_subexpr;
+
+typedef struct sensors_expr {
+	sensors_expr_kind kind;
+	union {
+		double val;
+		char *var;
+		sensors_subexpr subexpr;
+	} data;
+} sensors_expr;
+
+/* Config file line reference */
+typedef struct sensors_config_line {
+	const char *filename;
+	int lineno;
+} sensors_config_line;
+
+/* Config file label declaration: a feature name, combined with the label
+   value */
+typedef struct sensors_label {
+	char *name;
+	char *value;
+	sensors_config_line line;
+} sensors_label;
+
+/* Config file set declaration: a subfeature name, combined with an
+   expression */
+typedef struct sensors_set {
+	char *name;
+	sensors_expr *value;
+	sensors_config_line line;
+} sensors_set;
+
+/* Config file compute declaration: a feature name, combined with two
+   expressions */
+typedef struct sensors_compute {
+	char *name;
+	sensors_expr *from_proc;
+	sensors_expr *to_proc;
+	sensors_config_line line;
+} sensors_compute;
+
+/* Config file ignore declaration: a feature name */
+typedef struct sensors_ignore {
+	char *name;
+	sensors_config_line line;
+} sensors_ignore;
+
+/* A list of chip names, used to represent a config file chips declaration */
+typedef struct sensors_chip_name_list {
+	sensors_chip_name *fits;
+	int fits_count;
+	int fits_max;
+} sensors_chip_name_list;
+
+/* A config file chip block */
+typedef struct sensors_chip {
+	sensors_chip_name_list chips;
+	sensors_label *labels;
+	int labels_count;
+	int labels_max;
+	sensors_set *sets;
+	int sets_count;
+	int sets_max;
+	sensors_compute *computes;
+	int computes_count;
+	int computes_max;
+	sensors_ignore *ignores;
+	int ignores_count;
+	int ignores_max;
+	sensors_config_line line;
+} sensors_chip;
+
+/* Config file bus declaration: the bus type and number, combined with adapter
+   name */
+typedef struct sensors_bus {
+	char *adapter;
+	sensors_bus_id bus;
+	sensors_config_line line;
+} sensors_bus;
+
+/* Internal data about all features and subfeatures of a chip */
+typedef struct sensors_chip_features {
+	struct sensors_chip_name chip;
+	struct sensors_feature *feature;
+	struct sensors_subfeature *subfeature;
+	int feature_count;
+	int subfeature_count;
+} sensors_chip_features;
+
+extern char **sensors_config_files;
+extern int sensors_config_files_count;
+extern int sensors_config_files_max;
+
+#define sensors_add_config_files(el) sensors_add_array_el( \
+	(el), &sensors_config_files, &sensors_config_files_count, \
+	&sensors_config_files_max, sizeof(char *))
+
+extern sensors_chip *sensors_config_chips;
+extern int sensors_config_chips_count;
+extern int sensors_config_chips_subst;
+extern int sensors_config_chips_max;
+
+extern sensors_bus *sensors_config_busses;
+extern int sensors_config_busses_count;
+extern int sensors_config_busses_max;
+
+extern sensors_chip_features *sensors_proc_chips;
+extern int sensors_proc_chips_count;
+extern int sensors_proc_chips_max;
+
+#define sensors_add_proc_chips(el) sensors_add_array_el( \
+	(el), &sensors_proc_chips, &sensors_proc_chips_count,\
+	&sensors_proc_chips_max, sizeof(struct sensors_chip_features))
+
+extern sensors_bus *sensors_proc_bus;
+extern int sensors_proc_bus_count;
+extern int sensors_proc_bus_max;
+
+#define sensors_add_proc_bus(el) sensors_add_array_el( \
+	(el), &sensors_proc_bus, &sensors_proc_bus_count,\
+	&sensors_proc_bus_max, sizeof(struct sensors_bus))
+
+/* Substitute configuration bus numbers with real-world bus numbers
+   in the chips lists */
+int sensors_substitute_busses(void);
+
+
+/* Parse a bus id into its components. Returns 0 on success, a value from
+   error.h on failure. */
+int sensors_parse_bus_id(const char *name, sensors_bus_id *bus);
+
+#endif /* def LIB_SENSORS_DATA_H */
Index: /lm-sensors/tags/V3-3-0/lib/general.h
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/general.h	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/general.h	(revision 5844)
@@ -0,0 +1,39 @@
+/*
+    general.h - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+
+    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 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_SENSORS_GENERAL
+#define LIB_SENSORS_GENERAL
+
+/* These are general purpose functions. They allow you to use variable-
+   length arrays, which are extended automatically. A distinction is
+   made between the current number of elements and the maximum number.
+   You can only add elements at the end. Primitive, but very useful
+   for internal use. */
+void sensors_malloc_array(void *list, int *num_el, int *max_el,
+			  int el_size);
+void sensors_free_array(void *list, int *num_el, int *max_el);
+void sensors_add_array_el(const void *el, void *list, int *num_el,
+			  int *max_el, int el_size);
+void sensors_add_array_els(const void *els, int nr_els, void *list,
+			   int *num_el, int *max_el, int el_size);
+
+#define ARRAY_SIZE(arr)	(int)(sizeof(arr) / sizeof((arr)[0]))
+
+#endif /* LIB_SENSORS_GENERAL */
Index: /lm-sensors/tags/V3-3-0/lib/conf-parse.y
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/conf-parse.y	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/conf-parse.y	(revision 5844)
@@ -0,0 +1,347 @@
+%{
+/*
+    conf-parse.y - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+
+    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 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.
+*/
+
+#define YYERROR_VERBOSE
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "data.h"
+#include "general.h"
+#include "error.h"
+#include "conf.h"
+#include "access.h"
+#include "init.h"
+
+static void sensors_yyerror(const char *err);
+static sensors_expr *malloc_expr(void);
+
+static sensors_chip *current_chip = NULL;
+
+#define bus_add_el(el) sensors_add_array_el(el,\
+                                      &sensors_config_busses,\
+                                      &sensors_config_busses_count,\
+                                      &sensors_config_busses_max,\
+                                      sizeof(sensors_bus))
+#define label_add_el(el) sensors_add_array_el(el,\
+                                        &current_chip->labels,\
+                                        &current_chip->labels_count,\
+                                        &current_chip->labels_max,\
+                                        sizeof(sensors_label));
+#define set_add_el(el) sensors_add_array_el(el,\
+                                      &current_chip->sets,\
+                                      &current_chip->sets_count,\
+                                      &current_chip->sets_max,\
+                                      sizeof(sensors_set));
+#define compute_add_el(el) sensors_add_array_el(el,\
+                                          &current_chip->computes,\
+                                          &current_chip->computes_count,\
+                                          &current_chip->computes_max,\
+                                          sizeof(sensors_compute));
+#define ignore_add_el(el) sensors_add_array_el(el,\
+                                          &current_chip->ignores,\
+                                          &current_chip->ignores_count,\
+                                          &current_chip->ignores_max,\
+                                          sizeof(sensors_ignore));
+#define chip_add_el(el) sensors_add_array_el(el,\
+                                       &sensors_config_chips,\
+                                       &sensors_config_chips_count,\
+                                       &sensors_config_chips_max,\
+                                       sizeof(sensors_chip));
+
+#define fits_add_el(el,list) sensors_add_array_el(el,\
+                                                  &(list).fits,\
+                                                  &(list).fits_count,\
+                                                  &(list).fits_max, \
+		                                  sizeof(sensors_chip_name));
+
+%}
+
+%union {
+  double value;
+  char *name;
+  void *nothing;
+  sensors_chip_name_list chips;
+  sensors_expr *expr;
+  sensors_bus_id bus;
+  sensors_chip_name chip;
+  sensors_config_line line;
+}  
+
+%left <nothing> '-' '+'
+%left <nothing> '*' '/'
+%left <nothing> NEG
+%right <nothing> '^' '`'
+
+%token <nothing> ','
+%token <nothing> EOL
+%token <line> BUS
+%token <line> LABEL
+%token <line> SET
+%token <line> CHIP
+%token <line> COMPUTE
+%token <line> IGNORE
+%token <value> FLOAT
+%token <name> NAME
+%token <nothing> ERROR
+
+%type <chips> chip_name_list
+%type <expr> expression
+%type <bus> bus_id
+%type <name> adapter_name
+%type <name> function_name
+%type <name> string
+%type <chip> chip_name
+
+%start input
+
+%%
+
+input:	  /* empty */
+	| input line
+;
+
+line:	  bus_statement EOL
+	| label_statement EOL
+	| set_statement EOL
+	| chip_statement EOL
+	| compute_statement EOL
+	| ignore_statement EOL
+	| error	EOL
+;
+
+bus_statement:	  BUS bus_id adapter_name
+		  { sensors_bus new_el;
+		    new_el.line = $1;
+		    new_el.bus = $2;
+                    new_el.adapter = $3;
+		    bus_add_el(&new_el);
+		  }
+;
+
+label_statement:	  LABEL function_name string
+			  { sensors_label new_el;
+			    if (!current_chip) {
+			      sensors_yyerror("Label statement before first chip statement");
+			      free($2);
+			      free($3);
+			      YYERROR;
+			    }
+			    new_el.line = $1;
+			    new_el.name = $2;
+			    new_el.value = $3;
+			    label_add_el(&new_el);
+			  }
+;
+
+set_statement:	  SET function_name expression
+		  { sensors_set new_el;
+		    if (!current_chip) {
+		      sensors_yyerror("Set statement before first chip statement");
+		      free($2);
+		      sensors_free_expr($3);
+		      YYERROR;
+		    }
+		    new_el.line = $1;
+		    new_el.name = $2;
+		    new_el.value = $3;
+		    set_add_el(&new_el);
+		  }
+;
+
+compute_statement:	  COMPUTE function_name expression ',' expression
+			  { sensors_compute new_el;
+			    if (!current_chip) {
+			      sensors_yyerror("Compute statement before first chip statement");
+			      free($2);
+			      sensors_free_expr($3);
+			      sensors_free_expr($5);
+			      YYERROR;
+			    }
+			    new_el.line = $1;
+			    new_el.name = $2;
+			    new_el.from_proc = $3;
+			    new_el.to_proc = $5;
+			    compute_add_el(&new_el);
+			  }
+;
+
+ignore_statement:	IGNORE function_name
+			{ sensors_ignore new_el;
+			  if (!current_chip) {
+			    sensors_yyerror("Ignore statement before first chip statement");
+			    free($2);
+			    YYERROR;
+			  }
+			  new_el.line = $1;
+			  new_el.name = $2;
+			  ignore_add_el(&new_el);
+			}
+;
+
+chip_statement:	  CHIP chip_name_list
+		  { sensors_chip new_el;
+		    new_el.line = $1;
+		    new_el.labels = NULL;
+		    new_el.sets = NULL;
+		    new_el.computes = NULL;
+		    new_el.ignores = NULL;
+		    new_el.labels_count = new_el.labels_max = 0;
+		    new_el.sets_count = new_el.sets_max = 0;
+		    new_el.computes_count = new_el.computes_max = 0;
+		    new_el.ignores_count = new_el.ignores_max = 0;
+		    new_el.chips = $2;
+		    chip_add_el(&new_el);
+		    current_chip = sensors_config_chips + 
+		                   sensors_config_chips_count - 1;
+		  }
+;
+
+chip_name_list:	  chip_name
+		  { 
+		    $$.fits = NULL;
+		    $$.fits_count = $$.fits_max = 0;
+		    fits_add_el(&$1,$$);
+		  }
+		| chip_name_list chip_name
+		  { $$ = $1;
+		    fits_add_el(&$2,$$);
+		  }
+;
+	
+expression:	  FLOAT	
+		  { $$ = malloc_expr(); 
+		    $$->data.val = $1; 
+		    $$->kind = sensors_kind_val;
+		  }
+		| NAME
+		  { $$ = malloc_expr(); 
+		    $$->data.var = $1;
+		    $$->kind = sensors_kind_var;
+		  }
+		| '@'
+		  { $$ = malloc_expr();
+		    $$->kind = sensors_kind_source;
+		  }
+		| expression '+' expression
+		  { $$ = malloc_expr(); 
+		    $$->kind = sensors_kind_sub;
+		    $$->data.subexpr.op = sensors_add;
+		    $$->data.subexpr.sub1 = $1;
+		    $$->data.subexpr.sub2 = $3;
+		  }
+		| expression '-' expression
+		  { $$ = malloc_expr(); 
+		    $$->kind = sensors_kind_sub;
+		    $$->data.subexpr.op = sensors_sub;
+		    $$->data.subexpr.sub1 = $1;
+		    $$->data.subexpr.sub2 = $3;
+		  }
+		| expression '*' expression
+		  { $$ = malloc_expr(); 
+		    $$->kind = sensors_kind_sub;
+		    $$->data.subexpr.op = sensors_multiply;
+		    $$->data.subexpr.sub1 = $1;
+		    $$->data.subexpr.sub2 = $3;
+		  }
+		| expression '/' expression
+		  { $$ = malloc_expr(); 
+		    $$->kind = sensors_kind_sub;
+		    $$->data.subexpr.op = sensors_divide;
+		    $$->data.subexpr.sub1 = $1;
+		    $$->data.subexpr.sub2 = $3;
+		  }
+		| '-' expression  %prec NEG
+		  { $$ = malloc_expr(); 
+		    $$->kind = sensors_kind_sub;
+		    $$->data.subexpr.op = sensors_negate;
+		    $$->data.subexpr.sub1 = $2;
+		    $$->data.subexpr.sub2 = NULL;
+		  }
+		| '(' expression ')'
+		  { $$ = $2; }
+		| '^' expression
+		  { $$ = malloc_expr(); 
+		    $$->kind = sensors_kind_sub;
+		    $$->data.subexpr.op = sensors_exp;
+		    $$->data.subexpr.sub1 = $2;
+		    $$->data.subexpr.sub2 = NULL;
+		  }
+		| '`' expression
+		  { $$ = malloc_expr(); 
+		    $$->kind = sensors_kind_sub;
+		    $$->data.subexpr.op = sensors_log;
+		    $$->data.subexpr.sub1 = $2;
+		    $$->data.subexpr.sub2 = NULL;
+		  }
+;
+
+bus_id:		  NAME
+		  { int res = sensors_parse_bus_id($1,&$$);
+		    free($1);
+		    if (res) {
+                      sensors_yyerror("Parse error in bus id");
+		      YYERROR;
+                    }
+		  }
+;
+
+adapter_name:	  NAME
+		  { $$ = $1; }
+;
+
+function_name:	  NAME
+		  { $$ = $1; }
+;
+
+string:	  NAME
+	  { $$ = $1; }
+;
+
+chip_name:	  NAME
+		  { int res = sensors_parse_chip_name($1,&$$); 
+		    free($1);
+		    if (res) {
+		      sensors_yyerror("Parse error in chip name");
+		      YYERROR;
+		    }
+		  }
+;
+
+%%
+
+void sensors_yyerror(const char *err)
+{
+  if (sensors_lex_error[0]) {
+    sensors_parse_error_wfn(sensors_lex_error, sensors_yyfilename, sensors_yylineno);
+    sensors_lex_error[0] = '\0';
+  } else
+    sensors_parse_error_wfn(err, sensors_yyfilename, sensors_yylineno);
+}
+
+sensors_expr *malloc_expr(void)
+{
+  sensors_expr *res = malloc(sizeof(sensors_expr));
+  if (! res)
+    sensors_fatal_error(__func__, "Allocating a new expression");
+  return res;
+}
Index: /lm-sensors/tags/V3-3-0/lib/conf-lex.l
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/conf-lex.l	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/conf-lex.l	(revision 5844)
@@ -0,0 +1,372 @@
+%{
+/*
+    conf-lex.l - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> 
+
+    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 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 <stdlib.h>
+#include <string.h>
+
+#include "general.h"
+#include "data.h"
+#include "conf-parse.h"
+#include "error.h"
+#include "scanner.h"
+
+static int buffer_count;
+static int buffer_max;
+static char *buffer;
+
+char sensors_lex_error[100];
+
+const char *sensors_yyfilename;
+int sensors_yylineno;
+
+#define buffer_malloc() sensors_malloc_array(&buffer,&buffer_count,\
+                                             &buffer_max,1)
+#define buffer_free() sensors_free_array(&buffer,&buffer_count,\
+                                         &buffer_max)
+#define buffer_add_char(c) sensors_add_array_el(c,&buffer,\
+                                                &buffer_count,\
+                                                &buffer_max,1)
+#define buffer_add_string(s) sensors_add_array_els(s,strlen(s),\
+                                                   &buffer, \
+                                                   &buffer_count,&buffer_max,1)
+
+%}
+
+ /* Scanner for configuration files */
+
+%option nodefault
+%option noyywrap
+%option nounput
+
+ /* All states are exclusive */
+
+%x MIDDLE
+%x STRING
+%x ERR
+
+ /* Any whitespace-like character */
+
+BLANK		[ \f\r\t\v]
+
+IDCHAR		[[:alnum:]_]
+
+ /* Note: `10', `10.4' and `.4' are valid, `10.' is not */
+
+FLOAT   [[:digit:]]*\.?[[:digit:]]+
+
+ /* Only positive whole numbers are recognized here */
+
+NUM	0|([1-9][[:digit:]]*)
+
+
+%%
+
+ /*
+  * STATE: INITIAL
+  */
+
+<INITIAL>{
+
+<<EOF>>		{ /* EOF from this state terminates */
+		  return 0;
+		}
+
+{BLANK}+	; /* eat as many blanks as possible at once */
+
+{BLANK}*\n	{ /* eat a bare newline (possibly preceded by blanks) */
+		  sensors_yylineno++;
+		}
+
+ /* comments */
+
+#.*		; /* eat the rest of the line after comment char */
+
+#.*\n		{ /* eat the rest of the line after comment char */
+		  sensors_yylineno++;
+		}
+
+ /*
+  * Keywords must be followed by whitespace - eat that too.
+  * If there isn't trailing whitespace, we still need to
+  * accept it as lexically correct (even though the parser
+  * will reject it anyway.)
+  */
+
+label{BLANK}*	{
+		  sensors_yylval.line.filename = sensors_yyfilename;
+		  sensors_yylval.line.lineno = sensors_yylineno;
+		  BEGIN(MIDDLE);
+		  return LABEL;
+		}
+
+set{BLANK}*	{
+		  sensors_yylval.line.filename = sensors_yyfilename;
+		  sensors_yylval.line.lineno = sensors_yylineno;
+		  BEGIN(MIDDLE);
+		  return SET;
+		}
+
+compute{BLANK}*	{
+		  sensors_yylval.line.filename = sensors_yyfilename;
+		  sensors_yylval.line.lineno = sensors_yylineno;
+		  BEGIN(MIDDLE);
+		  return COMPUTE;
+		}
+
+bus{BLANK}*	{
+		  sensors_yylval.line.filename = sensors_yyfilename;
+		  sensors_yylval.line.lineno = sensors_yylineno;
+		  BEGIN(MIDDLE);
+		  return BUS;
+		}
+
+chip{BLANK}*	{
+		  sensors_yylval.line.filename = sensors_yyfilename;
+		  sensors_yylval.line.lineno = sensors_yylineno;
+		  BEGIN(MIDDLE);
+		  return CHIP;
+		}
+
+ignore{BLANK}*	{
+		  sensors_yylval.line.filename = sensors_yyfilename;
+		  sensors_yylval.line.lineno = sensors_yylineno;
+		  BEGIN(MIDDLE);
+		  return IGNORE;
+		}
+
+ /* Anything else at the beginning of a line is an error */
+
+[a-z]+		|
+.		{
+		  BEGIN(ERR);
+		  strcpy(sensors_lex_error,"Invalid keyword");
+		  return ERROR;
+		}
+}
+
+ /*
+  * STATE: ERROR
+  */
+
+<ERR>{
+
+.*		; /* eat whatever is left on this line */
+
+\n		{
+		  BEGIN(INITIAL);
+		  sensors_yylineno++;
+		  return EOL;
+		}
+}
+
+ /*
+  * STATE: MIDDLE
+  */
+
+<MIDDLE>{
+
+{BLANK}+	; /* eat as many blanks as possible at once */
+
+\n		{ /* newline here sends EOL token to parser */
+		  BEGIN(INITIAL);
+		  sensors_yylineno++;
+		  return EOL;
+		}
+
+<<EOF>>		{ /* EOF here sends EOL token to parser also */
+		  BEGIN(INITIAL);
+		  return EOL;
+		}
+
+\\{BLANK}*\n	{ /* eat an escaped newline with no state change */
+		  sensors_yylineno++;
+		}
+
+ /* comments */
+
+#.*		; /* eat the rest of the line after comment char */
+
+#.*\n		{ /* eat the rest of the line after comment char */
+		  BEGIN(INITIAL);
+		  sensors_yylineno++;
+		  return EOL;
+		}
+
+ /* A number */
+
+{FLOAT}		{
+		  sensors_yylval.value = atof(sensors_yytext);
+		  return FLOAT;
+		}
+
+ /* Some operators */
+
+"+"		return '+';
+"-"		return '-';
+"*"		return '*';
+"/"		return '/';
+"("		return '(';
+")"		return ')';
+","		return ',';
+"@"		return '@';
+"^"		return '^';
+"`"		return '`';
+
+ /* Quoted string */
+
+\"		{
+		  buffer_malloc();
+		  BEGIN(STRING);
+		}
+
+ /* A normal, unquoted identifier */
+
+{IDCHAR}+	{
+		  sensors_yylval.name = strdup(sensors_yytext);
+		  if (! sensors_yylval.name)
+		    sensors_fatal_error("conf-lex.l",
+                                        "Allocating a new string");
+		  
+		  return NAME;
+		}
+
+ /* anything else is bogus */
+
+.		|
+[[:digit:]]*\.	|
+\\{BLANK}*	{
+		  BEGIN(ERR);
+		  return ERROR;
+		}
+}
+
+ /*
+  * STATE: STRING
+  */
+
+<STRING>{
+
+ /* Oops, newline or EOF while in a string is not good */
+
+\n		|
+\\\n		{
+		  buffer_add_char("\0");
+		  strcpy(sensors_lex_error,
+			"No matching double quote.");
+		  buffer_free();
+		  yyless(0);
+		  BEGIN(ERR);
+		  return ERROR;
+		}
+
+<<EOF>>		{
+		  strcpy(sensors_lex_error,
+			"Reached end-of-file without a matching double quote.");
+		  buffer_free();
+		  BEGIN(MIDDLE);
+		  return ERROR;
+		}
+
+ /* At the end */
+
+\"\"		{
+		  buffer_add_char("\0");
+		  strcpy(sensors_lex_error,
+			"Quoted strings must be separated by whitespace.");
+		  buffer_free();
+		  BEGIN(ERR);
+		  return ERROR;
+		}
+		
+\"		{
+		  buffer_add_char("\0");
+		  sensors_yylval.name = strdup(buffer);
+		  if (! sensors_yylval.name)
+		    sensors_fatal_error("conf-lex.l",
+                                        "Allocating a new string");
+		  buffer_free();
+		  BEGIN(MIDDLE);
+		  return NAME;
+		}
+
+\\a		buffer_add_char("\a");
+\\b		buffer_add_char("\b");
+\\f		buffer_add_char("\f");
+\\n		buffer_add_char("\n");
+\\r		buffer_add_char("\r");
+\\t		buffer_add_char("\t");
+\\v		buffer_add_char("\v");
+
+ /* Other escapes: just copy the character behind the slash */
+
+\\.		{
+		  buffer_add_char(&sensors_yytext[1]);
+		}
+
+ /* Anything else (including a bare '\' which may be followed by EOF) */
+
+\\		|
+[^\\\n\"]+	{
+		  buffer_add_string(sensors_yytext);
+		}
+}
+
+%%
+
+/*
+	Do the buffer handling manually.  This allows us to scan as many
+	config files as we need to, while cleaning up properly after each
+	one.  The "BEGIN(0)" line ensures that we start in the default state,
+	even if e.g. the previous config file was syntactically broken.
+
+	Returns 0 if successful, !0 otherwise.
+*/
+
+static YY_BUFFER_STATE scan_buf = (YY_BUFFER_STATE)0;
+
+int sensors_scanner_init(FILE *input, const char *filename)
+{
+	BEGIN(0);
+	if (!(scan_buf = sensors_yy_create_buffer(input, YY_BUF_SIZE)))
+		return -1;
+
+	sensors_yy_switch_to_buffer(scan_buf);
+	sensors_yyfilename = filename;
+	sensors_yylineno = 1;
+	return 0;
+}
+
+void sensors_scanner_exit(void)
+{
+	sensors_yy_delete_buffer(scan_buf);
+	scan_buf = (YY_BUFFER_STATE)0;
+
+/* As of flex 2.5.9, yylex_destroy() must be called when done with the
+   scaller, otherwise we'll leak memory. */
+#if defined(YY_FLEX_MAJOR_VERSION) && defined(YY_FLEX_MINOR_VERSION) && defined(YY_FLEX_SUBMINOR_VERSION)
+#if YY_FLEX_MAJOR_VERSION > 2 || \
+    (YY_FLEX_MAJOR_VERSION == 2 && (YY_FLEX_MINOR_VERSION > 5 || \
+				    (YY_FLEX_MINOR_VERSION == 5 && YY_FLEX_SUBMINOR_VERSION >= 9)))
+	sensors_yylex_destroy();
+#endif
+#endif
+}
+
Index: /lm-sensors/tags/V3-3-0/lib/error.c
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/error.c	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/error.c	(revision 5844)
@@ -0,0 +1,92 @@
+/*
+    error.c - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    Copyright (C) 2007-2009   Jean Delvare <khali@linux-fr.org>
+
+    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 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 <stdlib.h>
+#include <stdio.h>
+#include "error.h"
+#include "general.h"
+
+static void sensors_default_parse_error(const char *err, int lineno);
+static void sensors_default_parse_error_wfn(const char *err,
+					    const char *filename, int lineno);
+static void sensors_default_fatal_error(const char *proc, const char *err);
+
+void (*sensors_parse_error) (const char *err, int lineno) =
+						sensors_default_parse_error;
+void (*sensors_parse_error_wfn) (const char *err, const char *filename,
+				 int lineno) = sensors_default_parse_error_wfn;
+void (*sensors_fatal_error) (const char *proc, const char *err) =
+						sensors_default_fatal_error;
+
+static const char *errorlist[] = {
+	/* Invalid error code    */ "Unknown error",
+	/* SENSORS_ERR_WILDCARDS */ "Wildcard found in chip name",
+	/* SENSORS_ERR_NO_ENTRY  */ "No such subfeature known",
+	/* SENSORS_ERR_ACCESS_R  */ "Can't read",
+	/* SENSORS_ERR_KERNEL    */ "Kernel interface error",
+	/* SENSORS_ERR_DIV_ZERO  */ "Divide by zero",
+	/* SENSORS_ERR_CHIP_NAME */ "Can't parse chip name",
+	/* SENSORS_ERR_BUS_NAME  */ "Can't parse bus name",
+	/* SENSORS_ERR_PARSE     */ "General parse error",
+	/* SENSORS_ERR_ACCESS_W  */ "Can't write",
+	/* SENSORS_ERR_IO        */ "I/O error",
+	/* SENSORS_ERR_RECURSION */ "Evaluation recurses too deep",
+};
+
+const char *sensors_strerror(int errnum)
+{
+	if (errnum < 0)
+		errnum = -errnum;
+	if (errnum >= ARRAY_SIZE(errorlist))
+		errnum = 0;
+	return errorlist[errnum];
+}
+
+void sensors_default_parse_error(const char *err, int lineno)
+{
+	if (lineno)
+		fprintf(stderr, "Error: Line %d: %s\n", lineno, err);
+	else
+		fprintf(stderr, "Error: %s\n", err);
+}
+
+void sensors_default_parse_error_wfn(const char *err,
+				     const char *filename, int lineno)
+{
+	/* If application provided a custom parse error reporting function
+	   but not the variant with the filename, fall back to the original
+	   variant without the filename, for backwards compatibility. */
+	if (sensors_parse_error != sensors_default_parse_error ||
+	    !filename)
+		return sensors_parse_error(err, lineno);
+
+	if (lineno)
+		fprintf(stderr, "Error: File %s, line %d: %s\n", filename,
+			lineno, err);
+	else
+		fprintf(stderr, "Error: File %s: %s\n", filename, err);
+}
+
+void sensors_default_fatal_error(const char *proc, const char *err)
+{
+	fprintf(stderr, "Fatal error in `%s': %s\n", proc, err);
+	exit(1);
+}
Index: /lm-sensors/tags/V3-3-0/lib/conf.h
===================================================================
--- /lm-sensors/tags/V3-3-0/lib/conf.h	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/lib/conf.h	(revision 5844)
@@ -0,0 +1,34 @@
+/*
+    conf.h - Part of libsensors, a Linux library for reading sensor data.
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+
+    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 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_SENSORS_CONF_H
+#define LIB_SENSORS_CONF_H
+
+/* This is defined in conf-lex.l */
+int sensors_yylex(void);
+extern char sensors_lex_error[];
+extern const char *sensors_yyfilename;
+extern int sensors_yylineno;
+extern FILE *sensors_yyin;
+
+/* This is defined in conf-parse.y */
+int sensors_yyparse(void);
+
+#endif /* LIB_SENSORS_CONF_H */
Index: /lm-sensors/tags/V3-3-0/prog/hotplug/README.p4b
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/hotplug/README.p4b	(revision 4402)
+++ /lm-sensors/tags/V3-3-0/prog/hotplug/README.p4b	(revision 4402)
@@ -0,0 +1,39 @@
+What is it?
+-----------
+
+It unhides the SMBus of Intel ICH southbridges hidden on Asus P4B series
+and some other Asus and non-Asus systems. It is implemented as a shell
+script for the 2.6 kernel: unhide_ICH_SMBus.
+
+It works with the following chips:
+ * ICH2   (82801BA)
+ * ICH2-M (82801BAM)
+ * ICH3   (82801CA)
+ * ICH3-M (82801CAM)
+ * ICH4   (82801DB)
+ * ICH4-M (82801DBM)
+ * ICH5   (82801EB)
+
+ASUS switches off the SMBus PCI Device in the i801 ICH chip. I spoke twice
+with the German support and learned that: "We do not want the users to be
+irritated by just another PCI Device in the Win98 device manager."
+Really funny :-).
+
+For 2.6 kernels, activation is normally handled by the code in
+drivers/pci/quirks.c in the kernel source. If you have the 2.6 kernel and
+the quirks code does not activate your SMBus, you may:
+
+ * first of all, read the paragraph "Hidden ICH SMBus" in
+   Documentation/i2c/busses/i2c-i801 in the kernel source.
+
+ * check if you have kernel version from 2.6.16 to 2.6.19
+   If so, you need to disable ACPI sleep support and recompile kernel. 
+   If you don't use suspend try below instead of recompilation.
+
+ * run as root the ./unhide_ICH_SMBus script. It will unhide the bus 
+   and load the i2c-i801 driver. Beware that you should then no longer
+   use system suspend as bad things might happen on resume.
+
+ * if above script works and you have latest kernel, you may 
+   try to fix the quirks.c, recompile and test your new kernel,
+   and then submit a patch to us. Contact us if you need assistance.
Index: /lm-sensors/tags/V3-3-0/prog/hotplug/unhide_ICH_SMBus
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/hotplug/unhide_ICH_SMBus	(revision 5082)
+++ /lm-sensors/tags/V3-3-0/prog/hotplug/unhide_ICH_SMBus	(revision 5082)
@@ -0,0 +1,72 @@
+#!/bin/bash
+# (C) Rudolf Marek <r.marek@assembler.cz>,
+#     Jean Delvare <khali@linux-fr.org>
+#
+#    Thanks Jean Delvare and Oliver Dreier <oliver@dinux.de> for testing.
+#
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License version 2
+#    as published by the Free Software Foundation
+#
+# Supports the following Intel ICH chipsets:
+#              PCI ID
+#   ICH       8086:2410
+#   ICH2      8086:2440
+#   ICH2-M    8086:244C
+#   ICH3      8086:2480
+#   ICH3-M    8086:248C
+#   ICH4      8086:24C0
+#   ICH4-M    8086:24CC
+#   ICH5      8086:24D0
+
+pcibus="/sys/bus/pci/slots"
+device="00:1f"
+
+if [ "$UID" -ne 0 ] ; then
+	echo "You need to be root to run this script!"
+	exit 252
+fi
+
+smbus=`lspci -n -s $device.3 | grep -i '0c05: *8086'`
+if [ -n "$smbus" ] ; then
+	echo "ICH SMBus is already there!"
+	lspci -s $device.3
+	exit
+fi
+
+intel=`lspci -n -s $device.0 | grep -i '8086:24[148CD][0C]'`
+if [ -z "$intel" ] ; then
+	echo "Not for your chipset - Intel (ICH) only"
+	echo "Supported: ICH, ICH2, ICH2-M, ICH3, ICH3-M, ICH4, ICH4-M, ICH5"
+	exit 255;
+fi
+modprobe fakephp &> /dev/null
+
+if [ ! -d "$pcibus" ] ; then
+	echo "You need the fake PCI hotplug driver! (fakephp.ko and 2.6 kernel)"
+	exit 255;
+fi
+
+echo "Enabling SMBus PCI device ..."
+
+newval=$( printf '%x' $((0x$(setpci -s $device.0 f2.w) & 0xfff7))) 
+setpci -s $device.0 f2.w=$newval
+
+echo "Rescanning the bus ..."
+echo 1 > $pcibus/0000:$device.0/power 2>/dev/null
+if [ ! -d "$pcibus/0000:$device.3" ] ; then
+	echo "Failed to enable the SMBUS"
+	exit 253;
+fi
+
+if [ ! -d "/sys/bus/pci/drivers/i801_smbus" ] ; then
+	echo "Loading i2c-i801 ..."
+	modprobe i2c-i801
+	if [ $? -ne 0 ] ; then
+		exit 251
+	fi
+fi
+
+lspci -s $device.3
+echo "Done!"
+echo "Remember: system suspend/resume is no longer safe to use."
Index: /lm-sensors/tags/V3-3-0/prog/daemon/healthd.sh
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/daemon/healthd.sh	(revision 229)
+++ /lm-sensors/tags/V3-3-0/prog/daemon/healthd.sh	(revision 229)
@@ -0,0 +1,42 @@
+#!/bin/bash
+#
+# healthd -- 	This is a simple daemon which can be used to alert you in the
+#		event of a hardware health monitoring alarm by sending an 
+#		email to the value of ADMIN_EMAIL (defined below).
+#
+# To Use  --	Simply start the daemon from a shell (may be backgrounded)
+#
+# Other details -- Checks status every 15 seconds.  Sends warning emails every
+#		   ten minutes during alarm until the alarm is cleared.
+#		   It won't start up if there is a pending alarm on startup.
+#		   Very low loading on the machine (sleeps almost all the time).
+#		   This is just an example.  It works, but hopefully we can
+#		   get something better written. :')
+#
+# Requirements -- grep, mail, sensors, bash
+#		  (You may need to alter the PATH, but probably not.)
+#
+# Written & Copyrighten by Philip Edelbrock, 1999.
+#
+# Version: 1.0.0
+#
+
+PATH="/bin:/usr/bin:/usr/local/bin:${PATH}"
+
+ADMIN_EMAIL="root@localhost"
+
+if [ -n "`sensors | grep ALARM`" ]
+then
+        echo "Pending Alarms on start up!  Exiting!"
+        exit
+fi
+
+while true
+do
+ sleep 15
+ if [ -n "`sensors | grep ALARM`" ]
+ then
+        sensors | mail -s "**** Hardware Health Warning ****"  $ADMIN_EMAIL
+        sleep 600
+ fi
+done
Index: /lm-sensors/tags/V3-3-0/prog/init/sensord.init
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/init/sensord.init	(revision 2777)
+++ /lm-sensors/tags/V3-3-0/prog/init/sensord.init	(revision 2777)
@@ -0,0 +1,157 @@
+#!@BASH@
+#
+# @INITRDDIR@/sensord
+#
+# sensord       This shell script takes care of starting and stopping
+#               sensord, the lm_sensors hardware health monitoring daemon.
+#
+# Here is the sensors service for SysV init, based on lm_sensors-2.5.5-sensors
+# from Mandrake lm_sensors source RPM. It is modified according to recommendations
+# for RedHat initscripts. The drivers starting part is taken from alsasound
+# service. To configure this service one should put appropriate "alias i2c-bus-0
+# xxx" and "alias i2c-sensors-chip-0 xxx" in /etc/modules.conf. The rest should be
+# self explaining.
+#
+# You put it into /etc/rc.d/init.d/, you make a symlink (probably using
+# chkconfig, ntsysv, tksysv or serviceconf program) named S95xxx and K05xxx
+# into /etc/rc#.d (where # is the number of runlevel), and sensors service
+# (which starts lm_sensors modules, runs sensors -s and starts sensord)
+# will be started automatically at startup/reboot and stopped at shutdown.
+# One could also start/stop service manually.
+#
+# This service was tested for RedHat 7.2 only.
+# Jakub Narêbski, Poland
+#
+
+# chkconfig: 2345 05 95
+# processname: sensord
+# config: @SYSCONFDIR@/sensors.conf
+# pidfile: /var/run/sensord.pid
+# description: Sensors is a sensors daemon which can be used to alert you \
+#              in the event of a hardware health monitoring alarm.
+
+# Source function library.
+. @INITRDDIR@/functions
+
+# Set default return value to 0 (success)
+RETVAL=0
+# Add @SBINDIR@ (sensord) and @BINDIR@ (sensors) to PATH if necessary
+echo "$PATH" | grep -q @SBINDIR@ || PATH=$PATH:@SBINDIR@
+echo "$PATH" | grep -q  @BINDIR@ || PATH=$PATH:@BINDIR@
+export PATH
+
+# Modules to load from modules.conf (modules configuration)
+i2c_bus_drivers=Žmodprobe -c | \
+  awk Ž/^[[:space:]]*alias[[:space:]]+i2c-bus-[[:digit:]]/ { print $3 }ŽŽ
+i2c_chip_drivers=Žmodprobe -c | \
+  awk Ž/^[[:space:]]*alias[[:space:]]+i2c-sensors-chip-[[:digit:]]/ { print $3
+}ŽŽ
+
+# Configuration of sensord
+interval=1m      # interval between scanning for sensor alarms
+log_interval=30m # interval between logging all sensor readings
+
+# Check that we use kernel for which lm_sensors-drivers was installed
+[ Žuname -rŽ = @MVERSION@ ] || exit 0
+
+# Check that lm_sensors is installed.
+[ -x @SBINDIR@/sensord ] || exit 0
+[ -x  @BINDIR@/sensors ] || exit 0
+
+echo_status()
+{
+        if [ $1 -eq 0 ]; then
+                echo_success
+        else
+                echo_failure
+        fi
+        echo
+}
+
+start()
+{
+        # Start modules
+        echo "Starting I2C bus (adapter) drivers: "
+        for driver in $i2c_bus_drivers; do
+                echo -n "Starting I2C driver: $driver "
+                /sbin/modprobe Žecho $driverŽ
+                echo_status $?
+        done
+        echo "Starting I2C chip (sensors) drivers: "
+        for driver in $i2c_chip_drivers; do
+                echo -n "Starting I2C driver: $driver "
+                /sbin/modprobe $(echo $driver)
+                echo_status $?
+        done
+        # Set Alarm
+        echo -n "Configuring sensors: "
+        sensors -s && sleep 2
+        echo_status $?
+        # Start daemons.
+        echo -n $"Starting sensord: "
+        daemon sensord -i $interval -l $log_interval
+        RETVAL=$?
+
+        [ $RETVAL -eq 0 ] && touch /var/lock/subsys/sensord
+
+        echo
+        return $RETVAL
+}
+
+stop()
+{
+        # Stop daemons.
+        echo -n $"Shutting down sensord: "
+        killproc sensord
+        RETVAL=$?
+
+        echo
+        # Remove modules
+        drivers=Žecho "$i2c_chip_drivers $i2c_bus_drivers" | \
+          tr -s "[:space:]\n" " "Ž
+        echo -n "Removing I2C drivers: $drivers"
+        /sbin/modprobe -r -q $drivers
+        echo_status $?
+
+        echo
+        [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/sensord
+
+        return $RETVAL
+}
+
+reload()
+{
+        # Reread configuration file
+        sensors -s
+
+        return $?
+}
+
+# See how we were called.
+case "$1" in
+  start)
+        start
+        ;;
+  stop)
+        stop
+        ;;
+  status)
+        status sensord
+        ;;
+  restart)
+        stop
+        start
+        ;;
+  reload)
+        reload
+        ;;
+  condrestart)
+        [ -e /var/lock/subsys/sensord ] && restart || :
+        ;;
+  *)
+        echo "Usage: sensord {start|stop|restart|reload|condrestart|status}"
+        exit 1
+        ;;
+esac
+
+exit $?
Index: /lm-sensors/tags/V3-3-0/prog/init/sysconfig-lm_sensors-convert
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/init/sysconfig-lm_sensors-convert	(revision 5755)
+++ /lm-sensors/tags/V3-3-0/prog/init/sysconfig-lm_sensors-convert	(revision 5755)
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+# Convert an lm_sensors service configuration file from the old convention
+# (MODULE_0, MODULE_1...) to the new convention (HWMON_MODULES).
+#
+# Copyright (C) 2009  Jean Delvare <khali@linux-fr.org>
+#
+# Released without a license on purpose. This is public domain code, so
+# that package maintainers can include it in their post-install script.
+
+CONFIG=/etc/sysconfig/lm_sensors
+test -r "$CONFIG" || exit 0
+
+unset ${!MODULE_*} $HWMON_MODULES
+. "$CONFIG"
+test -n "$HWMON_MODULES" && exit 0
+
+for i in ${!MODULE_*} ; do
+	eval module=\$$i
+	if test -z "$HWMON_MODULES" ; then
+		HWMON_MODULES="$module"
+	else
+		HWMON_MODULES="$HWMON_MODULES $module"
+	fi
+done
+test -z "$HWMON_MODULES" && exit 0
+
+echo >> "$CONFIG"
+echo "# New configuration format generated by sysconfig-lm_sensors-convert" >> "$CONFIG"
+echo "HWMON_MODULES=\"$HWMON_MODULES\"" >> "$CONFIG"
Index: /lm-sensors/tags/V3-3-0/prog/init/lm_sensors.init
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/init/lm_sensors.init	(revision 5622)
+++ /lm-sensors/tags/V3-3-0/prog/init/lm_sensors.init	(revision 5622)
@@ -0,0 +1,138 @@
+#!/bin/sh
+#
+# chkconfig: - 26 74
+# description: sensors is used for monitoring motherboard sensor values.
+# config: /etc/sysconfig/lm_sensors
+#
+#    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.
+
+# See also the lm_sensors homepage at:
+#     http://www.lm-sensors.org
+
+# It uses a config file /etc/sysconfig/lm_sensors that contains the modules
+# to be loaded/unloaded. That file is sourced into this one.
+
+# The format of this file is a shell script that simply defines variables:
+# HWMON_MODULES for hardware monitoring driver modules, and optionally
+# BUS_MODULES for any required bus driver module (for example for I2C or SPI).
+
+PSENSORS=/usr/local/bin/sensors
+
+if [ ! -x $PSENSORS ]; then
+	PSENSORS=/usr/bin/sensors
+fi
+
+# Source function library.
+. /etc/init.d/functions
+
+RETVAL=0
+prog="lm_sensors"
+
+# This functions checks if sensor support is compiled into the kernel, if
+# sensors are configured, and loads the config file
+check_sensors() {
+	CONFIG=/etc/sysconfig/lm_sensors
+	if ! [ -r "$CONFIG" ] || ! grep '^HWMON_MODULES' $CONFIG >/dev/null 2>&1; then
+		echo -n "$1 $prog: not configured, run sensors-detect"
+		echo_warning
+		echo
+		exit 6
+	fi
+
+	# Load config file
+	. "$CONFIG"
+}
+
+start() {
+	check_sensors "Starting"
+
+	echo -n "Starting $prog: loading module "
+
+	for module in $BUS_MODULES $HWMON_MODULES ; do
+		echo -n "${module} "
+		/sbin/modprobe $module >/dev/null 2>&1
+	done
+	$PSENSORS -s
+
+	RETVAL=$?
+	if [ $RETVAL -eq 0 ] && touch /var/lock/subsys/lm_sensors ; then
+		echo_success
+		echo
+	else
+		echo_failure
+		echo
+	fi
+}
+
+stop() {
+	check_sensors "Stopping"
+
+	echo -n "Stopping $prog: "
+
+	for module in $HWMON_MODULES $BUS_MODULES ; do
+		/sbin/modprobe -r $module >/dev/null 2>&1
+	done
+
+	RETVAL=$?
+	if [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/lm_sensors ; then
+		echo_success
+		echo
+	else
+		echo_failure
+		echo
+	fi
+}
+
+dostatus() {
+	$PSENSORS
+	RETVAL=$?
+	if [ $RETVAL -ne 0 ]; then
+		RETVAL=3
+	fi
+}
+
+restart() {
+	stop
+	start
+}
+
+condrestart() {
+	[ -e /var/lock/subsys/lm_sensors ] && restart || :
+}
+
+# See how we were called.
+case "$1" in
+  start)
+	start
+	;;
+  stop)
+	stop
+	;;
+  status)
+	dostatus
+	;;
+  restart|reload)
+	restart
+	;;
+  condrestart)
+	condrestart
+	;;
+  *)
+	echo "Usage: $0 {start|stop|status|restart|reload|condrestart}"
+	exit 3
+esac
+
+exit $RETVAL
Index: /lm-sensors/tags/V3-3-0/prog/init/fancontrol.init
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/init/fancontrol.init	(revision 5180)
+++ /lm-sensors/tags/V3-3-0/prog/init/fancontrol.init	(revision 5180)
@@ -0,0 +1,100 @@
+#!/bin/sh
+#
+# $Id$
+#
+# fancontrol
+#
+# chkconfig: 2345 90 01
+# description: start/stop fancontrol
+# pidfile: /var/run/fancontrol.pid
+#
+
+PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
+export PATH
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+FANCONTROL="/usr/local/sbin/fancontrol"
+PIDFILE="/var/run/fancontrol.pid"
+
+RETVAL=0
+
+start()
+{
+   echo -n "Starting fancontrol daemon"
+   daemon $FANCONTROL -d
+   RETVAL=$?
+   [ $RETVAL -eq 0 ] && touch /var/lock/subsys/fancontrol
+   echo
+}
+
+stop()
+{
+   echo -n "Stopping fancontrol daemon"
+   killproc $FANCONTROL
+   RETVAL=$?
+   rm -f /var/lock/subsys/fancontrol
+   echo
+}
+
+status()
+{
+   if [ -f $PIDFILE ] ; then
+     pid=`cat $PIDFILE`
+   else
+     echo -n "$FANCONTROL does not appear to be running"
+     echo
+     exit
+   fi
+
+   if [ -n "$pid" ] ; then
+     ps -p $pid > /dev/null
+     if [ $? ] ; then
+       echo -n "$FANCONTROL appears to be running"
+     else
+       echo -n "$FANCONTROL does not appear to be running"
+     fi
+     echo
+   fi
+}
+
+condrestart()
+{
+   [ -e /var/lock/subsys/fancontrol ] && restart || :
+}
+
+if [ ! -x $FANCONTROL ] ; then
+   echo "Cannot run $FANCONTROL"
+   exit 0
+fi
+
+case "$1" in
+   start)
+     start
+     ;;
+
+   stop)
+     stop
+     ;;
+
+   status)
+     status
+     ;;
+
+   restart)
+     stop
+     start
+     ;;
+
+   condrestart)
+     condrestart
+     ;;
+
+   *)
+     echo "Usage: /etc/init.d/fancontrol 
+{start|stop|status|restart|condrestart}"
+     RETVAL=1
+esac
+
+exit $RETVAL
Index: /lm-sensors/tags/V3-3-0/prog/init/README
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/init/README	(revision 5756)
+++ /lm-sensors/tags/V3-3-0/prog/init/README	(revision 5756)
@@ -0,0 +1,33 @@
+This directory contains files useful for initialization
+scripts, especially for RedHat.
+
+There are two choices, lm_sensors.init and sensord.init.
+Use one or the other but not both!
+
+----------------
+
+The script lm_sensors.init is a nice startup/shutdown script.
+On RH and other RH-based systems (MDK, notably) use this sort of startup
+mechanism (i.e. SVR4-based), as opposed to the everything-in-one-script
+(/etc/rc or /etc/rc.local) like the BSD-based systems do.
+The lm_sensors.init file should be copied to /etc/rc.d/init.d/lm_sensors.
+For more information see the top of the file.
+It requires the /etc/sysconfig/lm_sensors file, which identifies
+the correct modules.
+
+The /etc/sysconfig/lm_sensors file is generated by running sensors-detect.
+Once the script is done detecting the hardware monitoring devices, it will
+list all the required modules in that file, so that they can be loaded
+automatically at boot time.
+
+----------------
+
+The script sensord.init also loads the modules, and then
+starts the sensord daemon. See the manual page sensord(8) for
+more information on the daemon.
+
+The sensord.init file should be copied to /etc/rc.d/init.d/sensord.
+For more information see the top of the file.
+
+To configure this service one should put appropriate "alias i2c-bus-0 xxx"
+and "alias i2c-sensors-chip-0 xxx" in /etc/modules.conf.
Index: /lm-sensors/tags/V3-3-0/prog/pwm/pwmconfig.8
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/pwm/pwmconfig.8	(revision 5344)
+++ /lm-sensors/tags/V3-3-0/prog/pwm/pwmconfig.8	(revision 5344)
@@ -0,0 +1,55 @@
+.TH PWMCONFIG 8 "September 28, 2008" "lm-sensors 3"
+.SH NAME
+pwmconfig \- tests the PWM outputs of sensors and configures fancontrol
+
+.SH SYNOPSIS
+.B pwmconfig
+
+.SH WARNING
+\fBpwmconfig\fR will attempt to stop your fans, one at a time, for
+approximately 5 seconds each. This may cause your processor
+temperature to rise. Verify that all fans are running at normal speed
+after this program has exited.
+\fBpwmconfig\fR does its best to check that the fans are spinning when
+they are supposed to, but due to the diversity of available
+motherboards and fans, it shouldn't be blindly trusted. Always verify
+by yourself.
+
+It is strongly recommended to run \fBpwmconfig\fR at a time when there
+is no significant system load, to minimize the risk of overheating.
+
+.SH DESCRIPTION
+.B pwmconfig
+searches your sensors for pulse width modulation (PWM)
+controls, and tests each one to see if it controls a fan on
+your motherboard. Note that many motherboards do not have PWM
+circuitry installed, even if your sensor chip supports PWM.
+
+When a connection is established between a PWM control and a fan,
+\fBpwmconfig\fR can generate a detailed correlation, to show how a
+given fan is responding to various PWM duty cycles.
+
+Lastly, \fBpwmconfig\fR will enter in \fBfancontrol\fR configuration
+mode (unless you decide to skip that part.) In this mode, you are
+invited to enter several parameters which will determine how the
+\fBfancontrol\fR daemon regulates the speed of one or more fans in
+your system based on temperature measurements. In particular, you will
+have the opportunity to establish mappings between fans and
+temperature inputs, define the temperature range over which the speed
+of the fan should be adjusted dynamically, the minimum speed at which
+the fan should spin, etc. See fancontrol(8) for additional
+information.
+
+.SH BUGS
+The term "PWM" is used because most fan control systems in computers
+are based on pulse width modulation. Some motherboards however use DC
+variation instead. So, the term "PWM" should be seen as a generic term
+for "fan speed control", regardless of the actual method used.
+
+.SH SEE ALSO
+fancontrol(8), sensors(1).
+
+.SH AUTHORS
+.PP
+Marius Reiner <marius.reiner@hdev.de>,
+Jean Delvare <khali@linux-fr.org>
Index: /lm-sensors/tags/V3-3-0/prog/pwm/vt1211_pwm
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/pwm/vt1211_pwm	(revision 5338)
+++ /lm-sensors/tags/V3-3-0/prog/pwm/vt1211_pwm	(revision 5338)
@@ -0,0 +1,179 @@
+#!/bin/sh
+#
+# vt1211_pwm - Manually control the PWM outputs of the VIA vt1211 Super-I/O
+#
+# Copyright (C) 2008 Juerg Haefliger (juergh at gmail.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; 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+function usage
+{
+    echo "Usage: vt1211_pwm -i <pwm> | -r <pwm> | -s <pwm>,<val>"
+    echo
+    echo "DESCRIPTION"
+    echo "   This tool provides manual control of the PWM outputs of the VIA "
+    echo "   vt1211 Super-I/O."
+    echo
+    echo "OPTIONS"
+    echo "   -d               Display the current PWM settings of the vt1211."
+    echo "   -i <pwm>         Initialize the PWM output for manual control."
+    echo "   -r <pwm>         Restore the PWM output to the original settings."
+    echo "   -s <pwm>,<val>   Set the PWM output duty-cycle. "
+    echo
+    echo "WITH"
+    echo "   <pwm>   PWM output (1-2)."
+    echo "   <val>   PWM duty-cycle (0-255)."
+    echo
+    exit 2
+}
+
+function set_attr
+{
+    local tmp
+    read tmp < $1
+    echo "echo $tmp > $1"
+    echo $2 > $1
+}
+
+# check if we have the right permissions to run
+if [ `whoami` != "root" ] ; then
+    echo "You must be root to run this script."
+    exit 1
+fi
+
+# parse the command line options
+action=
+nr=1
+val=0
+while getopts di:r:s: opt ; do
+    [ -z "$action" ] || usage
+    case $opt in
+	d)  action="display"
+	    ;;
+	i)  action="init"
+	    nr=$OPTARG
+	    ;;
+	r)  action="restore"
+	    nr=$OPTARG
+	    ;;
+	s)  action="set"
+	    nr=${OPTARG%,*}
+	    val=${OPTARG#*,}
+	    ;;
+	?)  usage
+	    ;;
+    esac
+done
+
+[ -z "$action" ] && usage
+[ $nr -lt 1 -o $nr -gt 2 ] && usage
+[ $val -lt 0 -o $val -gt 255 ] && usage
+
+# check hwmon device
+dev=`ls -d /sys/devices/platform/vt1211.*`
+if [ -z "$dev" ] ; then
+    echo "Platform device vt1211 not found. Did you load the vt1211 module?"
+    exit 1
+fi
+
+# init file
+initfile="/root/.vt1211_pwm${nr}"
+
+# ACTION: display
+if [ $action = "display" ] ; then
+    for nr in 1 2 ; do
+	read mode < ${dev}/pwm${nr}_enable
+	if [ $mode -eq 0 ] ; then
+	    mode_txt="(disabled)"
+	elif [ $mode -eq 2 ] ; then
+	    mode_txt="(auto)    "
+	else
+	    mode_txt="(unknown) "
+	fi
+	read val < ${dev}/pwm${nr}
+	dc=$((val * 100 / 255))
+	echo "pwm${nr}: mode = ${mode} ${mode_txt}, val = ${val} (${dc}%)"
+    done
+fi
+
+# ACTION: init
+if [ $action = "init" ] ; then
+    # check for an init file
+    if [ -e  $initfile ] ; then
+	echo " $initfile exists. Did you already initialize this PWM?"
+	exit 1
+    fi
+
+    (
+        # set the pwm-to-temp mapping
+        # use the internal temp as a reference
+	set_attr ${dev}/pwm${nr}_auto_channels_temp 2
+
+        # enable the pwm auto controller
+	set_attr ${dev}/pwm${nr}_enable 2
+
+        # set the PWM duty-cycle
+	set_attr ${dev}/pwm${nr}_auto_point2_pwm 0
+	set_attr ${dev}/pwm${nr}_auto_point3_pwm 255
+
+        # set the temp boundaries to extrem values
+	set_attr ${dev}/pwm1_auto_point1_temp 0
+	set_attr ${dev}/pwm1_auto_point2_temp 0
+	set_attr ${dev}/pwm1_auto_point3_temp 1000000
+	set_attr ${dev}/pwm1_auto_point4_temp 1000000
+
+    ) > $initfile
+
+    exit 0
+fi
+
+# ACTION: restore
+if [ $action = "restore" ] ; then
+    # check for an init file
+    if [ ! -e  $initfile ] ; then
+	echo " $initfile not found. Nothing to restore."
+	exit 1
+    fi
+
+    sh $initfile
+    rm $initfile
+
+    exit 0
+fi
+
+# ACTION: set
+if [ $action = "set" ] ; then
+    # check for an init file
+    if [ ! -e $initfile ] ; then
+	echo "$initfile not found. Did you initialize this PWM?"
+	exit 1
+    fi
+
+    # set the PWM duty-cycle
+    echo $val > ${dev}/pwm${nr}_auto_point3_pwm
+
+    # fool the vt1211 into believing there is a temp change
+    # otherwise the new PWM duty-cycle doesn't take effect
+    echo 0 > ${dev}/pwm1_auto_point3_temp
+    echo 1000000 > ${dev}/pwm1_auto_point3_temp
+
+    # read the new PWM duty-cycle back and print it
+    read val < ${dev}/pwm${nr}
+    dc=$((val * 100 / 255))
+    echo "pwm${nr} is now at ${val} (${dc}%)."
+
+    exit 0
+fi
Index: /lm-sensors/tags/V3-3-0/prog/pwm/fancontrol
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/pwm/fancontrol	(revision 5770)
+++ /lm-sensors/tags/V3-3-0/prog/pwm/fancontrol	(revision 5770)
@@ -0,0 +1,487 @@
+#!/bin/bash
+#
+# Simple script implementing a temperature dependent fan speed control
+# Supported Linux kernel versions: 2.6.5 and later
+#
+# Version 0.70
+#
+# Usage: fancontrol [CONFIGFILE]
+#
+# Dependencies:
+#   bash, egrep, sed, cut, sleep, readlink, lm_sensors :)
+#
+# Please send any questions, comments or success stories to
+# marius.reiner@hdev.de
+# Thanks!
+#
+# For configuration instructions and warnings please see fancontrol.txt, which
+# can be found in the doc/ directory or at the website mentioned above.
+#
+#
+#    Copyright 2003 Marius Reiner <marius.reiner@hdev.de>
+#    Copyright (C) 2007-2009 Jean Delvare <khali@linux-fr.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.
+#
+#    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.
+#
+#
+
+PIDFILE="/var/run/fancontrol.pid"
+
+#DEBUG=1
+MAX=255
+
+declare -i pwmval
+
+function LoadConfig {
+	echo "Loading configuration from $1 ..."
+	if [ ! -r "$1" ]
+	then
+		echo "Error: Can't read configuration file"
+		exit 1
+	fi
+
+	# grep configuration from file
+	INTERVAL=`egrep '^INTERVAL=.*$' $1 | sed -e 's/INTERVAL=//g'`
+	DEVPATH=`egrep '^DEVPATH=.*$' $1 | sed -e 's/DEVPATH= *//g'`
+	DEVNAME=`egrep '^DEVNAME=.*$' $1 | sed -e 's/DEVNAME= *//g'`
+	FCTEMPS=`egrep '^FCTEMPS=.*$' $1 | sed -e 's/FCTEMPS=//g'`
+	MINTEMP=`egrep '^MINTEMP=.*$' $1 | sed -e 's/MINTEMP=//g'`
+	MAXTEMP=`egrep '^MAXTEMP=.*$' $1 | sed -e 's/MAXTEMP=//g'`
+	MINSTART=`egrep '^MINSTART=.*$' $1 | sed -e 's/MINSTART=//g'`
+	MINSTOP=`egrep '^MINSTOP=.*$' $1 | sed -e 's/MINSTOP=//g'`
+	# optional settings:
+	FCFANS=`egrep '^FCFANS=.*$' $1 | sed -e 's/FCFANS=//g'`
+	MINPWM=`egrep '^MINPWM=.*$' $1 | sed -e 's/MINPWM=//g'`
+	MAXPWM=`egrep '^MAXPWM=.*$' $1 | sed -e 's/MAXPWM=//g'`
+
+	# Check whether all mandatory settings are set
+	if [[ -z ${INTERVAL} || -z ${FCTEMPS} || -z ${MINTEMP} || -z ${MAXTEMP} || -z ${MINSTART} || -z ${MINSTOP} ]]
+	then
+		echo "Some mandatory settings missing, please check your config file!"
+		exit 1
+	fi
+	if [ "$INTERVAL" -le 0 ]
+	then
+		echo "Error in configuration file:"
+		echo "INTERVAL must be at least 1"
+		exit 1
+	fi
+
+	# write settings to arrays for easier use and print them
+	echo
+	echo "Common settings:"
+	echo "  INTERVAL=$INTERVAL"
+
+	let fcvcount=0
+	for fcv in $FCTEMPS
+	do
+		if ! echo $fcv | egrep -q '='
+		then
+			echo "Error in configuration file:"
+			echo "FCTEMPS value is improperly formatted"
+			exit 1
+		fi
+
+		AFCPWM[$fcvcount]=`echo $fcv |cut -d'=' -f1`
+		AFCTEMP[$fcvcount]=`echo $fcv |cut -d'=' -f2`
+		AFCFAN[$fcvcount]=`echo $FCFANS |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2`
+		AFCMINTEMP[$fcvcount]=`echo $MINTEMP |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2`
+		AFCMAXTEMP[$fcvcount]=`echo $MAXTEMP |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2`
+		AFCMINSTART[$fcvcount]=`echo $MINSTART |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2`
+		AFCMINSTOP[$fcvcount]=`echo $MINSTOP |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2`
+		AFCMINPWM[$fcvcount]=`echo $MINPWM |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2`
+		[ -z "${AFCMINPWM[$fcvcount]}" ] && AFCMINPWM[$fcvcount]=0
+		AFCMAXPWM[$fcvcount]=`echo $MAXPWM |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2`
+		[ -z "${AFCMAXPWM[$fcvcount]}" ] && AFCMAXPWM[$fcvcount]=255
+
+		# verify the validity of the settings
+		if [ "${AFCMINTEMP[$fcvcount]}" -ge "${AFCMAXTEMP[$fcvcount]}" ]
+		then
+			echo "Error in configuration file (${AFCPWM[$fcvcount]}):"
+			echo "MINTEMP must be less than MAXTEMP"
+			exit 1
+		fi
+		if [ "${AFCMAXPWM[$fcvcount]}" -gt 255 ]
+		then
+			echo "Error in configuration file (${AFCPWM[$fcvcount]}):"
+			echo "MAXPWM must be at most 255"
+			exit 1
+		fi
+		if [ "${AFCMINSTOP[$fcvcount]}" -ge "${AFCMAXPWM[$fcvcount]}" ]
+		then
+			echo "Error in configuration file (${AFCPWM[$fcvcount]}):"
+			echo "MINSTOP must be less than MAXPWM"
+			exit 1
+		fi
+		if [ "${AFCMINSTOP[$fcvcount]}" -lt "${AFCMINPWM[$fcvcount]}" ]
+		then
+			echo "Error in configuration file (${AFCPWM[$fcvcount]}):"
+			echo "MINSTOP must be greater than or equal to MINPWM"
+			exit 1
+		fi
+		if [ "${AFCMINPWM[$fcvcount]}" -lt 0 ]
+		then
+			echo "Error in configuration file (${AFCPWM[$fcvcount]}):"
+			echo "MINPWM must be at least 0"
+			exit 1
+		fi
+
+		echo
+		echo "Settings for ${AFCPWM[$fcvcount]}:"
+		echo "  Depends on ${AFCTEMP[$fcvcount]}"
+		echo "  Controls ${AFCFAN[$fcvcount]}"
+		echo "  MINTEMP=${AFCMINTEMP[$fcvcount]}"
+		echo "  MAXTEMP=${AFCMAXTEMP[$fcvcount]}"
+		echo "  MINSTART=${AFCMINSTART[$fcvcount]}"
+		echo "  MINSTOP=${AFCMINSTOP[$fcvcount]}"
+		echo "  MINPWM=${AFCMINPWM[$fcvcount]}"
+		echo "  MAXPWM=${AFCMAXPWM[$fcvcount]}"
+		let fcvcount=fcvcount+1
+	done
+	echo
+}
+
+function DevicePath()
+{
+	if [ -h "$1/device" ]
+	then
+		readlink -f "$1/device" | sed -e 's/^\/sys\///'
+	fi
+}
+
+function DeviceName()
+{
+	if [ -r "$1/name" ]
+	then
+		cat "$1/name" | sed -e 's/[[:space:]=]/_/g'
+	elif [ -r "$1/device/name" ]
+	then
+		cat "$1/device/name" | sed -e 's/[[:space:]=]/_/g'
+	fi
+}
+
+function ValidateDevices()
+{
+	local OLD_DEVPATH="$1" OLD_DEVNAME="$2" outdated=0
+	local entry device name path
+
+	for entry in $OLD_DEVPATH
+	do
+		device=`echo "$entry" | sed -e 's/=[^=]*$//'`
+		path=`echo "$entry" | sed -e 's/^[^=]*=//'`
+
+		if [ "`DevicePath "$device"`" != "$path" ]
+		then
+			echo "Device path of $device has changed"
+			outdated=1
+		fi
+	done
+
+	for entry in $OLD_DEVNAME
+	do
+		device=`echo "$entry" | sed -e 's/=[^=]*$//'`
+		name=`echo "$entry" | sed -e 's/^[^=]*=//'`
+
+		if [ "`DeviceName "$device"`" != "$name" ]
+		then
+			echo "Device name of $device has changed"
+			outdated=1
+		fi
+	done
+
+	return $outdated
+}
+
+# Check that all referenced sysfs files exist
+function CheckFiles {
+	local outdated=0
+
+	let fcvcount=0
+	while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs
+	do
+		pwmo=${AFCPWM[$fcvcount]}
+		if [ ! -w $pwmo ]
+		then
+			echo "Error: file $pwmo doesn't exist"
+			outdated=1
+		fi
+		let fcvcount=$fcvcount+1
+	done
+
+	let fcvcount=0
+	while (( $fcvcount < ${#AFCTEMP[@]} )) # go through all temp inputs
+	do
+		tsen=${AFCTEMP[$fcvcount]}
+		if [ ! -r $tsen ]
+		then
+			echo "Error: file $tsen doesn't exist"
+			outdated=1
+		fi
+		let fcvcount=$fcvcount+1
+	done
+
+	let fcvcount=0
+	while (( $fcvcount < ${#AFCFAN[@]} )) # go through all fan inputs
+	do
+		fan=${AFCFAN[$fcvcount]}
+		if [ ! -r $fan ]
+		then
+			echo "Error: file $fan doesn't exist"
+			outdated=1
+		fi
+		let fcvcount=$fcvcount+1
+	done
+
+	if [ $outdated -eq 1 ]
+	then
+		echo
+		echo "At least one referenced file is missing. Either some required kernel"
+		echo "modules haven't been loaded, or your configuration file is outdated."
+		echo "In the latter case, you should run pwmconfig again."
+	fi
+
+	return $outdated
+}
+
+if [ -f "$1" ]
+then
+	LoadConfig $1
+else
+	LoadConfig /etc/fancontrol
+fi
+
+# Detect path to sensors
+if echo "${AFCPWM[0]}" | egrep -q '^/'
+then
+	DIR=/
+elif echo "${AFCPWM[0]}" | egrep -q '^hwmon[0-9]'
+then
+	DIR=/sys/class/hwmon
+elif echo "${AFCPWM[0]}" | egrep -q '^[1-9]*[0-9]-[0-9abcdef]{4}'
+then
+	DIR=/sys/bus/i2c/devices
+else
+	echo "$0: Invalid path to sensors"
+	exit 1
+fi
+
+if [ ! -d $DIR ]
+then
+	echo $0: 'No sensors found! (did you load the necessary modules?)'
+	exit 1
+fi
+cd $DIR
+
+# Check for configuration change
+if [ -z "$DEVPATH" -o -z "$DEVNAME" ]
+then
+	echo "Configuration is too old, please run pwmconfig again"
+	exit 1
+fi
+if ! ValidateDevices "$DEVPATH" "$DEVNAME"
+then
+	echo "Configuration appears to be outdated, please run pwmconfig again"
+	exit 1
+fi
+CheckFiles || exit 1
+
+if [ -f "$PIDFILE" ]
+then
+	echo "File $PIDFILE exists, is fancontrol already running?"
+	exit 1
+fi
+echo $$ > "$PIDFILE"
+
+# $1 = pwm file name
+function pwmdisable()
+{
+	ENABLE=${1}_enable
+	# No enable file? Just set to max
+	if [ ! -f $ENABLE ]
+	then
+		echo $MAX > $1
+		return 0
+	fi
+
+	# Try pwmN_enable=0
+	echo 0 > $ENABLE 2> /dev/null
+	if [ `cat $ENABLE` -eq 0 ]
+	then
+		# Success
+		return 0
+	fi
+
+	# It didn't work, try pwmN_enable=1 pwmN=255
+	echo 1 > $ENABLE 2> /dev/null
+	echo $MAX > $1
+	if [ `cat $ENABLE` -eq 1 -a `cat $1` -ge 190 ]
+	then
+		# Success
+		return 0
+	fi
+
+	# Nothing worked
+	echo "$ENABLE stuck to" `cat $ENABLE` >&2
+	return 1
+}
+
+# $1 = pwm file name
+function pwmenable()
+{
+	ENABLE=${1}_enable
+	if [ -f $ENABLE ]
+	then
+		echo 1 > $ENABLE 2> /dev/null
+		if [ $? -ne 0 ]
+		then
+			return 1
+		fi
+	fi
+	echo $MAX > $1
+}
+
+function restorefans()
+{
+	local status=$1
+	echo 'Aborting, restoring fans...'
+	let fcvcount=0
+	while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs
+	do
+		pwmo=${AFCPWM[$fcvcount]}
+		pwmdisable $pwmo
+		let fcvcount=$fcvcount+1
+	done
+	echo 'Verify fans have returned to full speed'
+	rm -f "$PIDFILE"
+	exit $status
+}
+
+trap 'restorefans 0' SIGQUIT SIGTERM
+trap 'restorefans 1' SIGHUP SIGINT
+
+# main function
+function UpdateFanSpeeds {
+	let fcvcount=0
+	while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs
+	do
+		#hopefully shorter vars will improve readability:
+		pwmo=${AFCPWM[$fcvcount]}
+		tsens=${AFCTEMP[$fcvcount]}
+		fan=${AFCFAN[$fcvcount]}
+		let mint="${AFCMINTEMP[$fcvcount]}*1000"
+		let maxt="${AFCMAXTEMP[$fcvcount]}*1000"
+		minsa=${AFCMINSTART[$fcvcount]}
+		minso=${AFCMINSTOP[$fcvcount]}
+		minpwm=${AFCMINPWM[$fcvcount]}
+		maxpwm=${AFCMAXPWM[$fcvcount]}
+
+		read tval < ${tsens}
+		if [ $? -ne 0 ]
+		then
+			echo "Error reading temperature from $DIR/$tsens"
+			restorefans 1
+		fi
+
+		read pwmpval < ${pwmo}
+		if [ $? -ne 0 ]
+		then
+			echo "Error reading PWM value from $DIR/$pwmo"
+			restorefans 1
+		fi
+
+		# If fanspeed-sensor output shall be used, do it
+		if [[ -n ${fan} ]]
+		then
+			read fanval < ${fan}
+			if [ $? -ne 0 ]
+			then
+				echo "Error reading Fan value from $DIR/$fan"
+				restorefans 1
+			fi
+		else
+			fanval=1  # set it to a non zero value, so the rest of the script still works
+		fi
+
+		# debug info
+		if [ "$DEBUG" != "" ]
+		then
+			echo "pwmo=$pwmo"
+			echo "tsens=$tsens"
+			echo "fan=$fan"
+			echo "mint=$mint"
+			echo "maxt=$maxt"
+			echo "minsa=$minsa"
+			echo "minso=$minso"
+			echo "minpwm=$minpwm"
+			echo "maxpwm=$maxpwm"
+			echo "tval=$tval"
+			echo "pwmpval=$pwmpval"
+			echo "fanval=$fanval"
+		fi
+
+		if (( $tval <= $mint ))
+		  then pwmval=$minpwm # below min temp, use defined min pwm
+		elif (( $tval >= $maxt ))
+		  then pwmval=$maxpwm # over max temp, use defined max pwm
+		else
+		  # calculate the new value from temperature and settings
+		  pwmval="(${tval}-${mint})*(${maxpwm}-${minso})/(${maxt}-${mint})+${minso}"
+		  if [ $pwmpval -eq 0 -o $fanval -eq 0 ]
+		  then # if fan was stopped start it using a safe value
+		  	echo $minsa > $pwmo
+			# Sleep while still handling signals
+			sleep 1 &
+			wait $!
+		  fi
+		fi
+		echo $pwmval > $pwmo # write new value to pwm output
+		if [ $? -ne 0 ]
+		then
+			echo "Error writing PWM value to $DIR/$pwmo"
+			restorefans 1
+		fi
+		if [ "$DEBUG" != "" ]
+		then
+			echo "new pwmval=$pwmval"
+		fi
+		let fcvcount=$fcvcount+1
+	done
+}
+
+echo 'Enabling PWM on fans...'
+let fcvcount=0
+while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs
+do
+	pwmo=${AFCPWM[$fcvcount]}
+	pwmenable $pwmo
+	if [ $? -ne 0 ]
+	then
+		echo "Error enabling PWM on $DIR/$pwmo"
+		restorefans 1
+	fi
+	let fcvcount=$fcvcount+1
+done
+
+echo 'Starting automatic fan control...'
+
+# main loop calling the main function at specified intervals
+while true
+do
+	UpdateFanSpeeds
+	# Sleep while still handling signals
+	sleep $INTERVAL &
+	wait $!
+done
Index: /lm-sensors/tags/V3-3-0/prog/pwm/vt1211_pwm.8
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/pwm/vt1211_pwm.8	(revision 5338)
+++ /lm-sensors/tags/V3-3-0/prog/pwm/vt1211_pwm.8	(revision 5338)
@@ -0,0 +1,52 @@
+.TH VT1211_PWM 8 "July 2008" "lm-sensors 3"
+.SH NAME
+vt1211_pwm \- manual control of VIA vt1211 PWM outputs
+
+.SH SYNOPSIS
+.B vt1211_pwm
+.B -d | -i <pwm> | -r <pwm> | -s <pwm>,<val>
+
+.SH DESCRIPTION
+\fBvt1211_pwm\fP is a shell script that provides manual control of the two PWM
+outputs of the VIA vt1211 Super-I/O device.
+
+.SH OPTIONS
+.TP
+.B -d
+Display the current settings of the two PWM outputs.
+.TP
+.B -i <pwm>
+Initialize PWM \fB<pwm>\fP for manual control. Valid numbers for \fB<pwm>\fP
+are 1 or 2. This option also writes the original vt1211 register values to
+~/.vt1211_pwm\fB<pwm>\fP which is used for restoring the original settings.
+.TP
+.B -r <pwm>
+Restore PWM \fB<pwm>\fP to the orignal settings. Valid numbers for \fB<pwm>\fP
+are 1 or 2. This options reads the original settings from
+~/.vt1211_pwm\fB<pwm>\fP and writes them to the vt1211.
+.TP
+.B -s <pwm>,<val>
+Set PMW \fB<pwm>\fP output to \fB<val>\fP. Valid numbers for \fB<pwm>\fP are 1
+or 2 and valid numbers for \fB<val>\fP are 0 to 255. The \fB<val>\fP range of
+0-255 maps to an output duty-cycle of 0%-100%, i.e., output duty-cycle =
+\fB<val>\fP * 100 / 255.
+
+.SH REQUIREMENTS
+You need root privileges to execute the vt1211_pwm script and the vt1211 kernel
+module needs to be loaded and you obviously need to have fans connected to the
+PWM outputs of the vt1211 :-)
+
+.SH WARNING
+This script puts the PWM controller of the vt1211 into automatic mode and
+toggles the thermal thresholds between extrem values to fool the chip into
+believing that there is a temperature change and thus forcing the PWM outputs
+to change to the specified values. Since there is only a single PWM controller
+that controls both PWM outputs, setting the duty-cycle of one PWM output could
+affect the duty-cycle of the other PWM output. Use at you own risk!
+
+.SH SEE ALSO
+sensors(1)
+
+.SH AUTHOR
+.PP
+Juerg Haefliger <juergh at gmail.com>
Index: /lm-sensors/tags/V3-3-0/prog/pwm/pwmconfig
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/pwm/pwmconfig	(revision 5857)
+++ /lm-sensors/tags/V3-3-0/prog/pwm/pwmconfig	(revision 5857)
@@ -0,0 +1,1019 @@
+#!/bin/bash
+#
+# pwmconfig
+# Tests the pwm outputs of sensors and configures fancontrol
+# Supported Linux kernel versions: 2.6.5 and later
+#
+#    Warning!!! This program will stop your fans, one at a time,
+#    for approximately 5 seconds each!!!
+#    This may cause your processor temperature to rise!!!
+#    Verify that all fans are running at normal speed after this
+#    program has exited!!!
+#
+#    Copyright (C) 2007-2009 Jean Delvare <khali@linux-fr.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.
+#
+#    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.
+#
+#
+
+REVISION=$(echo '$Revision$' | cut -d' ' -f2)
+REVDATE=$(echo '$Date$' | cut -d' ' -f2)
+PIDFILE="/var/run/fancontrol.pid"
+
+if [ -f "$PIDFILE" ]
+then
+	echo "File $PIDFILE exists. This typically means that the"
+	echo "fancontrol deamon is running. You should stop it before running pwmconfig."
+	echo "If you are certain that fancontrol is not running, then you can delete"
+	echo "$PIDFILE manually."
+	exit 1
+fi
+
+if [ "`id -u`" != "0" ]
+then
+	echo "You need to be root to run this script."
+	exit 1
+fi
+
+echo "# pwmconfig revision $REVISION ($REVDATE)"
+echo 'This program will search your sensors for pulse width modulation (pwm)'
+echo 'controls, and test each one to see if it controls a fan on'
+echo 'your motherboard. Note that many motherboards do not have pwm'
+echo 'circuitry installed, even if your sensor chip supports pwm.'
+echo
+echo 'We will attempt to briefly stop each fan using the pwm controls.'
+echo 'The program will attempt to restore each fan to full speed'
+echo 'after testing. However, it is ** very important ** that you'
+echo 'physically verify that the fans have been to full speed'
+echo 'after the program has completed.'
+echo
+
+DELAY=5 # 3 seconds delay is too short for large fans, thus I increased it to 5
+MAX=255
+
+if [ -d "/sys/class/hwmon" ]
+then
+	SYSFS=2
+	DIR="/sys/class/hwmon"
+	PREFIX='hwmon*'
+elif [ -d "/sys/bus/i2c/devices" ]
+then
+	SYSFS=1
+	DIR="/sys/bus/i2c/devices"
+	PREFIX='*-*'
+else
+	echo $0: 'No sensors found! (modprobe sensor modules?)'
+	exit 1
+fi
+
+cd $DIR
+DEVICES=`echo $PREFIX`
+if [ "$PREFIX" = "$DEVICES" ]
+then
+	echo $0: 'No sensors found! (modprobe sensor modules?)'
+	exit 1
+fi
+
+# We may need to adjust the device path
+if [ "$SYSFS" = "2" ]
+then
+	OLD_DEVICES="$DEVICES"
+	DEVICES=""
+
+	for device in $OLD_DEVICES
+	do
+		if [ ! -r "$device/name" ]
+		then
+			device="$device/device"
+		fi
+		
+		DEVICES="$DEVICES $device"
+	done
+fi
+
+
+for device in $DEVICES
+do
+	# Find available fan control outputs
+	MATCH=$device/'pwm[1-9]'
+	device_pwm=`echo $MATCH`
+	if [ "$SYSFS" = "1" -a "$MATCH" = "$device_pwm" ]
+	then
+		# Deprecated naming scheme (used in kernels 2.6.5 to 2.6.9)
+		MATCH=$device/'fan[1-9]_pwm'
+		device_pwm=`echo $MATCH`
+	fi
+	if [ "$MATCH" != "$device_pwm" ]
+	then
+		PWM="$PWM $device_pwm"
+	fi
+
+	# Find available fan monitoring inputs
+	MATCH=$device/'fan[1-9]_input'
+	device_fan=`echo $MATCH`
+	if [ "$MATCH" != "$device_fan" ]
+	then
+		FAN="$FAN $device_fan"
+	fi
+done
+
+if [ -z "$PWM" ]
+then
+	echo $0: 'There are no pwm-capable sensor modules installed'
+	exit 1
+fi
+if [ -z "$FAN" ]
+then
+	echo $0: 'There are no fan-capable sensor modules installed'
+	exit 1
+fi
+
+# $1 = padding
+function print_devices()
+{
+	local name
+
+	for device in $DEVICES
+	do
+		name=`cat $device/name 2> /dev/null`
+		[ -z "$name" ] && name="unknown (no name attribute)"
+		echo "$1$device is $name"
+	done
+}
+
+# $1 = pwm file name
+function is_pwm_auto()
+{
+	ENABLE=${1}_enable
+	if [ -f $ENABLE ]
+	then
+		if [ "`cat $ENABLE`" -gt 1 ]
+		then
+			return 0
+		fi
+	fi
+
+	return 1
+}
+
+# $1 = pwm file name
+function pwmdisable()
+{
+	ENABLE=${1}_enable
+	# No enable file? Just set to max
+	if [ ! -f $ENABLE ]
+	then
+		echo $MAX > $1
+		return 0
+	fi
+
+	# Try pwmN_enable=0
+	echo 0 2>/dev/null > $ENABLE
+	if [ "`cat $ENABLE`" -eq 0 ]
+	then
+		# Success
+		return 0
+	fi
+
+	# It didn't work, try pwmN_enable=1 pwmN=255
+	echo 1 2>/dev/null > $ENABLE
+	if [ "`cat $ENABLE`" -ne 1 ]
+	then
+		echo "$ENABLE stuck to `cat $ENABLE`" >&2
+		return 1
+	fi
+
+	echo $MAX > $1
+	if [ "`cat $1`" -ge 190 ]
+	then
+		# Success
+		return 0
+	fi
+
+	# Nothing worked
+	echo "$1 stuck to `cat $1`" >&2
+	return 1
+}
+
+# $1 = pwm file name
+function pwmenable()
+{
+	ENABLE=${1}_enable
+	if [ -w $ENABLE ]
+	then
+		echo 1 2>/dev/null > $ENABLE
+		if [ $? -ne 0 ]
+		then
+			return 1
+		fi
+	fi
+	echo $MAX > $1
+}
+
+# $1 = pwm file name; $2 = pwm value 0-255
+function pwmset()
+{
+	echo $2 > $1
+}
+
+echo 'Found the following devices:'
+print_devices "   "
+echo
+
+echo 'Found the following PWM controls:'
+for i in $PWM
+do
+	echo "   $i"
+	if [ -w $i ]
+	then
+		# First check if PWM output is in automatic mode
+		if is_pwm_auto $i
+		then
+			echo "$i is currently setup for automatic speed control."
+			echo 'In general, automatic mode is preferred over manual mode, as'
+			echo 'it is more efficient and it reacts faster. Are you sure that'
+			echo -n 'you want to setup this output for manual control? (n) '
+			read X
+			if [ "$X" = "" -o "$X" != "y" -a "$X" != "Y" ]
+			then
+				continue
+			fi
+		fi
+
+		pwmdisable $i
+		if [ $? -ne 0 ]
+		then
+			echo "Manual control mode not supported, skipping $i."
+		elif [ "$GOODPWM" = "" ]
+		then
+			GOODPWM=$i
+		else
+			GOODPWM="$GOODPWM $i"
+		fi
+	else
+		echo "Can't write to $i, skipping."
+	fi
+done
+
+if [ "$GOODPWM" = "" ]
+then
+	echo 'There are no usable PWM outputs.'
+	exit 1
+fi
+
+echo
+echo "Giving the fans some time to reach full speed..."
+sleep $DELAY
+echo 'Found the following fan sensors:'
+for i in $FAN
+do
+	S=`cat $i`
+	if [ "$S" = "0" -o "$S" = "-1" ]
+	then
+		echo "   $i     current speed: 0 ... skipping!"
+	else
+		echo "   $i     current speed: $S RPM"
+		if [ "$GOODFAN" = "" ]
+		then
+			GOODFAN=$i
+			SPEEDS=$S
+		else
+			GOODFAN="$GOODFAN $i"
+			SPEEDS="$SPEEDS $S"
+		fi
+	fi
+done
+echo
+
+if [ "$GOODFAN" = "" ]
+then
+	echo 'There are no working fan sensors, all readings are 0.'
+	echo 'Make sure you have a 3-wire fan connected.'
+	echo 'You may also need to increase the fan divisors.'
+	echo 'See doc/fan-divisors for more information.'
+	exit 1
+fi
+
+echo 'Warning!!! This program will stop your fans, one at a time,'
+echo "for approximately $DELAY seconds each!!!"
+echo 'This may cause your processor temperature to rise!!!'
+echo 'If you do not want to do this hit control-C now!!!'
+echo -n 'Hit return to continue: '
+read X
+echo
+
+PLOTTER=gnuplot
+STEP=15
+PDELAY=2
+# Use a smaller step for low PWM values as this is typically where the
+# more important fan speed changes are happening.
+STEP2=2
+STEP2_BELOW=31
+
+function pwmdetail()
+{
+	P=$1
+	F=$2
+	PLOT=
+
+	type $PLOTTER > /dev/null 2>&1
+	if [ $? -eq 0 ]
+	then
+		echo -n "Would you like to generate a graphical plot using $PLOTTER (y)? "
+		read X
+		if [ "$X" = "y" -o "$X" = "Y" -o "$X" = "" ]
+		then
+			PLOT=y
+		fi
+	else
+		if [ -n "$DISPLAY" ]
+		then
+			echo "Note: If you had $PLOTTER installed, I could generate a graphical plot."
+		fi
+	fi
+
+	if [ "$PLOT" = "y" ]
+	then
+		TMP1=`mktemp -t pwmtest1.XXXXXXXXXX` || { echo "$0: Cannot create temporary file" >&2; exit 1; }
+		TMP2=`mktemp -t pwmtest2.XXXXXXXXXX` || { rm -f $TMP1 ; echo "$0: Cannot create temporary file" >&2; exit 1; }
+		echo "set xlabel \"PWM: $P\"" > $TMP1
+		echo "set ylabel \"FAN: $F (RPM)\"" >> $TMP1
+		echo 'set nokey' >> $TMP1
+		echo 'set xrange [0:255]' >> $TMP1
+		echo "plot \"$TMP2\" with lines" >> $TMP1
+		echo 'pause -1 "    Hit return to continue..."' >> $TMP1
+		> $TMP2
+	fi
+
+	local threshold=100000
+	let pwm=$MAX
+	pwmenable $P
+	while [ $pwm -ge 0 ]
+	do
+		pwmset $P $pwm
+		sleep $PDELAY
+		if [ $? -ne 0 ]
+		then
+			pwmdisable $P
+			echo '^C received, aborting...'
+			rm -f $TMP1 $TMP2
+			exit 1
+		fi
+		S=`cat $F`
+		# Fan speed should never increase significantly
+		if [ $S -gt $threshold ]
+		then
+			echo "    PWM $pwm FAN $S (probably incorrect)"
+		else
+			echo "    PWM $pwm FAN $S"
+			let threshold=S*6/5
+		fi
+
+		if [ "$PLOT" = "y" ]
+		then
+			echo "$pwm $S" >> $TMP2
+		fi
+		if [ "$S" = "0" -o "$S" = "-1" ]
+		then
+			pwmdisable $P
+			echo "    Fan Stopped at PWM = $pwm"
+			if [ $pwm -eq $MAX ]
+			then
+				echo "    This fan appears to stop when the PWM is enabled;"
+				echo "    perhaps the fan input shares a pin with the PWM output"
+				echo "    on the sensor chip."
+				echo "    You cannot control this fan with this PWM output."
+				rm -f $TMP1 $TMP2
+				echo
+				return 0
+			fi
+			break
+		fi
+		if [ $pwm -lt $STEP2_BELOW ]
+		then
+			let pwm=$pwm-$STEP2
+		else
+			let pwm=$pwm-$STEP
+		fi
+	done
+	pwmdisable $P
+	if [ "$PLOT" = "y" ]
+	then
+		$PLOTTER  $TMP1
+		rm -f $TMP1 $TMP2
+	fi
+	echo
+}
+
+for i in $GOODPWM
+do
+	echo Testing pwm control $i ...
+	pwmenable $i
+	if [ $? -ne 0 ]
+	then
+		echo "Manual control mode not supported, skipping."
+		continue
+	fi
+	pwmset $i 0
+	sleep $DELAY
+	if [ $? -ne 0 ]
+	then
+		pwmdisable $i
+		echo '^C received, restoring PWM and aborting...'
+		exit 1
+	fi
+
+	# Sample all current fan speeds at once, so that we can quickly
+	# disable PWM and return all fans to full speed
+	CURRENT_SPEEDS="`cat $GOODFAN`"
+	pwmdisable $i
+
+	let pwmactivecount=0
+	let count=1
+	for j in $GOODFAN
+	do
+		OS=`echo $SPEEDS | cut -d' ' -f$count`
+		S=`echo $CURRENT_SPEEDS | cut -d' ' -f$count`
+		echo "  $j ... speed was $OS now $S"
+		let threshold=2*$OS/3
+		if [ $S -lt $threshold ]
+		then
+			echo "    It appears that fan $j"
+			echo "    is controlled by pwm $i"
+#
+# a PWM can control more than one fan....
+#
+			if [ $pwmactivecount -eq 0 ]
+			then
+				let pwmactivecount=1
+				pwmactive="$i ${pwmactive}"
+				fanactive="$j ${fanactive}"
+				fanactive_min="$S ${fanactive_min}"
+
+				# Give all correlated fans time to return to full speed
+				sleep $DELAY
+				if [ $? -ne 0 ]
+				then
+					echo '^C received, aborting...'
+					exit 1
+				fi
+			else
+				fanactive="$j+${fanactive}" #not supported yet by fancontrol
+				fanactive_min="$S+${fanactive_min}"
+			fi
+			S=`cat $j`
+			if [ $S -lt $threshold ]
+			then
+				echo "    Fan $j has not returned to speed, please investigate!"
+			else
+				echo -n "Would you like to generate a detailed correlation (y)? "
+				read X
+				if [ "$X" = "y" -o "$X" = "Y" -o "$X" = "" ]
+				then
+					pwmdetail $i $j
+				fi
+			fi
+		else
+			echo "    no correlation"
+		fi
+		let count=count+1
+	done
+	echo
+	if [ "$pwmactivecount" = "0" ]
+	then
+		echo "No correlations were detected."
+		echo "There is either no fan connected to the output of $i,"
+		echo "or the connected fan has no rpm-signal connected to one of"
+		echo "the tested fan sensors. (Note: not all motherboards have"
+		echo "the pwm outputs connected to the fan connectors,"
+		echo "check out the hardware database on http://www.almico.com/forumindex.php)"
+		echo
+		echo -n "Did you see/hear a fan stopping during the above test (n)? "
+		read X
+		if [ "$X" = "y" -o "$X" = "Y" ]
+		then
+			pwmactive="$i ${pwmactive}"
+		fi
+		echo
+	fi
+done
+
+
+echo 'Testing is complete.'
+echo 'Please verify that all fans have returned to their normal speed.'
+echo
+echo 'The fancontrol script can automatically respond to temperature changes'
+echo 'of your system by changing fanspeeds.'
+echo -n 'Do you want to set up its configuration file now (y)? '
+
+read X
+if [ "$X" = "n" -o "$X" = "N" ]
+then
+	exit
+fi
+
+for device in $DEVICES
+do
+	# Find available temperature monitoring inputs
+	MATCH=$device/'temp[1-9]_input'
+	device_temp=`echo $MATCH`
+	if [ "$MATCH" != "$device_temp" ]
+	then
+		TEMPS="$TEMPS $device_temp"
+	fi
+done
+
+if [ -z "$TEMPS" ]
+then
+	echo $0: 'There are no temperature-capable sensor modules installed'
+	exit 1
+fi
+
+function DevicePath()
+{
+	if [ -h "$1/device" ]
+	then
+		readlink -f "$1/device" | sed -e 's/^\/sys\///'
+	fi
+}
+
+function DeviceName()
+{
+	if [ -r "$1/name" ]
+	then
+		cat "$1/name" | sed -e 's/[[:space:]=]/_/g'
+	elif [ -r "$1/device/name" ]
+	then
+		cat "$1/device/name" | sed -e 's/[[:space:]=]/_/g'
+	fi
+}
+
+function ValidateDevices()
+{
+	local OLD_DEVPATH="$1" OLD_DEVNAME="$2" outdated=0
+	local entry device name path
+
+	for entry in $OLD_DEVPATH
+	do
+		device=`echo "$entry" | sed -e 's/=[^=]*$//'`
+		path=`echo "$entry" | sed -e 's/^[^=]*=//'`
+
+		if [ "`DevicePath "$device"`" != "$path" ]
+		then
+			echo "Device path of $device has changed"
+			outdated=1
+		fi
+	done
+
+	for entry in $OLD_DEVNAME
+	do
+		device=`echo "$entry" | sed -e 's/=[^=]*$//'`
+		name=`echo "$entry" | sed -e 's/^[^=]*=//'`
+
+		if [ "`DeviceName "$device"`" != "$name" ]
+		then
+			echo "Device name of $device has changed"
+			outdated=1
+		fi
+	done
+
+	return $outdated
+}
+
+function AskPath()
+{
+	echo -n 'What should be the path to your fancontrol config file (/etc/fancontrol)? '
+
+	read FCCONFIG
+	if [ "$FCCONFIG" = "" ]
+	then
+		FCCONFIG="/etc/fancontrol"
+	fi
+}
+
+AskPath
+
+function ClearConfig()
+{
+	FCTEMPS=""
+	FCFANS=""
+	MINTEMP=""
+	MAXTEMP=""
+	MINSTART=""
+	MINSTOP=""
+	MINPWM=""
+	MAXPWM=""
+}
+
+function LoadConfig()
+{
+	local OLD_DEVPATH OLD_DEVNAME
+
+	# Nothing to do
+	if [ ! -f "$1" ]
+	then
+		ClearConfig
+		return 0
+	fi
+
+	echo "Loading configuration from $1 ..."
+	INTERVAL=`egrep '^INTERVAL=.*$' $1 | sed -e 's/INTERVAL= *//g'`
+	OLD_DEVPATH=`egrep '^DEVPATH=.*$' $1 | sed -e 's/DEVPATH= *//g'`
+	OLD_DEVNAME=`egrep '^DEVNAME=.*$' $1 | sed -e 's/DEVNAME= *//g'`
+	FCTEMPS=`egrep '^FCTEMPS=.*$' $1 | sed -e 's/FCTEMPS= *//g'`
+	FCFANS=`egrep '^FCFANS=.*$' $1 | sed -e 's/FCFANS= *//g'`
+	MINTEMP=`egrep '^MINTEMP=.*$' $1 | sed -e 's/MINTEMP= *//g'`
+	MAXTEMP=`egrep '^MAXTEMP=.*$' $1 | sed -e 's/MAXTEMP= *//g'`
+	MINSTART=`egrep '^MINSTART=.*$' $1 | sed -e 's/MINSTART= *//g'`
+	MINSTOP=`egrep '^MINSTOP=.*$' $1 | sed -e 's/MINSTOP= *//g'`
+	MINPWM=`egrep '^MINPWM=.*$' $1 | sed -e 's/MINPWM= *//g'`
+	MAXPWM=`egrep '^MAXPWM=.*$' $1 | sed -e 's/MAXPWM= *//g'`
+
+	# Check for configuration change
+	if ! ValidateDevices "$OLD_DEVPATH" "$OLD_DEVNAME"
+	then
+		echo "Configuration appears to be outdated, discarded"
+		ClearConfig
+		return 0
+	fi
+}
+
+LoadConfig $FCCONFIG
+
+# $1 = pwm value below which the fan is stopped
+function TestMinStart()
+{
+	echo
+	echo 'Now we increase the PWM value in 10-unit-steps.'
+	echo 'Let the fan stop completely, then press return until the'
+	echo "fan starts spinning. Then enter 'y'."
+	echo 'We will use this value +20 as the starting speed.'
+	let fanok=0
+	let fanval="$1"
+
+	pwmenable $pwms
+	until [ "$fanok" = "1" ]
+	do
+		if [ $fanval -gt 240 ] ; then let fanval=$MAX ; let fanok=1 ; fi
+		echo -n "Setting $pwms to $fanval..."
+		pwmset $pwms $fanval
+		read FANTEST
+		if [ "$FANTEST" != "" ] ; then let fanok=1 ; fi
+		let fanval=fanval+10
+	done
+	pwmdisable $pwms
+
+	let fanval=fanval+20
+	if [ $fanval -gt 240 ] ; then let fanval=$MAX ; fi
+	echo "OK, using $fanval"
+}
+
+# $1 = fan input to read the fan speed from
+function TestMinStop()
+{
+	local faninput=$1
+	local threshold=100000
+	local fanspeed
+
+	echo
+	echo 'Now we decrease the PWM value to figure out the lowest usable value.'
+	echo 'We will use a slightly greater value as the minimum speed.'
+	let fanval=$MAX
+
+	pwmenable $pwms
+	while [ $fanval -ge 0 ]
+	do
+		pwmset $pwms $fanval
+		sleep $PDELAY
+		fanspeed=`cat $faninput`
+		if [ $fanspeed -gt $threshold ]
+		then
+			echo "    PWM $fanval -> $fanspeed RPM (probably incorrect)"
+			break
+		else
+			echo "    PWM $fanval -> $fanspeed RPM"
+			if [ $fanspeed = "0" -o $fanspeed = "-1" ]
+			then
+				break
+			fi
+			let threshold=fanspeed*6/5
+		fi
+		if [ $fanval -lt $STEP2_BELOW ]
+		then
+			let fanval=$fanval-$STEP2
+		else
+			let fanval=$fanval-$STEP
+		fi
+	done
+	pwmdisable $pwms
+
+	if [ $fanval -lt $STEP2_BELOW ]
+	then
+		let 'fanval=fanval+2*STEP2'
+	else
+		let 'fanval=fanval+STEP'
+	fi
+	echo "OK, using $fanval"
+}
+
+# Remember the path and name of each device with at least one
+# reference (pwm, temp or fan) in the configuration file.
+# This function sets globals DEVPATH and DEVNAME as a side effect.
+function RememberDevices()
+{
+	local used entry device name path tempfandev pwmdev
+	DEVPATH=""
+	DEVNAME=""
+
+	for device in $DEVICES
+	do
+		device=`echo "$device" | sed -e 's/\/.*$//'`
+
+		used=0
+		for entry in $1 $2
+		do
+			pwmdev=`echo "$entry" | sed -e 's/\/.*$//'`
+			tempfandev=`echo "$entry" | sed -e 's/^[^=]*=//' -e 's/\/.*$//'`
+
+			if [ "$device" = "$pwmdev" -o "$device" = "$tempfandev" ]
+			then
+				used=1
+			fi
+		done
+		if [ "$used" -eq 0 ]
+		then
+			continue
+		fi
+
+		# Record the device path and name. This lets the fancontrol
+		# script check that they didn't change. If they did, then the
+		# configuration file can no longer be trusted.
+		path=`DevicePath "$device"`
+		if [ -z "$DEVPATH" ]
+		then
+			DEVPATH="$device=$path"
+		else
+			DEVPATH="$DEVPATH $device=$path"
+		fi
+
+		name=`DeviceName "$device"`
+		if [ -z "$DEVNAME" ]
+		then
+			DEVNAME="$device=$name"
+		else
+			DEVNAME="$DEVNAME $device=$name"
+		fi
+	done
+}
+
+function SaveConfig()
+{
+	RememberDevices "$FCTEMPS" "$FCFANS"
+
+	echo
+	echo "Saving configuration to $FCCONFIG..."
+	tmpfile=`mktemp -t pwmcfg.XXXXXXXXXX` || { echo "$0: Cannot create temporary file" >&2; exit 1;  }
+	trap " [ -f \"$tmpfile\" ] && /bin/rm -f -- \"$tmpfile\"" 0 1 2 3 13 15
+	echo "# Configuration file generated by pwmconfig, changes will be lost" >$tmpfile
+	echo "INTERVAL=$INTERVAL" >>$tmpfile
+	echo "DEVPATH=$DEVPATH" >>$tmpfile
+	echo "DEVNAME=$DEVNAME" >>$tmpfile
+	echo "FCTEMPS=$FCTEMPS" >>$tmpfile
+	echo "FCFANS=$FCFANS" >>$tmpfile
+	echo "MINTEMP=$MINTEMP" >>$tmpfile
+	echo "MAXTEMP=$MAXTEMP" >>$tmpfile
+	echo "MINSTART=$MINSTART" >>$tmpfile
+	echo "MINSTOP=$MINSTOP" >>$tmpfile
+	[ -n "$MINPWM" ] && echo "MINPWM=$MINPWM" >>$tmpfile
+	[ -n "$MAXPWM" ] && echo "MAXPWM=$MAXPWM" >>$tmpfile
+	mv $tmpfile $FCCONFIG
+	chmod +r $FCCONFIG
+	#check if file was written correctly
+	echo 'Configuration saved'
+}
+
+INTERVAL=10
+PS3='select (1-n): '
+DEFMINTEMP=20
+DEFMAXTEMP=60
+DEFMINSTART=150
+DEFMINSTOP=100
+
+function filter_cfgvar()
+{
+	echo "$1" | sed -e 's/ /\n/g' \
+		  | egrep "$2" \
+		  | sed -e 's/.*=//g'
+}
+
+# "select" won't repeat the list of options, so we enclose it in a
+# never-ending loop so that it starts over again with each iteration.
+# I admit it's not exactly nice, but I do not have a better idea to
+# keep usability at an acceptable level.
+while [ 1 ] ; do
+echo
+echo 'Select fan output to configure, or other action:'
+select pwms in $pwmactive "Change INTERVAL" "Just quit" "Save and quit" "Show configuration"; do
+	case $pwms in
+	"Change INTERVAL")
+		echo
+		echo "Current interval is $INTERVAL seconds."
+		echo -n "Enter the interval at which fancontrol should update PWM values (in s): "
+		read INTERVAL #check user input here
+		break ;;
+	"Just quit")
+		exit ;;
+	"Save and quit")
+		SaveConfig
+		exit ;;
+	"Show configuration")
+		echo
+		echo "Common Settings:"
+		echo "INTERVAL=$INTERVAL"
+		for pwmo in $pwmactive
+		do
+			echo
+			echo "Settings of ${pwmo}:"
+			echo "  Depends on `filter_cfgvar "$FCTEMPS" "$pwmo"`"
+			echo "  Controls `filter_cfgvar "$FCFANS" "$pwmo"`"
+			echo "  MINTEMP=`filter_cfgvar "$MINTEMP" $pwmo`"
+			echo "  MAXTEMP=`filter_cfgvar "$MAXTEMP" "$pwmo"`"
+			echo "  MINSTART=`filter_cfgvar "$MINSTART" "$pwmo"`"
+			echo "  MINSTOP=`filter_cfgvar "$MINSTOP" "$pwmo"`"
+			XMINP=`filter_cfgvar "$MINPWM" "$pwmo"`
+			[ -n "$XMINP" ] && echo "  MINPWM=$XMINP"
+			XMAXP=`filter_cfgvar "$MAXPWM" "$pwmo"`
+			[ -n "$XMAXP" ] && echo "  MAXPWM=$XMAXP"
+		done
+		echo
+		break ;;
+
+	"`echo ${pwmactive} |sed -e 's/ /\n/g' | egrep "${pwms}"`" )
+		pwmsed=`echo ${pwms} | sed -e 's/\//\\\\\//g'` #escape / for sed
+		echo
+
+		echo 'Devices:'
+		print_devices ""
+		echo
+
+		echo 'Current temperature readings are as follows:'
+		for j in $TEMPS
+		do
+			S=`cat $j`
+			let S="$S / 1000"
+			echo "$j	$S"
+		done
+		FAN=`echo $fanactive|cut -d' ' -f$REPLY`
+		FAN_MIN=`echo $fanactive_min|cut -d' ' -f$REPLY`
+	       	FCFANS="`echo $FCFANS | sed -e "s/${pwmsed}[^ ]* *//g"` ${pwms}=$FAN"
+		echo
+		echo "Select a temperature sensor as source for ${pwms}:"
+		select tempss in $TEMPS "None (Do not affect this PWM output)"; do
+			if [ "$tempss" = "None (Do not affect this PWM output)" ]
+			then
+
+				break;
+			else
+				if [ "$FCTEMPS" = "" ]
+				then
+					FCTEMPS="${pwms}=${tempss}"
+				else
+					FCTEMPS="`echo $FCTEMPS | sed -e "s/${pwmsed}[^ ]* *//g"` ${pwms}=${tempss}"
+				fi
+			fi
+			echo
+			echo 'Enter the low temperature (degree C)'
+			echo -n "below which the fan should spin at minimum speed ($DEFMINTEMP): "
+			read XMT
+			if [ "$XMT" = "" ]
+			then
+				XMT=$DEFMINTEMP
+			fi
+			if [ "$MINTEMP" = "" ]
+			then
+				MINTEMP="${pwms}=${XMT}"
+			else
+				MINTEMP="`echo $MINTEMP | sed -e "s/${pwmsed}[^ ]* *//g"` ${pwms}=${XMT}"
+			fi
+			echo
+			echo 'Enter the high temperature (degree C)'
+			echo -n "over which the fan should spin at maximum speed ($DEFMAXTEMP): "
+			read XMT
+			if [ "$XMT" = "" ]
+			then
+				XMT=$DEFMAXTEMP
+			fi
+			if [ "$MAXTEMP" = "" ]
+			then
+				MAXTEMP="${pwms}=${XMT}"
+			else
+				MAXTEMP="`echo $MAXTEMP | sed -e "s/${pwmsed}[^ ]* *//g"` ${pwms}=${XMT}"
+			fi
+
+			if [ $FAN_MIN -eq 0 ]
+			then
+				echo
+				echo "Enter the minimum PWM value (0-$MAX)"
+				echo -n "at which the fan STOPS spinning (press t to test) ($DEFMINSTOP): "
+				read XMSTOP
+
+				if [ "$XMSTOP" = "" ]
+				then
+					XMSTOP=$DEFMINSTOP
+				fi
+				if [ "$XMSTOP" = "t" -o "$XMSTOP" = "T" ]
+				then
+					TestMinStop $FAN
+					XMSTOP=$fanval
+				fi
+			else
+				XMSTOP=0
+			fi
+			if [ "$MINSTOP" = "" ]
+			then
+				MINSTOP="${pwms}=${XMSTOP}"
+			else
+				MINSTOP="`echo $MINSTOP | sed -e "s/${pwmsed}[^ ]* *//g"` ${pwms}=${XMSTOP}"
+			fi
+
+			if [ $FAN_MIN -eq 0 ]
+			then
+				echo
+				echo "Enter the minimum PWM value ($XMSTOP-$MAX)"
+				echo -n "at which the fan STARTS spinning (press t to test) ($DEFMINSTART): "
+				read XMSTART
+
+				if [ "$XMSTART" = "" ]
+				then
+					XMSTART=$DEFMINSTART
+				fi
+				if [ "$XMSTART" = "t" -o "$XMSTART" = "T" ]
+				then
+					TestMinStart $XMSTOP
+					XMSTART=$fanval
+				fi
+			else
+				XMSTART=$DEFMINSTART
+			fi
+			if [ "$MINSTART" = "" ]
+			then
+				MINSTART="${pwms}=${XMSTART}"
+			else
+				MINSTART="`echo $MINSTART | sed -e "s/${pwmsed}[^ ]* *//g"` ${pwms}=${XMSTART}"
+			fi
+
+			if [ $XMSTOP -gt 0 ]
+			then
+				echo
+				echo "Enter the PWM value (0-$XMSTOP) to use when the temperature"
+				echo -n "is below the low temperature limit (0): "
+				read XMINP
+			else
+				XMINP=""
+			fi
+			if [ -n "$XMINP" ]
+			then
+				if [ "$MINPWM" = "" ]
+				then
+					MINPWM="${pwms}=${XMINP}"
+				else
+					MINPWM="`echo $MINPWM | sed -e "s/${pwmsed}[^ ]* *//g"` ${pwms}=${XMINP}"
+				fi
+			fi
+			echo
+			echo "Enter the PWM value ($XMSTOP-$MAX) to use when the temperature"
+			echo -n "is over the high temperature limit ($MAX): "
+			read XMAXP
+			if [ -n "$XMAXP" ]
+			then
+				if [ "$MAXPWM" = "" ]
+				then
+					MAXPWM="${pwms}=${XMAXP}"
+				else
+					MAXPWM="`echo $MAXPWM | sed -e "s/${pwmsed}[^ ]* *//g"` ${pwms}=${XMAXP}"
+				fi
+			fi
+			echo
+			break;
+		done
+		break ;;
+
+	*)
+		echo "No such option. Enter a number."
+		break ;;
+	esac
+done
+done
Index: /lm-sensors/tags/V3-3-0/prog/pwm/Module.mk
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/pwm/Module.mk	(revision 5338)
+++ /lm-sensors/tags/V3-3-0/prog/pwm/Module.mk	(revision 5338)
@@ -0,0 +1,47 @@
+#  Module.mk
+#  Copyright (c) 1998, 1999  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.
+
+MODULE_DIR := prog/pwm
+PROGPWMDIR := $(MODULE_DIR)
+
+PROGPWMMAN8DIR := $(MANDIR)/man8
+PROGPWMMAN8FILES := $(MODULE_DIR)/fancontrol.8 $(MODULE_DIR)/pwmconfig.8
+
+PROGPWMTARGETS := $(MODULE_DIR)/fancontrol \
+                  $(MODULE_DIR)/pwmconfig
+
+# The vt1211_pwm script is not installed by default, pass VT1211_PWM=1
+# to get it 
+ifeq ($(VT1211_PWM),1)
+PROGPWMMAN8FILES += $(MODULE_DIR)/vt1211_pwm.8
+PROGPWMTARGETS += $(MODULE_DIR)/vt1211_pwm
+endif
+
+REMOVEPWMBIN := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(SBINDIR)/%,$(PROGPWMTARGETS))
+REMOVEPWMMAN := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(PROGPWMMAN8DIR)/%,$(PROGPWMMAN8FILES))
+
+install-prog-pwm: $(PROGPWMTARGETS)
+	$(MKDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(PROGPWMMAN8DIR)
+	$(INSTALL) -m 755 $(PROGPWMTARGETS) $(DESTDIR)$(SBINDIR)
+	$(INSTALL) -m 644 $(PROGPWMMAN8FILES) $(DESTDIR)$(PROGPWMMAN8DIR)
+
+user_install :: install-prog-pwm
+
+user_uninstall::
+	$(RM) $(REMOVEPWMBIN)
+	$(RM) $(REMOVEPWMMAN)
Index: /lm-sensors/tags/V3-3-0/prog/pwm/fancontrol.8
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/pwm/fancontrol.8	(revision 5770)
+++ /lm-sensors/tags/V3-3-0/prog/pwm/fancontrol.8	(revision 5770)
@@ -0,0 +1,124 @@
+.TH FANCONTROL 8 "September 2009" "lm-sensors 3"
+.SH NAME
+fancontrol \- automated software based fan speed regulation
+
+.SH SYNOPSIS
+.B fancontrol
+.I [configfile]
+
+.SH DESCRIPTION
+\fBfancontrol\fP is a shell script for use with lm_sensors. It reads its
+configuration from a file, then calculates fan speeds from temperatures and
+sets the corresponding PWM outputs to the computed values.
+
+.SH WARNING
+Please be careful when using the fan control features of your mainboard, in
+addition to the risk of burning your CPU, at higher temperatures there will be
+a higher wearout of your other hardware components, too. So if you plan to use
+these components in 50 years, \fBmaybe\fP you shouldn't use fancontrol
+at all.  Also please keep in mind most fans aren't designed to be
+powered by a PWMed voltage.
+
+In practice it doesn't seem to be a major issue, the fans will get slightly
+warmer, just be sure to have a temperature alarm and/or shutdown call, in case
+some fan fails, because you probably won't hear it anymore ;)
+
+.SH CONFIGURATION
+For easy configuration, there's a script
+named \fBpwmconfig\fP(8) which lets you interactively write your
+configuration file for \fBfancontrol\fP. Alternatively you can write this
+file yourself using the information from this manpage.
+
+Since most of you are going to use \fBpwmconfig\fP(8) script, the config
+file syntax will be discussed last. First I'm going to describe the various
+variables available for changing \fBfancontrol\fP's behaviour:
+
+.TP
+.B INTERVAL
+This variable defines at which interval in seconds the main loop of
+\fBfancontrol\fP will be executed
+.TP
+.B DEVPATH
+Maps hwmon class devices to physical devices. This lets \fBfancontrol\fP
+check that the configuration file is still up-to-date.
+.TP
+.B DEVNAME
+Records hwmon class device names. This lets \fBfancontrol\fP check that
+the configuration file is still up-to-date.
+.TP
+.B FCTEMPS
+Maps PWM outputs to temperature sensors so \fBfancontrol\fP knows which
+temperature sensors should be used for calculation of new values for
+the corresponding PWM outputs.
+.TP
+.B FCFANS
+Records the association between a PWM output and a fan input.
+Then \fBfancontrol\fP can check the fan speed and restart it if it
+stops unexpectedly.
+.TP
+.B MINTEMP
+The temperature below which the fan gets switched to minimum speed.
+.TP
+.B MAXTEMP
+The temperature over which the fan gets switched to maximum speed.
+.TP
+.B MINSTART
+Sets the minimum speed at which the fan begins spinning. You should
+use a safe value to be sure it works, even when the fan gets old.
+.TP
+.B MINSTOP
+The minimum speed at which the fan still spins. Use a safe value here,
+too.
+.TP
+.B MINPWM
+The PWM value to use when the temperature is below MINTEMP.
+Typically, this will be either 0 if it is OK for the fan to plain
+stop, or the same value as MINSTOP if you don't want the fan to
+ever stop.
+If this value isn't defined, it defaults to 0 (stopped fan).
+.TP
+.B MAXPWM
+The PWM value to use when the temperature is over MAXTEMP.
+If this value isn't defined, it defaults to 255 (full speed).
+.PP
+The configuration file format is a bit strange:
+.IP
+.nf
+VARIABLE=chip/pwmdev=value chip/pwmdev2=value2
+VARIABLE2=...
+.fi
+.PP
+Each variable has its own line. The variable name is followed by an equal sign
+and the device=value pairs. These consist of the path to the pwm output for
+which the value is valid, equal sign followed by the value and are separated
+by a blank. Path can be absolute or relative (from /sys/bus/i2c/devices or
+/sys/class/hwmon depending on the kernel version). Example:
+.IP
+MINTEMP=hwmon0/device/pwm1=40 hwmon0/device/pwm2=54
+.PP
+You have to play with the temperature values a bit to get happy. For initial
+setup I recommend using the \fBpwmconfig\fP script. Small changes can be made by
+editing the config file directly following the rules above.
+
+Upon starting, fancontrol will make sure that all referenced devices
+do exist and match what they were at configuration time, and that all
+referenced sysfs files do exist. If not, it will quit immediately, upon
+the assumption that the configuration file may be out-of-sync with the
+loaded kernel drivers.
+
+.SH THE ALGORITHM
+
+\fBfancontrol\fP first reads its configuration, writes it to arrays and loops its
+main function.  This function gets the temperatures and fanspeeds from
+kernel driver files and calculates new speeds depending on temperature
+changes, but only if the temp is between MINTEMP and MAXTEMP. After that, the
+new values are written to the PWM outputs. Currently the speed increases
+quadratically with rising temperature. This way you won't hear your fans most
+of the time at best.
+
+.SH SEE ALSO
+pwmconfig(8), sensors(1).
+
+.SH AUTHOR
+.PP
+Marius Reiner <marius.reiner@hdev.de>
Index: /lm-sensors/tags/V3-3-0/prog/dump/isadump.c
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/dump/isadump.c	(revision 5709)
+++ /lm-sensors/tags/V3-3-0/prog/dump/isadump.c	(revision 5709)
@@ -0,0 +1,317 @@
+/*
+    isadump.c - isadump, a user-space program to dump ISA registers
+    Copyright (C) 2000  Frodo Looijaard <frodol@dds.nl>, and
+                        Mark D. Studebaker <mdsxyz123@yahoo.com>
+    Copyright (C) 2004,2007  Jean Delvare <khali@linux-fr.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.
+
+    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.
+*/
+
+/*
+	Typical usage:
+	isadump 0x295 0x296		Basic winbond dump using address/data registers
+	isadump 0x295 0x296 2		Winbond dump, bank 2
+	isadump 0x2e 0x2f 0x09		Super-I/O, logical device 9
+	isadump -f 0x5000		Flat address space dump like for Via 686a
+	isadump -f 0xecf0 0x10 1	PC87366, temperature channel 2
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "util.h"
+#include "superio.h"
+
+
+/* To keep glibc2 happy */
+#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ >= 0
+#include <sys/io.h>
+#else
+#include <asm/io.h>
+#endif
+
+#ifdef __powerpc__
+unsigned long isa_io_base = 0; /* XXX for now */
+#endif /* __powerpc__ */
+
+static void help(void)
+{
+	fprintf(stderr,
+	        "Syntax for I2C-like access:\n"
+	        "  isadump [-y] [-k V1,V2...] ADDRREG DATAREG [BANK [BANKREG]]\n"
+	        "Syntax for flat address space:\n"
+	        "  isadump [-y] -f ADDRESS [RANGE [BANK [BANKREG]]]\n");
+}
+
+static int default_bankreg(int flat, int addrreg, int datareg)
+{
+	if (flat) {
+		return 0x09; /* Works for National Semiconductor
+		                Super-IO chips */
+	}
+
+	if ((addrreg == 0x2e && datareg == 0x2f)
+	 || (addrreg == 0x4e && datareg == 0x4f)) {
+		return 0x07; /* Works for all Super-I/O chips */
+	}
+	
+	return 0x4e; /* Works for Winbond ISA chips, default */
+}
+
+static int set_bank(int flat, int addrreg, int datareg, int bank, int bankreg)
+{
+	int oldbank;
+
+	if (flat) {
+		oldbank = inb(addrreg+bankreg);
+		outb(bank, addrreg+bankreg);
+	} else {
+		outb(bankreg, addrreg);
+		oldbank = inb(datareg);
+		outb(bank, datareg);
+	}
+
+	return oldbank;
+}
+
+int main(int argc, char *argv[])
+{
+	int addrreg;        /* address in flat mode */
+	int datareg = 0;    /* unused in flat mode */
+	int range = 256;    /* can be changed only in flat mode */
+	int bank = -1;      /* -1 means no bank operation */
+	int bankreg;
+	int oldbank = 0;
+	int i, j, res;
+	int flags = 0;
+	int flat = 0, yes = 0;
+	char *end;
+	unsigned char enter_key[SUPERIO_MAX_KEY+1];
+
+	enter_key[0] = 0;
+
+	/* handle (optional) flags first */
+	while (1+flags < argc && argv[1+flags][0] == '-') {
+		switch (argv[1+flags][1]) {
+		case 'f': flat = 1; break;
+		case 'y': yes = 1; break;
+		case 'k':
+			if (2+flags >= argc
+			 || superio_parse_key(enter_key, argv[2+flags]) < 0) {
+				fprintf(stderr, "Invalid or missing key\n");
+				help();
+				exit(1);
+			}
+			flags++;
+			break;
+		default:
+			fprintf(stderr, "Warning: Unsupported flag "
+				"\"-%c\"!\n", argv[1+flags][1]);
+			help();
+			exit(1);
+		}
+		flags++;
+	}
+
+	/* key is never needed in flat mode */
+	if (flat && enter_key[0]) {
+		fprintf(stderr, "Error: Cannot use key in flat mode\n");
+		exit(1);
+	}
+
+	/* verify that the argument count is correct */
+	if ((!flat && argc < 1+flags+2)
+	 || (flat && argc < 1+flags+1)) {
+		help();
+		exit(1);
+	}
+
+	addrreg = strtol(argv[1+flags], &end, 0);
+	if (*end) {
+		fprintf(stderr, "Error: Invalid address!\n");
+		help();
+		exit(1);
+	}
+	if (addrreg < 0 || addrreg > (flat?0xffff:0x3fff)) {
+		fprintf(stderr, "Error: Address out of range "
+		        "(0x0000-0x%04x)!\n", flat?0xffff:0x3fff);
+		help();
+		exit(1);
+	}
+
+	if (flat) {
+		if (1+flags+1 < argc) {
+			range = strtol(argv[1+flags+1], &end, 0);
+			if (*end || range <= 0 || range > 0x100
+			 || range & 0xf) {
+				fprintf(stderr, "Error: Invalid range!\n"
+				        "Hint: Must be a multiple of 16 no "
+				        "greater than 256.\n");
+				help();
+				exit(1);
+			}
+		} else {
+			addrreg &= 0xff00; /* Force alignment */
+		}
+	} else {
+		datareg = strtol(argv[1+flags+1], &end, 0);
+		if (*end) {
+			fprintf(stderr, "Error: Invalid data register!\n");
+			help();
+			exit(1);
+		}
+		if (datareg < 0 || datareg > 0x3fff) {
+			fprintf(stderr, "Error: Data register out of range "
+			        "(0x0000-0x3fff)!\n");
+			help();
+			exit(1);
+		}
+	}
+
+	bankreg = default_bankreg(flat, addrreg, datareg);
+
+	if (1+flags+2 < argc) {
+		bank = strtol(argv[1+flags+2], &end, 0);
+		if (*end) {
+			fprintf(stderr, "Error: Invalid bank number!\n");
+			help();
+			exit(1);
+		}
+		if ((bank < 0) || (bank > 31)) {
+			fprintf(stderr, "Error: bank out of range (0-31)!\n");
+			help();
+			exit(1);
+		}
+
+		if (1+flags+3 < argc) {
+			bankreg = strtol(argv[1+flags+3], &end, 0);
+			if (*end) {
+				fprintf(stderr, "Error: Invalid bank "
+				        "register!\n");
+				help();
+				exit(1);
+			}
+			if (bankreg < 0 || bankreg >= range) {
+				fprintf(stderr, "Error: bank out of range "
+				        "(0x00-0x%02x)!\n", range-1);
+				help();
+				exit(1);
+			}
+		}
+	}
+
+	if (geteuid()) {
+		fprintf(stderr, "Error: Can only be run as root (or make it "
+		        "suid root)\n");
+		exit(1);
+	}
+
+	if (!yes) {
+		fprintf(stderr, "WARNING! Running this program can cause "
+		        "system crashes, data loss and worse!\n");
+
+		if (flat)
+			fprintf(stderr, "I will probe address range 0x%x to "
+			        "0x%x.\n", addrreg, addrreg + range - 1);
+		else
+			fprintf(stderr, "I will probe address register 0x%x "
+			        "and data register 0x%x.\n", addrreg, datareg);
+
+		if (bank>=0) 	
+			fprintf(stderr, "Probing bank %d using bank register "
+			        "0x%02x.\n", bank, bankreg);
+
+		fprintf(stderr, "Continue? [Y/n] ");
+		fflush(stderr);
+		if (!user_ack(1)) {
+			fprintf(stderr, "Aborting on user request.\n");
+			exit(0);
+		}
+	}
+
+#ifndef __powerpc__
+	if ((datareg < 0x400) && (addrreg < 0x400) && !flat) {
+		if (ioperm(datareg, 1, 1)) {
+			fprintf(stderr, "Error: Could not ioperm() data "
+			        "register!\n");
+			exit(1);
+		}
+		if (ioperm(addrreg, 1, 1)) {
+			fprintf(stderr, "Error: Could not ioperm() address "
+			        "register!\n");
+			exit(1);
+		}
+	} else {
+		if (iopl(3)) {
+			fprintf(stderr, "Error: Could not do iopl(3)!\n");
+			exit(1);
+		}
+	}
+#endif
+
+	/* Enter Super-I/O configuration mode */
+	if (enter_key[0])
+		superio_write_key(addrreg, enter_key);
+
+	if (bank >= 0)
+		oldbank = set_bank(flat, addrreg, datareg, bank, bankreg);
+
+	if (flat)
+		printf("  ");
+	printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f\n");
+	for (i = 0; i < range; i += 16) {
+		if (flat)
+			printf("%04x: ", addrreg + i);
+		else
+			printf("%02x: ", i);
+
+		/* It was noticed that Winbond Super-I/O chips
+		   would leave the configuration mode after
+		   an arbitrary number of register reads,
+		   causing any subsequent read attempt to
+		   silently fail. Repeating the key every 16 reads
+		   prevents that. */
+		if (enter_key[0])
+			superio_write_key(addrreg, enter_key);
+
+		for (j = 0; j < 16; j++) {
+			fflush(stdout);
+			if (flat) {
+				res = inb(addrreg + i + j);
+			} else {	
+				outb(i+j, addrreg);
+				if (i+j == 0 && inb(addrreg) == 0x80) {
+					/* Bit 7 appears to be a busy flag */
+					range = 128;
+				}
+				res = inb(datareg);
+			}
+			printf("%02x ", res);
+		}
+		printf("\n");
+	}
+
+	/* Restore the original bank value */
+	if (bank >= 0)
+		set_bank(flat, addrreg, datareg, oldbank, bankreg);
+
+	/* Exit Super-I/O configuration mode */
+	if (enter_key[0])
+		superio_reset(addrreg, datareg);
+
+	exit(0);
+}
Index: /lm-sensors/tags/V3-3-0/prog/dump/util.h
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/dump/util.h	(revision 4670)
+++ /lm-sensors/tags/V3-3-0/prog/dump/util.h	(revision 4670)
@@ -0,0 +1,16 @@
+/*
+    util - helper functions
+    Copyright (C) 2006 Jean Delvare <khali@linux-fr.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 _UTIL_H
+#define _UTIL_H
+
+extern int user_ack(int def);
+
+#endif /* _UTIL_H */
Index: /lm-sensors/tags/V3-3-0/prog/dump/superio.c
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/dump/superio.c	(revision 5163)
+++ /lm-sensors/tags/V3-3-0/prog/dump/superio.c	(revision 5163)
@@ -0,0 +1,76 @@
+/*
+    superio: Handle special I/O operations needed by most Super-I/O chips
+   
+    Copyright (C) 2005  Jean Delvare <khali@linux-fr.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.
+
+    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 <stdlib.h>
+
+#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ >= 0
+#include <sys/io.h>
+#else
+#include <asm/io.h>
+#endif
+
+#include "superio.h"
+
+int superio_parse_key(unsigned char *key, const char *s)
+{
+	char *end;
+	int tmp;
+	key[0] = 0;
+	
+	while (*s != '\0') {
+		tmp = strtol(s, &end, 0);
+		if ((*end != '\0' && *end != ',')
+		 || (tmp < 0x00 || tmp > 0xff))
+			return -1;
+
+		/* Byte is valid, store it */
+		key[++key[0]] = tmp;
+
+		/* Last byte? */
+		if (key[0] == SUPERIO_MAX_KEY
+		 || *end == '\0')
+			return 0;
+
+		/* Skip comma */
+		s = end + 1;
+	}
+	
+	/* Unexpected end of string */
+	return -1;
+}
+
+void superio_write_key(int addrreg, unsigned char *key)
+{
+	int i;
+
+	for (i = 1; i <= key[0]; i++)
+		outb(key[i], addrreg);
+}
+
+void superio_reset(int addrreg, int datareg)
+{
+	/* Some chips (SMSC, Winbond) want this */
+	outb(0xaa, addrreg);
+
+	/* Return to "Wait For Key" state (PNP-ISA spec) */
+	outb(0x02, addrreg);
+	outb(0x02, datareg);
+}
Index: /lm-sensors/tags/V3-3-0/prog/dump/isadump.8
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/dump/isadump.8	(revision 4471)
+++ /lm-sensors/tags/V3-3-0/prog/dump/isadump.8	(revision 4471)
@@ -0,0 +1,105 @@
+.TH ISADUMP 8 "August 2004"
+.SH NAME
+isadump \- examine ISA registers
+
+.SH SYNOPSIS
+.B isadump
+.RB [ -y ]
+.RB [ "-k V1,V2..." ]
+.I addrreg
+.I datareg
+.RI [ "bank " [ bankreg ]]
+#for I2C-like access
+.br
+.B isadump
+.RB [ -y ]
+.BI "-f " address
+.RI [ "range " [ "bank " [ bankreg ]]]
+#for flat address space
+
+.SH DESCRIPTION
+isadump is a small helper program to examine registers visible through the ISA
+bus. It is intended to probe any chip that lives on the ISA bus working with an
+address register and a data register (I2C-like access) or a flat range (of up
+to 256 bytes).
+
+.SH OPTIONS
+.TP
+.B -f
+Enable flat address space mode.
+.TP
+.B -y
+Disable interactive mode. By default, isadump will wait for a confirmation
+from the user before messing with the ISA bus. When this flag is used, it
+will perform the operation directly. This is mainly meant to be used in
+scripts.
+.TP
+.B -k V1,V2...
+Specify a comma-separated list of bytes to send as the key sequence to enter
+the chip configuration mode. Most Super-I/O chips need this.
+Known key sequences are: 0x87,0x01,0x55,0x55 for ITE, 0x55 for SMSC, 0x87,0x87
+for Winbond and VIA, none needed for National Semiconductor.
+
+.SH OPTIONS (I2C-like access mode)
+At least two options must be provided to isadump. \fIaddrreg\fR contains the
+ISA address of the address register for the chip to probe; \fIdatareg\fR
+contains the address of the data register. Both addresses are integers between
+0x0000 and 0x3FFF. Usually, if the chip's base address is 0x0nn0, the
+address register is at 0x0nn5 and the data register is at 0x0nn6. The most
+common base address for hardware monitoring chips is 0x0290.
+.PP
+For Super-I/O chips, address register is typically at 0x2E with data
+register at 0x2F.
+.PP
+The \fIbank\fR and \fIbankreg\fR parameters are useful on the Winbond chips
+as well as on Super-I/O chips.
+\fIbank\fR is an integer between 0 and 31, and \fIbankreg\fR is an integer
+between 0x00 and 0xFF (default value: 0x4E for Winbond chips, 0x07
+for Super-I/O chips). The W83781D datasheet has more information on bank
+selection.
+
+.SH OPTIONS (flat address space mode)
+In flat mode, only one parameter is
+mandatory. \fIaddress\fR contains the ISA address of the chip to probe;
+it is an integer between 0x0000 and 0xFFFF.
+If provided, \fIrange\fR is how many bytes should be read (must be a
+multiple of 16). If the range isn't provided, it defaults to 256 bytes
+and the address is forcibly aligned on a 256-byte boundary.
+.PP
+The \fIbank\fR and \fIbankreg\fR parameters are useful on the National
+Semiconductor PC87365 and PC87366 Super-I/O chips.
+\fIbank\fR is an integer between 0 and 31, and \fIbankreg\fR is an integer
+between 0x00 and 0xFF (default value: 0x09; must fit in the specified
+range). See the PC87365 datasheet for more information on bank selection.
+
+.SH NOTES
+If no bank is specified, no bank change operation is performed.
+.PP
+If a bank is specified, the original value is restored before isadump exits.
+.PP
+Dumping Super-I/O chips is typically a two-step process. First, you will have
+to access the main Super-I/O address using a command like:
+isadump 0x2e 0x2f 0x09.
+This will select logical device 9 (correct value depend on the chip). At 0x60
+you will find the logical device address word, for example "ec c0".
+Then you can use a command like:
+isadump -f 0xecc0 16.
+This will dump the logical device registers. The correct range depends on
+the chip.
+
+.SH WARNING
+Poking around in ISA data space is extremely dangerous.
+Running isadump with random parameters can cause system
+crashes, data loss, and worse!  Be extremely careful when using
+this program.
+
+.SH SEE ALSO
+i2cdump(8), isaset(8)
+
+.SH AUTHOR
+Frodo Looijaard, Mark D. Studebaker, and the lm_sensors group
+(http://www.lm-sensors.org/)
+.PP
+This manual page was originally written by David Z Maze <dmaze@debian.org> for
+the Debian GNU/Linux system. It was then reviewed and augmented by the lm_sensors
+team and is now part of the lm_sensors source distribution.
Index: /lm-sensors/tags/V3-3-0/prog/dump/isaset.c
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/dump/isaset.c	(revision 5709)
+++ /lm-sensors/tags/V3-3-0/prog/dump/isaset.c	(revision 5709)
@@ -0,0 +1,261 @@
+/*
+    isaset.c - isaset, a user-space program to write ISA registers
+    Copyright (C) 2000  Frodo Looijaard <frodol@dds.nl>, and
+                        Mark D. Studebaker <mdsxyz123@yahoo.com>
+    Copyright (C) 2004,2007  Jean Delvare <khali@linux-fr.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.
+
+    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.
+*/
+
+/*
+	Typical usage:
+	isaset 0x295 0x296 0x10 0x12	Write 0x12 to address 0x10 using address/data registers
+	isaset -f 0x5010 0x12		Write 0x12 to location 0x5010
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "util.h"
+
+
+/* To keep glibc2 happy */
+#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ >= 0
+#include <sys/io.h>
+#else
+#include <asm/io.h>
+#endif
+
+#ifdef __powerpc__
+unsigned long isa_io_base = 0; /* XXX for now */
+#endif /* __powerpc__ */
+
+static void help(void)
+{
+	fprintf(stderr,
+	        "Syntax for I2C-like access:\n"
+	        "  isaset [-y] ADDRREG DATAREG ADDRESS VALUE [MASK]\n"
+	        "Syntax for flat address space:\n"
+	        "  isaset [-y] -f ADDRESS VALUE [MASK]\n");
+}
+
+int main(int argc, char *argv[])
+{
+	int addrreg, datareg = 0, value, addr = 0, vmask = 0;
+	unsigned char res;
+	int flags = 0;
+	int flat = 0, yes = 0;
+	char *end;
+
+	/* handle (optional) flags first */
+	while (1+flags < argc && argv[1+flags][0] == '-') {
+		switch (argv[1+flags][1]) {
+		case 'f': flat = 1; break;
+		case 'y': yes = 1; break;
+		default:
+			fprintf(stderr, "Warning: Unsupported flag "
+				"\"-%c\"!\n", argv[1+flags][1]);
+			help();
+			exit(1);
+		}
+		flags++;
+	}
+
+	/* verify that the argument count is correct */
+	if ((!flat && (argc < 1+flags+4 || argc > 1+flags+5))
+	 || (flat && (argc < 1+flags+2 || argc > 1+flags+3))) {
+		help();
+		exit(1);
+	}
+
+	addrreg = strtol(argv[1+flags], &end, 0);
+	if (*end) {
+		fprintf(stderr, "Error: Invalid address!\n");
+		help();
+		exit(1);
+	}
+	if (addrreg < 0 || addrreg > (flat?0xffff:0x3fff)) {
+		fprintf(stderr,
+		        "Error: Address out of range (0x0000-0x%04x)!\n",
+			flat?0xffff:0x3fff);
+		help();
+		exit(1);
+	}
+
+	if (!flat) {
+		datareg = strtol(argv[1+flags+1], &end, 0);
+		if (*end) {
+			fprintf(stderr, "Error: Invalid data register!\n");
+			help();
+			exit(1);
+		}
+		if (datareg < 0 || datareg > 0x3fff) {
+			fprintf(stderr, "Error: Data register out of range "
+			        "(0x0000-0x3fff)!\n");
+			help();
+			exit(1);
+		}
+
+		addr = strtol(argv[1+flags+2], &end, 0);
+		if (*end) {
+			fprintf(stderr, "Error: Invalid address!\n");
+			help();
+			exit(1);
+		}
+		if (addr < 0 || addr > 0xff) {
+			fprintf(stderr, "Error: Address out of range "
+			        "(0x00-0xff)!\n");
+			help();
+			exit(1);
+		}
+	}
+
+	/* rest is the same for both modes so we cheat on flags */
+	if (!flat)
+		flags += 2;
+
+	value = strtol(argv[flags+2], &end, 0);
+	if (*end) {
+		fprintf(stderr, "Error: Invalid value!\n");
+		help();
+		exit(1);
+	}
+	if (value < 0 || value > 0xff) {
+		fprintf(stderr, "Error: Value out of range "
+			"(0x00-0xff)!\n");
+		help();
+		exit(1);
+	}
+
+	if (flags+3 < argc) {
+		vmask = strtol(argv[flags+3], &end, 0);
+		if (*end) {
+			fprintf(stderr, "Error: Invalid mask!\n");
+			help();
+			exit(1);
+		}
+		if (vmask < 0 || vmask > 0xff) {
+			fprintf(stderr, "Error: Mask out of range "
+				"(0x00-0xff)!\n");
+			help();
+			exit(1);
+		}
+	}
+
+	if (geteuid()) {
+		fprintf(stderr, "Error: Can only be run as root "
+		        "(or make it suid root)\n");
+		exit(1);
+	}
+
+	if (!yes) {
+		fprintf(stderr, "WARNING! Running this program can cause "
+		        "system crashes, data loss and worse!\n");
+
+		if (flat)
+			fprintf(stderr, "I will write value 0x%02x%s to address "
+		                "0x%x.\n", value, vmask ? " (masked)" : "",
+			        addrreg);
+		else
+			fprintf(stderr, "I will write value 0x%02x%s to address "
+		                "0x%02x of chip with address register 0x%x\n"
+		                "and data register 0x%x.\n",
+		                value, vmask ? " (masked)" : "", addr,
+			        addrreg, datareg);
+
+		fprintf(stderr, "Continue? [Y/n] ");
+		fflush(stderr);
+		if (!user_ack(1)) {
+			fprintf(stderr, "Aborting on user request.\n");
+			exit(0);
+		}
+	}
+
+#ifndef __powerpc__
+	if (!flat && datareg < 0x400 && addrreg < 0x400) {
+		if (ioperm(datareg, 1, 1)) {
+			fprintf(stderr, "Error: Could not ioperm() data "
+			        "register!\n");
+			exit(1);
+		}
+		if (ioperm(addrreg, 1, 1)) {
+			fprintf(stderr, "Error: Could not ioperm() address "
+		        	"register!\n");
+			exit(1);
+		}
+	} else {
+		if (iopl(3)) {
+			fprintf(stderr, "Error: Could not do iopl(3)!\n");
+			exit(1);
+		}
+	}
+#endif
+
+	if (vmask) {
+		int oldvalue;
+
+		if (flat) {
+			oldvalue = inb(addrreg);
+		} else {	
+			outb(addr, addrreg);
+			oldvalue = inb(datareg);
+		}
+
+		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%02x, write mask "
+				"0x%02x: Will write 0x%02x to %s "
+				"0x%02x\n", oldvalue, vmask, value,
+				flat ? "address" : "register",
+				flat ? addrreg : addr);
+
+			fprintf(stderr, "Continue? [Y/n] ");
+			fflush(stderr);
+			if (!user_ack(1)) {
+				fprintf(stderr, "Aborting on user request.\n");
+				exit(0);
+			}
+		}
+	}
+
+	/* do the real thing */
+	if (flat) {
+		/* write */
+		outb(value, addrreg);
+		/* readback */
+		res = inb(addrreg);
+	} else {	
+		/* write */
+		outb(addr, addrreg);
+		outb(value, datareg);
+		/* readback */
+		res = inb(datareg);
+	}
+
+	if (res != value) {
+		fprintf(stderr, "Data mismatch, wrote 0x%02x, "
+		        "read 0x%02x back.\n", value, res);
+	}
+
+	exit(0);
+}
Index: /lm-sensors/tags/V3-3-0/prog/dump/Module.mk
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/dump/Module.mk	(revision 5163)
+++ /lm-sensors/tags/V3-3-0/prog/dump/Module.mk	(revision 5163)
@@ -0,0 +1,63 @@
+#  Module.mk - Makefile for a Linux module for reading sensor data.
+#  Copyright (c) 1998, 1999  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.
+
+# Note that MODULE_DIR (the directory in which this file resides) is a
+# 'simply expanded variable'. That means that its value is substituted
+# verbatim in the rules, until it is redefined. 
+MODULE_DIR := prog/dump
+PROGDUMPDIR := $(MODULE_DIR)
+
+PROGDUMPMAN8DIR := $(MANDIR)/man8
+PROGDUMPMAN8FILES := $(MODULE_DIR)/isadump.8 $(MODULE_DIR)/isaset.8
+
+# Regrettably, even 'simply expanded variables' will not put their currently
+# defined value verbatim into the command-list of rules...
+PROGDUMPTARGETS := $(MODULE_DIR)/isadump $(MODULE_DIR)/isaset
+PROGDUMPSOURCES := $(MODULE_DIR)/util.c $(MODULE_DIR)/isadump.c \
+		   $(MODULE_DIR)/isaset.c $(MODULE_DIR)/superio.c
+PROGDUMPBININSTALL := $(MODULE_DIR)/isadump $(MODULE_DIR)/isaset
+
+# Include all dependency files. We use '.rd' to indicate this will create
+# executables.
+INCLUDEFILES += $(PROGDUMPSOURCES:.c=.rd)
+
+REMOVEDUMPBIN := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(SBINDIR)/%,$(PROGDUMPBININSTALL))
+REMOVEDUMPMAN := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(PROGDUMPMAN8DIR)/%,$(PROGDUMPMAN8FILES))
+
+all-prog-dump: $(PROGDUMPTARGETS)
+user :: all-prog-dump
+
+$(MODULE_DIR)/isadump: $(MODULE_DIR)/isadump.ro $(MODULE_DIR)/superio.ro $(MODULE_DIR)/util.ro
+	$(CC) $(EXLDFLAGS) -o $@ $^
+
+$(MODULE_DIR)/isaset: $(MODULE_DIR)/isaset.ro $(MODULE_DIR)/util.ro
+	$(CC) $(EXLDFLAGS) -o $@ $^
+
+install-prog-dump: all-prog-dump
+	$(MKDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(PROGDUMPMAN8DIR)
+	$(INSTALL) -m 755 $(PROGDUMPBININSTALL) $(DESTDIR)$(SBINDIR)
+	$(INSTALL) -m 644 $(PROGDUMPMAN8FILES) $(DESTDIR)$(PROGDUMPMAN8DIR)
+user_install :: install-prog-dump
+
+user_uninstall::
+	$(RM) $(REMOVEDUMPBIN)
+	$(RM) $(REMOVEDUMPMAN)
+
+clean-prog-dump:
+	$(RM) $(PROGDUMPDIR)/*.rd $(PROGDUMPDIR)/*.ro $(PROGDUMPTARGETS)
+clean :: clean-prog-dump
Index: /lm-sensors/tags/V3-3-0/prog/dump/superio.h
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/dump/superio.h	(revision 5163)
+++ /lm-sensors/tags/V3-3-0/prog/dump/superio.h	(revision 5163)
@@ -0,0 +1,31 @@
+/*
+    superio: Handle special I/O operations needed by most Super-I/O chips
+
+    Copyright (C) 2005  Jean Delvare <khali@linux-fr.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.
+
+    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 _SUPERIO_H
+#define _SUPERIO_H
+
+#define SUPERIO_MAX_KEY	8
+
+int superio_parse_key(unsigned char *key, const char *s);
+void superio_write_key(int addrreg, unsigned char *key);
+void superio_reset(int addrreg, int datareg);
+
+#endif /* _SUPERIO_H */
Index: /lm-sensors/tags/V3-3-0/prog/dump/isaset.8
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/dump/isaset.8	(revision 4471)
+++ /lm-sensors/tags/V3-3-0/prog/dump/isaset.8	(revision 4471)
@@ -0,0 +1,74 @@
+.TH ISASET 8 "May 2005"
+.SH "NAME"
+isaset \- set ISA registers
+
+.SH SYNOPSIS
+.B isaset
+.RB [ -y ]
+.I addrreg
+.I datareg
+.I address
+.I value
+.RI [ mask ]
+#for I2C-like access
+.br
+.B isaset
+.RB [ -y ]
+.BI "-f " address
+.I value
+.RI [ mask ]
+#for flat address space
+
+.SH DESCRIPTION
+isaset is a small helper program to set registers visible through the ISA
+bus.
+
+.SH OPTIONS
+.TP
+.B -f
+Enable flat address space mode.
+.TP
+.B -y
+Disable interactive mode. By default, isaset will wait for a confirmation
+from the user before messing with the ISA bus. When this flag is used, it
+will perform the operation directly. This is mainly meant to be used in
+scripts.
+
+.SH OPTIONS (I2C-like access mode)
+Four options must be provided to isaset. \fIaddrreg\fR contains the
+ISA address of the address register for the chip to probe; \fIdatareg\fR
+contains the address of the data register. Both addresses are integers between
+0x0000 and 0x3FFF. Usually, if the chip's base address is 0x0nn0, the
+address register is at 0x0nn5 and the data register is at 0x0nn6. The most
+common base address for hardware monitoring chips is 0x0290.
+For Super-I/O chips, address register is typically at 0x2E with data
+register at 0x2F.
+The \fIaddress\fR and \fIvalue\fR parameters are two integers between
+0x00 and 0xFF. isaset will write value \fIvalue\fR to address \fIaddress\fR.
+An optional \fImask\fR can be provided as a fifth parameter, preserving
+unmasked bits at the written location.
+
+.SH OPTIONS (flat address space mode)
+In flat mode, two parameters must
+be provided. \fIaddress\fR contains the ISA address of the register to
+write to; it is an integer between 0x0000 and 0xFFFF. Basically, it is
+the sum of the chip's base address and the chip register's address. isaset
+will write value \fIvalue\fR at this address.
+An optional \fImask\fR can be provided as a third parameter, preserving
+unmasked bits at the written location.
+
+.SH WARNING
+Poking around in ISA data space is extremely dangerous.
+Running isaset with random parameters can cause system
+crashes, data loss, and worse!  Be extremely careful when using
+this program.
+
+.SH SEE ALSO
+i2cset(8), isadump(8)
+
+.SH AUTHOR
+Mark D. Studebaker, and the lm_sensors group
+(http://www.lm-sensors.org/)
+.PP
+This manual page was shamelessly ripped from the i2cset and isadump manual
+pages by Jean Delvare.
Index: /lm-sensors/tags/V3-3-0/prog/dump/util.c
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/dump/util.c	(revision 4670)
+++ /lm-sensors/tags/V3-3-0/prog/dump/util.c	(revision 4670)
@@ -0,0 +1,48 @@
+/*
+    util.c - helper functions
+    Copyright (C) 2006 Jean Delvare <khali@linux-fr.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 "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;
+}
+
Index: /lm-sensors/tags/V3-3-0/prog/tellerstats/tellerstats.sh
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/tellerstats/tellerstats.sh	(revision 5163)
+++ /lm-sensors/tags/V3-3-0/prog/tellerstats/tellerstats.sh	(revision 5163)
@@ -0,0 +1,166 @@
+#!/bin/bash
+#
+#    tellerstats.sh                  3
+#	generate graphs from the data
+#
+#    Copyright (C) 2001  Philip Edelbrock
+#
+#    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.
+#
+
+# generic tellerstats init BEGIN
+
+# get config information from /etc/tellerstats.conf or whereever we are pointed
+
+if [ -z "$TELLERSTATS_CONF" ]
+then
+   TELLERSTATS_CONF=/etc/tellerstats.conf
+fi   
+
+export TELLERSTATS_CONF
+
+if [ ! -r $TELLERSTATS_CONF ]
+then
+   echo "$0: Could not find config file $TELLERSTATS_CONF"
+   exit 1
+fi   
+
+. $TELLERSTATS_CONF
+
+if [ ! -d $DBPATH ]
+then
+   echo "$0: data directory $DBPATH does not exist"
+   exit 1
+fi
+
+if [ ! -d $SENSORPATH ]
+then
+   echo "$0: sensor information directory $SENSORPATH does not exist."
+   exit 1
+fi
+
+if [ ! -d $HTMLROOT ]
+then
+   echo "$0: The root of your webserver - $HTMLROOT - does not exist..bailing out"
+   exit 1
+fi
+
+if [ ! -d $HTMLPATH ]
+then
+   echo "$0: The place where we keep HTML files and pictures - $HTMLPATH - does not exist..bailing out"
+   exit 1
+fi
+
+if [ ! -r $GNUPLOTSCRIPT_TMPL ]
+then
+   echo "$0: The gnuplot script template $GNUPLOTSCRIPT_TMPL does not exist..bailing out"
+   exit 1
+fi
+
+export DBPATH SENSORPATH TEMPPATH HTMLROOT HTMLPATH GNUPLOTSCRIPT_TMPL
+
+if [ -n "$DEBUG" ]
+then
+   echo "DBPATH = $DBPATH"
+   echo "SENSORPATH = $SENSORPATH"
+   echo "TEMPPATH = $TEMPPATH"
+   echo "HTMLROOT = $HTMLROOT"
+   echo "HTMLPATH = $HTMLPATH"
+   echo "GNUPLOTSCRIPT_TMPL = $GNUPLOTSCRIPT_TMPL"
+fi
+
+# generic tellerstats init END
+
+if [ -z "$LINEWIDTH" ]
+then
+   LINEWIDTH=5
+fi
+export LINEWIDTH   
+
+if [ -z "$PLOTFORMAT" ]
+then
+   PLOTFORMAT=ps
+fi
+export PLOTFORMAT
+
+if [ -z "$PLOTTERMINAL" ]
+then
+   PLOTTERMINAL="postscript eps enhanced color \"Helvetica\" 22"
+fi
+export PLOTTERMINAL
+
+if [ -n "$DEBUG" ]
+then
+   echo "LINEWIDTH = $LINEWIDTH"
+   echo "PLOTFORMAT = $PLOTFORMAT"
+   echo "PLOTTERMINAL = $PLOTTERMINAL"
+fi
+
+# Trim files to 48 hour window
+
+cd $DBPATH
+files="`echo *`"
+
+for this in $files
+do
+   tail $this -n576 > ${this}.tmp
+   mv ${this}.tmp $this
+done
+
+###############################################
+
+rm -rf $TEMPPATH
+mkdir -p $TEMPPATH
+
+cd $TEMPPATH
+
+# Update primary plots
+GNUPLOTSCRIPT="$TEMPPATH/gnuplotscript"
+cat $GNUPLOTSCRIPT_TMPL | perl -p -e's/\$(\w+)/$ENV{$1}/g' > $GNUPLOTSCRIPT
+gnuplot < $GNUPLOTSCRIPT
+rm $GNUPLOTSCRIPT
+
+files="`echo *`"
+
+CONVERT_OPTS_A="-interlace none -scale 320x240 -quality 100"
+CONVERT_OPTS_B="-interlace none -scale 800x600 -quality 100"
+
+for this in $files
+do
+   prefix=`echo $this|perl -p -e's/\.\w+$//'`
+   convert $CONVERT_OPTS_A $TEMPPATH/$this $HTMLPATH/${prefix}.png
+   convert $CONVERT_OPTS_B $TEMPPATH/$this $HTMLPATH/${prefix}B.png
+   touch $HTMLPATH/${prefix}.png $HTMLPATH/${prefix}B.png
+done
+
+# Update timestamp
+
+touch $HTMLPATH/index.shtml
+
+# if this was called as a cgi script, it should redirect to the index.shtml file
+if [ -n "$REMOTE_HOST" ]
+then
+   REL_HTML=${HTMLPATH#$HTMLROOT}
+   echo "Location: $REL_HTML/index.shtml"
+   echo
+fi
+
+if [ -z "$DEBUG" ]
+then
+   rm -rf $TEMPPATH
+fi   
+
+exit 0
Index: /lm-sensors/tags/V3-3-0/prog/tellerstats/gather.sh
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/tellerstats/gather.sh	(revision 5163)
+++ /lm-sensors/tags/V3-3-0/prog/tellerstats/gather.sh	(revision 5163)
@@ -0,0 +1,144 @@
+#!/bin/bash
+#
+#    gather.sh
+#	gather the data.
+#	run from cron every 5 minutes.
+#	Don't run manually as root or else files in data/ will get
+#	root ownership then your (non-root) cron daemon won't work
+#
+#    Copyright (C) 2001  Philip Edelbrock
+#
+#    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.
+#
+
+# generic tellerstats init BEGIN
+
+# get config information from /etc/tellerstats.conf or whereever we are pointed
+
+if [ -z "$TELLERSTATS_CONF" ]
+then
+   TELLERSTATS_CONF=/etc/tellerstats.conf
+fi   
+
+export TELLERSTATS_CONF
+
+if [ ! -r $TELLERSTATS_CONF ]
+then
+   echo "$0: Could not find config file $TELLERSTATS_CONF"
+   exit 1
+fi   
+
+. $TELLERSTATS_CONF
+
+if [ ! -d $DBPATH ]
+then
+   echo "$0: data directory $DBPATH does not exist"
+   exit 1
+fi
+
+if [ ! -d $SENSORPATH ]
+then
+   echo "$0: sensor information directory $SENSORPATH does not exist."
+   exit 1
+fi
+
+if [ ! -d $HTMLROOT ]
+then
+   echo "$0: The root of your webserver - $HTMLROOT - does not exist..bailing out"
+   exit 1
+fi
+
+if [ ! -d $HTMLPATH ]
+then
+   echo "$0: The place where we keep HTML files and pictures - $HTMLPATH - does not exist..bailing out"
+   exit 1
+fi
+
+if [ ! -r $GNUPLOTSCRIPT_TMPL ]
+then
+   echo "$0: The gnuplot script template $GNUPLOTSCRIPT_TMPL does not exist..bailing out"
+   exit 1
+fi
+
+export DBPATH SENSORPATH TEMPPATH HTMLROOT HTMLPATH GNUPLOTSCRIPT_TMPL
+
+if [ -n "$DEBUG" ]
+then
+   echo "DBPATH = $DBPATH"
+   echo "SENSORPATH = $SENSORPATH"
+   echo "TEMPPATH = $TEMPPATH"
+   echo "HTMLROOT = $HTMLROOT"
+   echo "HTMLPATH = $HTMLPATH"
+   echo "GNUPLOTSCRIPT_TMPL = $GNUPLOTSCRIPT_TMPL"
+fi
+
+# generic tellerstats init END
+
+# From /etc/sensors.conf for the W83781D:
+#
+#    compute in3 ((6.8/10)+1)*@ ,  @/((6.8/10)+1)
+#    compute in4 ((28/10)+1)*@  ,  @/((28/10)+1)
+#    compute in5 -(210/60.4)*@  ,  -@/(210/60.4)
+#    compute in6 -(90.9/60.4)*@ ,  -@/(90.9/60.4)
+#
+
+#date=yyyyMMddHHmmss, the same format gnuplot expects for the x-axis
+DATE=`date +%Y%m%d%H%M%S`
+
+T=`cat $SENSORPATH/in0   | perl -p -e 's/^.+ ([^ ]+)$/$1/'`
+echo $DATE $T >> $DBPATH/cpu1V
+
+T=`cat $SENSORPATH/in1   | perl -p -e 's/^.+ ([^ ]+)$/$1/'`
+echo $DATE $T >> $DBPATH/cpu2V
+
+T=`cat $SENSORPATH/in2   | perl -p -e 's/^.+ ([^ ]+)$/$1/'`
+echo $DATE $T >> $DBPATH/ThreeVOLT
+
+T=`cat $SENSORPATH/in3   | perl -p -e 's/^.+ ([^ ]+)$/$1/'`
+T=`echo $T \* 1.68 | bc`
+echo $DATE $T >> $DBPATH/FiveVOLT
+
+T=`cat $SENSORPATH/in4   | perl -p -e 's/^.+ ([^ ]+)$/$1/'`
+T=`echo $T \* 3.8 | bc`
+echo $DATE $T >> $DBPATH/TwelveVOLT
+
+T=`cat $SENSORPATH/in5   | perl -p -e 's/^.+ ([^ ]+)$/$1/'`
+T=`echo $T \* -3.477 | bc`
+echo $DATE $T >> $DBPATH/NegTwelveVOLT
+
+T=`cat $SENSORPATH/in6   | perl -p -e 's/^.+ ([^ ]+)$/$1/'`
+T=`echo $T \* -1.505 | bc`
+echo $DATE $T >> $DBPATH/NegFiveVOLT
+
+T=`cat $SENSORPATH/temp1 | perl -p -e 's/^.+ ([^ ]+)$/$1/'`
+echo $DATE $T >> $DBPATH/mb_temp
+
+T=`cat $SENSORPATH/temp2 | perl -p -e 's/^.+ ([^ ]+)$/$1/'`
+echo $DATE $T >> $DBPATH/cpu_temp
+
+T=`cat $SENSORPATH/fan1  | perl -p -e 's/^.+ ([^ ]+)$/$1/'`
+echo $DATE $T >> $DBPATH/fanone
+
+T=`cat $SENSORPATH/fan2  | perl -p -e 's/^.+ ([^ ]+)$/$1/'`
+echo $DATE $T >> $DBPATH/fantwo
+
+T=`cat $SENSORPATH/fan3  | perl -p -e 's/^.+ ([^ ]+)$/$1/'`
+echo $DATE $T >> $DBPATH/fanthree
+
+T=`cat /proc/loadavg     | perl -p -e 's/^([^ ]+) .+$/$1/'`
+echo $DATE $T >> $DBPATH/load
+
+exit 0
Index: /lm-sensors/tags/V3-3-0/prog/tellerstats/gnuplotscript.tmpl
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/tellerstats/gnuplotscript.tmpl	(revision 1051)
+++ /lm-sensors/tags/V3-3-0/prog/tellerstats/gnuplotscript.tmpl	(revision 1051)
@@ -0,0 +1,79 @@
+set term $PLOTTERMINAL
+set timefmt "%Y%m%d%H%M%S"
+set notime
+set autoscale xy
+set size 2,2
+set data style lp
+set xlabel "Time"
+set xdata time
+
+set ylabel "Voltage (V)"
+
+set output "$TEMPPATH/cpu1V.$PLOTFORMAT"
+plot "$DBPATH/cpu1V"         using 1:2 title ''     w lp lw $LINEWIDTH, \
+     2.1                               title 'min'  w lp lw $LINEWIDTH, \
+     2.3                               title 'max'  w lp lw $LINEWIDTH
+set output "$TEMPPATH/cpu2V.$PLOTFORMAT"
+plot "$DBPATH/cpu2V"         using 1:2 title ''     w lp lw $LINEWIDTH, \
+     2.1                               title 'min'  w lp lw $LINEWIDTH, \
+     2.3                               title 'max'  w lp lw $LINEWIDTH
+set output "$TEMPPATH/ThreeVOLT.$PLOTFORMAT"
+plot "$DBPATH/ThreeVOLT"     using 1:2 title ''     w lp lw $LINEWIDTH, \
+     3.0                               title 'min'  w lp lw $LINEWIDTH, \
+     3.6                               title 'max'  w lp lw $LINEWIDTH
+set output "$TEMPPATH/FiveVOLT.$PLOTFORMAT"
+plot "$DBPATH/FiveVOLT"      using 1:2 title ''     w lp lw $LINEWIDTH, \
+     4.85                              title 'min'  w lp lw $LINEWIDTH, \
+     5.15                              title 'max'  w lp lw $LINEWIDTH
+set output "$TEMPPATH/TwelveVOLT.$PLOTFORMAT"
+plot "$DBPATH/TwelveVOLT"    using 1:2 title ''     w lp lw $LINEWIDTH, \
+     11.3                              title 'min'  w lp lw $LINEWIDTH, \
+     12.7                              title 'max'  w lp lw $LINEWIDTH
+set output "$TEMPPATH/NegTwelveVOLT.$PLOTFORMAT"
+plot "$DBPATH/NegTwelveVOLT" using 1:2 title ''     w lp lw $LINEWIDTH, \
+     -12.7                             title 'min'  w lp lw $LINEWIDTH, \
+     -11.3                             title 'max'  w lp lw $LINEWIDTH
+set output "$TEMPPATH/NegFiveVOLT.$PLOTFORMAT"
+plot "$DBPATH/NegFiveVOLT"   using 1:2 title ''     w lp lw $LINEWIDTH, \
+     -5.25                             title 'min'  w lp lw $LINEWIDTH, \
+     -4.75                             title 'max'  w lp lw $LINEWIDTH
+set output "$TEMPPATH/voltages.$PLOTFORMAT"
+plot "$DBPATH/cpu1V"         using 1:2 title '1V'   w lp lw $LINEWIDTH,\
+     "$DBPATH/cpu2V"         using 1:2 title '2V'   w lp lw $LINEWIDTH,\
+     "$DBPATH/ThreeVOLT"     using 1:2 title '3V'   w lp lw $LINEWIDTH,\
+     "$DBPATH/FiveVOLT"      using 1:2 title '5V'   w lp lw $LINEWIDTH,\
+     "$DBPATH/TwelveVOLT"    using 1:2 title '12V'  w lp lw $LINEWIDTH,\
+     "$DBPATH/NegTwelveVOLT" using 1:2 title '-12V' w lp lw $LINEWIDTH,\
+     "$DBPATH/NegFiveVOLT"   using 1:2 title '-5V'  w lp lw $LINEWIDTH
+
+set ylabel "Temperature (Degree C)"
+
+set output "$TEMPPATH/mb_temp.$PLOTFORMAT"
+plot "$DBPATH/mb_temp"       using 1:2 title ''     w lp lw $LINEWIDTH, \
+     50                                title 'max'  w lp lw $LINEWIDTH
+set output "$TEMPPATH/cpu_temp.$PLOTFORMAT"
+plot "$DBPATH/cpu_temp"      using 1:2 title ''     w lp lw $LINEWIDTH, \
+     50                                title 'max'  w lp lw $LINEWIDTH
+set output "$TEMPPATH/temps.$PLOTFORMAT"
+plot "$DBPATH/mb_temp"       using 1:2 title 'motherboard' w lp lw $LINEWIDTH, \
+     "$DBPATH/cpu_temp"      using 1:2 title 'CPU'  w lp lw $LINEWIDTH
+
+set ylabel "Fan Speed (RPM)"
+
+set output "$TEMPPATH/fanone.$PLOTFORMAT"
+plot "$DBPATH/fanone"        using 1:2 title ''     w lp lw $LINEWIDTH, \
+     3000                              title 'min'  w lp lw $LINEWIDTH
+set output "$TEMPPATH/fantwo.$PLOTFORMAT"
+plot "$DBPATH/fantwo"        using 1:2 title ''     w lp lw $LINEWIDTH, \
+     3000                              title 'min'  w lp lw $LINEWIDTH
+set output "$TEMPPATH/fanthree.$PLOTFORMAT"
+plot "$DBPATH/fanthree"      using 1:2 title ''     w lp lw $LINEWIDTH, \
+     3000                              title 'min'  w lp lw $LINEWIDTH
+
+set ylabel "Load Average"
+
+set output "$TEMPPATH/load.$PLOTFORMAT"
+plot "$DBPATH/load"          using 1:2 title ''     w lp lw $LINEWIDTH
+
+exit
+
Index: /lm-sensors/tags/V3-3-0/prog/tellerstats/tellerstats.conf
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/tellerstats/tellerstats.conf	(revision 1051)
+++ /lm-sensors/tags/V3-3-0/prog/tellerstats/tellerstats.conf	(revision 1051)
@@ -0,0 +1,30 @@
+# this is the tellerstats config file. Please be sure to
+# edit these for your setup!!!
+
+
+# where we keep the log of sensor data
+DBPATH="/var/local/LM78DB/data"
+
+# where we get the sensor data from
+SENSORPATH="/proc/sys/dev/sensors/w83781d-isa-0290"
+
+# where temporary plots and files are kept
+TEMPPATH="/usr/tmp/lm78plots"
+
+# the root of the webserver
+HTMLROOT="/home/httpd/html"
+
+# where all the web-visible files are
+HTMLPATH="$HTMLROOT/tellerstats"
+
+# the template of the gnuplot script
+GNUPLOTSCRIPT_TMPL=/usr/local/install/tellerstats/gnuplotscript.tmpl
+
+# linewidth of the plots
+LINEWIDTH=5
+
+# format (fileextensions) for primary plots
+PLOTFORMAT=ps
+# terminal and options for primary plots (should match above) as give to gnuplot. 
+# Please see gnuplot online help for "set terminal"
+PLOTTERMINAL="postscript eps enhanced color \"Helvetica\" 22"
Index: /lm-sensors/tags/V3-3-0/prog/tellerstats/README
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/tellerstats/README	(revision 3075)
+++ /lm-sensors/tags/V3-3-0/prog/tellerstats/README	(revision 3075)
@@ -0,0 +1,44 @@
+This is tellerstats Version 1.9.2
+
+
+Author: Phil Edelbrock <phil@netroedge.com>
+with Changes by 
+   Mark D. Studebaker <mdsxyz123@yahoo.com> and
+   Hans Ecke <hans@ecke.ws>
+
+
+Required Utilities and Packages:
+
+	apache or other web server
+
+	'convert' from ImageMagick or X11
+		http://www.imagemagick.org
+
+	'bc' command line calculator
+
+	gnuplot
+		http://www.gnuplot.org
+
+	lm_sensors
+
+	perl
+
+
+Follow those steps to install tellerstats:
+
+
+* Put the following line into your /etc/crontab file
+
+       */5 * * * * root /usr/local/install/tellerstats/gather.sh
+       
+  (change the directory to reflect where you installed gather.sh)     
+
+* Link tellerstats.sh to /etc/cron.hourly/tellerstats.sh
+
+* move index.shtml into a directory accessible with a webbrowser
+  and configure your webserver for SSI (server side includes)
+ 
+* move tellerstats.conf to /etc and edit it to reflect correct directories
+
+
+
Index: /lm-sensors/tags/V3-3-0/prog/tellerstats/index.shtml
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/tellerstats/index.shtml	(revision 4157)
+++ /lm-sensors/tags/V3-3-0/prog/tellerstats/index.shtml	(revision 4157)
@@ -0,0 +1,48 @@
+<html>
+<META HTTP-EQUIV="expires" CONTENT="300">
+<META HTTP-EQUIV="Refresh" CONTENT="3600;URL=http:index.shtml">
+<title>Computer Hardware Statistics</title>
+<body bgcolor=FFFFFF>
+
+<center><font size=5>Computer Hardware Statistics</font><br>
+<font size=4>Running 48 Hour Window</font><p>
+
+<!--#config timefmt="%c" -->
+These plots were generated <!--#echo var="LAST_MODIFIED" -->
+<br>
+
+<table border=2>
+<tr>
+<td valign=center align=center><a href=voltagesB.png><img src=voltages.png width=320 height=240><br>All Voltages</a></td>
+<td valign=center align=center><a href=tempsB.png><img src=temps.png width=320 height=240><br>All Temps</td>
+<td valign=center align=center><a href=loadB.png><img src=load.png width=320 height=240><br>System Loading</td>
+</tr>
+<tr>
+<td valign=center align=center><a href=TwelveVOLTB.png><img src=TwelveVOLT.png width=320 height=240><br>+12 Volt Supply</td>
+<td valign=center align=center><a href=NegFiveVOLTB.png><img src=NegFiveVOLT.png width=320 height=240><br>-5 Volt Supply</td>
+<td valign=center align=center><a href=NegTwelveVOLTB.png><img src=NegTwelveVOLT.png width=320 height=240><br>-12 Volt Supply</td>
+</tr>
+<tr>
+<td valign=center align=center><a href=cpu1VB.png><img src=cpu1V.png width=320 height=240><br>CPU1 Core Voltage</td>
+<td valign=center align=center><a href=cpu2VB.png><img src=cpu2V.png width=320 height=240><br>CPU2 Core Voltage</td>
+<td valign=center align=center><a href=ThreeVOLTB.png><img src=ThreeVOLT.png width=320 height=240><br>+3.3 Volt Supply</td>
+</tr>
+<tr>
+<td valign=center align=center><a href=FiveVOLTB.png><img src=FiveVOLT.png width=320 height=240><br>+5 Volt Supply</td>
+<td valign=center align=center><a href=cpu_tempB.png><img src=cpu_temp.png width=320 height=240><br>CPU Temperature (degrees C)</td>
+<td valign=center align=center><a href=mb_tempB.png><img src=mb_temp.png width=320 height=240><br>Motherboard Temperature (degrees C)</td>
+</tr>
+<tr>
+<td valign=center align=center><a href=fanoneB.png><img src=fanone.png width=320 height=240><br>Fan #1 Speed (RPM)</td>
+<td valign=center align=center><a href=fantwoB.png><img src=fantwo.png width=320 height=240><br>Fan #2 Speed (RPM)</td>
+<td valign=center align=center><a href=fanthreeB.png><img src=fanthree.png width=320 height=240><br>Fan #3 Speed (RPM)</td>
+</tr>
+</table><p>
+
+This page generated with drivers and the tellerstats scripts from
+<a href="http://www.lm-sensors.org/">The lm_sensors drivers</a>.
+
+<p>
+
+</body>
+</html>
Index: /lm-sensors/tags/V3-3-0/prog/rrd/sens_update_rrd
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/rrd/sens_update_rrd	(revision 5163)
+++ /lm-sensors/tags/V3-3-0/prog/rrd/sens_update_rrd	(revision 5163)
@@ -0,0 +1,102 @@
+#!/bin/sh
+#
+#    sens_update_rrd -
+#	Update a sensors rrd database.
+#	Sample usage:
+#		sens_update_rrd /var/lib/database.rrd hwmon0
+#	Sample cron entry:
+#		*/5 * * * * /usr/local/bin/sens_update_rrd /var/lib/sensors-rrd/sensors.rrd hwmon0
+#
+#################################################################
+#
+#    Copyright 2001,2005 Mark D. Studebaker <mdsxyz123@yahoo.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; 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.
+#
+#################################################################
+#
+if [ $# -lt 1 -o $# -gt 2 ]
+then
+	echo "usage: $0 database.rrd [hwmonN]"
+	exit 1
+elif [ $# -eq 2 ]
+then
+	HWMON=$2
+else
+	HWMON=hwmon0
+fi
+#
+RRDPATH=/usr/bin
+RRDB=$1
+
+SENSDIR=/sys/class/hwmon
+SENS=$SENSDIR/$HWMON/device
+
+if [ ! -d $SENS ]
+then
+	echo "No sensors found in: $SENS"
+	echo "(modprobe sensor modules?)"
+	exit 1
+fi
+
+STRING=N
+#
+# Get the value from these sensor files (/sys)
+#
+SENSORS="fan1 fan2 fan3"
+for i in $SENSORS
+do
+	V="`cat $SENS/${i}_input 2> /dev/null`"
+	if [ $? -ne 0 ]
+	then
+		STRING="${STRING}:U"
+	else
+		STRING="${STRING}:${V}"
+	fi
+done
+#
+# Get the value from these sensor files (/sys) and divide by 1000
+#
+SENSORS="temp1 temp2 temp3 in0 in1 in2 in3 in4 in5 in6"
+for i in $SENSORS
+do
+	V="`cat $SENS/${i}_input 2> /dev/null`"
+	if [ $? -ne 0 ]
+	then
+		STRING="${STRING}:U"
+	else
+		V=`echo "3k0 ${V/-/_} 1000/p"|dc`
+		STRING="${STRING}:${V}"
+	fi
+done
+
+#
+# Get the first value from these /proc files
+#
+SENSORS="loadavg"
+for i in $SENSORS
+do
+	V="`cat /proc/$i 2> /dev/null`"
+	if [ $? -ne 0 ]
+	then
+		STRING="${STRING}:U"
+	else
+		V="`echo $V | cut -d ' ' -f 1`"
+		STRING="${STRING}:${V}"
+	fi
+done
+
+$RRDPATH/rrdtool update $RRDB $STRING
Index: /lm-sensors/tags/V3-3-0/prog/rrd/summ_week.in
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/rrd/summ_week.in	(revision 4372)
+++ /lm-sensors/tags/V3-3-0/prog/rrd/summ_week.in	(revision 4372)
@@ -0,0 +1,103 @@
+#!%%RRDPATH%%/rrdcgi 
+
+<HTML>
+<HEAD>
+<TITLE>One Week Summary Hardware Statistics for %%MACH%%</TITLE>
+</HEAD>
+<BODY>
+<H1>ONE WEEK SUMMARY VIEW</H1>
+See also: <br>
+<a href="sens_day.cgi">One Day View</a> <br>
+<a href="sens_week.cgi">One Week View</a> <br>
+
+<H1>TEMPERATURES</H1>
+
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/tempall.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Temperature (degrees C); Load Avg * 10"
+	-t "Temperatures and Load Average"
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	-y 5:10
+	DEF:temp=%%RRDDIR%%/sensors.rrd:temp1:AVERAGE
+	DEF:ttemp=%%RRDDIR%%/sensors.rrd:temp2:AVERAGE
+	DEF:la=%%RRDDIR%%/sensors.rrd:loadavg:AVERAGE
+	CDEF:lla=10,la,*
+	LINE2:ttemp#FF0000:"CPU Temp"
+	LINE2:temp#FF00FF:"Motherboard Temp"
+	LINE2:lla#00FFFF:"Load Average * 10">
+</P>
+
+
+<H1>FANS</H1>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/fanall.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Fan speed (RPM)"
+	-t "Fan speeds"
+	-u 5000
+	--units-exponent 0
+	--alt-autoscale
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	DEF:f1=%%RRDDIR%%/sensors.rrd:fan1:AVERAGE
+	DEF:f2=%%RRDDIR%%/sensors.rrd:fan2:AVERAGE
+	DEF:f3=%%RRDDIR%%/sensors.rrd:fan3:AVERAGE
+	LINE2:f1#FF00FF:"Fan 1"
+	LINE2:f2#FF0000:"Fan 2"
+	LINE2:f3#00FFFF:"Fan 3">
+</P>
+
+<H1>POWER SUPPLIES</H1>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/inall.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Volts"
+	-t "All Power Supplies"
+	-l -15 -u 15 --rigid
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	-y 1:5
+	DEF:i0=%%RRDDIR%%/sensors.rrd:in0:AVERAGE
+	DEF:i1=%%RRDDIR%%/sensors.rrd:in1:AVERAGE
+	DEF:i2=%%RRDDIR%%/sensors.rrd:in2:AVERAGE
+	DEF:i3=%%RRDDIR%%/sensors.rrd:in3:AVERAGE
+	DEF:i4=%%RRDDIR%%/sensors.rrd:in4:AVERAGE
+	DEF:i5=%%RRDDIR%%/sensors.rrd:in5:AVERAGE
+	DEF:i6=%%RRDDIR%%/sensors.rrd:in6:AVERAGE
+	CDEF:ii3=1.68,i3,*
+	CDEF:ii4=3.8,i4,*
+	CDEF:ii5=-3.48,i5,*
+	CDEF:ii6=-1.5,i6,*
+	LINE2:ii4#00FF00:"+12V"
+	LINE2:ii3#FF0000:"+5V"
+	LINE2:i2#FF8000:"+3.3V"
+	LINE2:i0#FF00FF:"Core 1"
+	LINE2:i1#00FFFF:"Core 2"
+	LINE2:ii6#FF0080:"-5V"
+	LINE2:ii5#0000FF:"-12V">
+</P>
+
+This page generated with data and scripts from
+<a href="http://www.lm-sensors.org/">the lm_sensors project</a>;
+the data are stored in a Round Robin Database and
+the graphs are generated by
+<a href="http://oss.oetiker.ch/rrdtool/">
+RRD</a>.
+
+</BODY>
+</HTML>
+
+
+
+
Index: /lm-sensors/tags/V3-3-0/prog/rrd/sens_create_rrd
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/rrd/sens_create_rrd	(revision 5163)
+++ /lm-sensors/tags/V3-3-0/prog/rrd/sens_create_rrd	(revision 5163)
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+#    sens_create_rrd -
+#	Create a sensors rrd database of 14 sensor readings
+#	at 5 minute intervals for 1 week.
+#
+#    Copyright 2001 Mark D. Studebaker <mdsxyz123@yahoo.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; 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.
+#
+RRDPATH=/usr/bin
+RRDB=$1
+#Heartbeat 15 minutes
+HB=900
+#Rows 12 * 24 * 7 = 1 week of every 5 minutes
+RW=2016
+
+if [ $# -ne 1 ]
+then
+	echo usage: $0 database.rrd
+	exit 1
+fi
+
+DIR=`dirname $1`
+if [ ! -w $DIR ]
+then
+	echo $0: directory $DIR not present or not writable
+	exit 1
+fi
+
+$RRDPATH/rrdtool create $RRDB --step 300 \
+	DS:fan1:GAUGE:${HB}:0:12000 \
+	DS:fan2:GAUGE:${HB}:0:12000 \
+	DS:fan3:GAUGE:${HB}:0:12000 \
+	DS:temp1:GAUGE:${HB}:-25:250 \
+	DS:temp2:GAUGE:${HB}:-25:250 \
+	DS:temp3:GAUGE:${HB}:-25:250 \
+	DS:in0:GAUGE:${HB}:-25:25 \
+	DS:in1:GAUGE:${HB}:-25:25 \
+	DS:in2:GAUGE:${HB}:-25:25 \
+	DS:in3:GAUGE:${HB}:-25:25 \
+	DS:in4:GAUGE:${HB}:-25:25 \
+	DS:in5:GAUGE:${HB}:-25:25 \
+	DS:in6:GAUGE:${HB}:-25:25 \
+	DS:loadavg:GAUGE:${HB}:0:U \
+	RRA:AVERAGE:0.5:1:$RW
+
Index: /lm-sensors/tags/V3-3-0/prog/rrd/sens_week.in
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/rrd/sens_week.in	(revision 4372)
+++ /lm-sensors/tags/V3-3-0/prog/rrd/sens_week.in	(revision 4372)
@@ -0,0 +1,231 @@
+#!%%RRDPATH%%/rrdcgi 
+
+<HTML>
+<HEAD>
+<TITLE>One Week Hardware Statistics for %%MACH%%</TITLE>
+</HEAD>
+<BODY>
+<H1>ONE WEEK VIEW</H1>
+See also: <br>
+<a href="sens_day.cgi">One Day View</a> <br>
+<a href="summ_week.cgi">One Week Summary View</a>
+
+<H1>TEMPERATURES</H1>
+
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/temp1.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Temperature (degrees C)"
+	-t "Motherboard temperature"
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	-y 1:5
+	DEF:temp=%%RRDDIR%%/sensors.rrd:temp1:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/temp2.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Temperature (degrees C)"
+	-t "CPU 1 temperature"
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	-y 1:5
+	DEF:temp=%%RRDDIR%%/sensors.rrd:temp2:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/temp3.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Temperature (degrees C)"
+	-t "CPU 2 temperature"
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	-y 1:5
+	DEF:temp=%%RRDDIR%%/sensors.rrd:temp3:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<H1>FANS</H1>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/fan1.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Fan speed (RPM)"
+	-t "Fan 1 speed"
+	-u 5000
+	--units-exponent 0
+	--alt-autoscale
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	DEF:temp=%%RRDDIR%%/sensors.rrd:fan1:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/fan2.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Fan speed (RPM)"
+	-t "Fan 2 speed"
+	-u 5000
+	--units-exponent 0
+	--alt-autoscale
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	DEF:temp=%%RRDDIR%%/sensors.rrd:fan2:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/fan3.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Fan speed (RPM)"
+	-t "Fan 3 speed"
+	-u 5000
+	--units-exponent 0
+	--alt-autoscale
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	DEF:temp=%%RRDDIR%%/sensors.rrd:fan3:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<H1>POWER SUPPLIES</H1>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/in0.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Volts"
+	-t "Core 1 Voltage"
+	-l 1 -u 3 --rigid
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	-y 0.2:1
+	DEF:temp=%%RRDDIR%%/sensors.rrd:in0:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/in1.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Volts"
+	-t "Core 2 Voltage"
+	-l 1 -u 3 --rigid
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	-y 0.2:1
+	DEF:temp=%%RRDDIR%%/sensors.rrd:in1:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/in2.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Volts"
+	-t "+3.3 Volt Supply"
+	-l 2 -u 4 --rigid
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	-y 0.2:1
+	DEF:temp=%%RRDDIR%%/sensors.rrd:in2:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/in3.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Volts"
+	-t "+5 Volt Supply"
+	-l 4 -u 6 --rigid
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	-y 0.2:1
+	DEF:x=%%RRDDIR%%/sensors.rrd:in3:AVERAGE
+	CDEF:temp=1.68,x,*
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/in4.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Volts"
+	-t "+12 Volt Supply"
+	-l 10 -u 14
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	-y 1:1
+	DEF:x=%%RRDDIR%%/sensors.rrd:in4:AVERAGE
+	CDEF:temp=3.8,x,*
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/in5.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Volts"
+	-t "-12 Volt Supply"
+	-l -14 -u -10
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	-y 1:1
+	DEF:x=%%RRDDIR%%/sensors.rrd:in5:AVERAGE
+	CDEF:temp=-3.48,x,*
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/in6.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1week
+	-v "Volts"
+	-t "-5 Volt Supply"
+	-l -6 -u -4 --rigid
+	-x HOUR:6:DAY:1:DAY:1:86400:'%a %b %d'
+	-y 0.2:1
+	DEF:x=%%RRDDIR%%/sensors.rrd:in6:AVERAGE
+	CDEF:temp=-1.5,x,*
+	LINE2:temp#FF00FF>
+</P>
+
+This page generated with data and scripts from
+<a href="http://www.lm-sensors.org/">the lm_sensors project</a>;
+the data are stored in a Round Robin Database and
+the graphs are generated by
+<a href="http://oss.oetiker.ch/rrdtool/">
+RRD</a>.
+
+</BODY>
+</HTML>
+
+
+
+
Index: /lm-sensors/tags/V3-3-0/prog/rrd/sens_day.in
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/rrd/sens_day.in	(revision 4372)
+++ /lm-sensors/tags/V3-3-0/prog/rrd/sens_day.in	(revision 4372)
@@ -0,0 +1,231 @@
+#!%%RRDPATH%%/rrdcgi 
+
+<HTML>
+<HEAD>
+<TITLE>One Day Hardware Statistics for %%MACH%%</TITLE>
+</HEAD>
+<BODY>
+<H1>ONE DAY VIEW</H1>
+See also: <br>
+<a href="sens_week.cgi">One Week View</a> <br>
+<a href="summ_week.cgi">One Week Summary View</a>
+
+<H1>TEMPERATURES</H1>
+
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/temp1d.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1days
+	-v "Temperature (degrees C)"
+	-t "Motherboard temperature"
+	-x HOUR:1:HOUR:3:HOUR:3:0:'%b %d %H:00'
+	-y 1:5
+	DEF:temp=%%RRDDIR%%/sensors.rrd:temp1:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/temp2d.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1days
+	-v "Temperature (degrees C)"
+	-t "CPU 1 temperature"
+	-x HOUR:1:HOUR:3:HOUR:3:0:'%b %d %H:00'
+	-y 1:5
+	DEF:temp=%%RRDDIR%%/sensors.rrd:temp2:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/temp3d.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1days
+	-v "Temperature (degrees C)"
+	-t "CPU 2 temperature"
+	-x HOUR:1:HOUR:3:HOUR:3:0:'%b %d %H:00'
+	-y 1:5
+	DEF:temp=%%RRDDIR%%/sensors.rrd:temp3:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<H1>FANS</H1>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/fan1d.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1days
+	-v "Fan speed (RPM)"
+	-t "Fan 1 speed"
+	-u 5000
+	--units-exponent 0
+	--alt-autoscale
+	-x HOUR:1:HOUR:3:HOUR:3:0:'%b %d %H:00'
+	DEF:temp=%%RRDDIR%%/sensors.rrd:fan1:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/fan2d.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1days
+	-v "Fan speed (RPM)"
+	-t "Fan 2 speed"
+	-u 5000
+	--units-exponent 0
+	--alt-autoscale
+	-x HOUR:1:HOUR:3:HOUR:3:0:'%b %d %H:00'
+	DEF:temp=%%RRDDIR%%/sensors.rrd:fan2:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/fan3d.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1days
+	-v "Fan speed (RPM)"
+	-t "Fan 3 speed"
+	-u 5000
+	--units-exponent 0
+	--alt-autoscale
+	-x HOUR:1:HOUR:3:HOUR:3:0:'%b %d %H:00'
+	DEF:temp=%%RRDDIR%%/sensors.rrd:fan3:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<H1>POWER SUPPLIES</H1>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/in0d.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1days
+	-v "Volts"
+	-t "Core 1 Voltage"
+	-l 1 -u 3 --rigid
+	-x HOUR:1:HOUR:3:HOUR:3:0:'%b %d %H:00'
+	-y 0.2:1
+	DEF:temp=%%RRDDIR%%/sensors.rrd:in0:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/in1d.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1days
+	-v "Volts"
+	-t "Core 2 Voltage"
+	-l 1 -u 3 --rigid
+	-x HOUR:1:HOUR:3:HOUR:3:0:'%b %d %H:00'
+	-y 0.2:1
+	DEF:temp=%%RRDDIR%%/sensors.rrd:in1:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/in2d.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1days
+	-v "Volts"
+	-t "+3.3 Volt Supply"
+	-l 2 -u 4 --rigid
+	-x HOUR:1:HOUR:3:HOUR:3:0:'%b %d %H:00'
+	-y 0.2:1
+	DEF:temp=%%RRDDIR%%/sensors.rrd:in2:AVERAGE
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/in3d.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1days
+	-v "Volts"
+	-t "+5 Volt Supply"
+	-l 4 -u 6 --rigid
+	-x HOUR:1:HOUR:3:HOUR:3:0:'%b %d %H:00'
+	-y 0.2:1
+	DEF:x=%%RRDDIR%%/sensors.rrd:in3:AVERAGE
+	CDEF:temp=1.68,x,*
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/in4d.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1days
+	-v "Volts"
+	-t "+12 Volt Supply"
+	-l 10 -u 14
+	-x HOUR:1:HOUR:3:HOUR:3:0:'%b %d %H:00'
+	-y 0.5:1
+	DEF:x=%%RRDDIR%%/sensors.rrd:in4:AVERAGE
+	CDEF:temp=3.8,x,*
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/in5d.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1days
+	-v "Volts"
+	-t "-12 Volt Supply"
+	-l -14 -u -10
+	-x HOUR:1:HOUR:3:HOUR:3:0:'%b %d %H:00'
+	-y 0.5:1
+	DEF:x=%%RRDDIR%%/sensors.rrd:in5:AVERAGE
+	CDEF:temp=-3.48,x,*
+	LINE2:temp#FF00FF>
+</P>
+
+<P><RRD::GRAPH %%APACHDIR%%/pix/in6d.png
+        --imginfo '<IMG SRC=pix/%s WIDTH=%lu HEIGHT=%lu >'
+	-a PNG
+	-h 200 -w 800
+	--lazy
+	-s -1days
+	-v "Volts"
+	-t "-5 Volt Supply"
+	-l -6 -u -4 --rigid
+	-x HOUR:1:HOUR:3:HOUR:3:0:'%b %d %H:00'
+	-y 0.2:1
+	DEF:x=%%RRDDIR%%/sensors.rrd:in6:AVERAGE
+	CDEF:temp=-1.5,x,*
+	LINE2:temp#FF00FF>
+</P>
+
+This page generated with data and scripts from
+<a href="http://www.lm-sensors.org/">the lm_sensors project</a>;
+the data are stored in a Round Robin Database and
+the graphs are generated by
+<a href="http://oss.oetiker.ch/rrdtool/">
+RRD</a>.
+
+</BODY>
+</HTML>
+
+
+
+
Index: /lm-sensors/tags/V3-3-0/prog/rrd/Makefile
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/rrd/Makefile	(revision 4372)
+++ /lm-sensors/tags/V3-3-0/prog/rrd/Makefile	(revision 4372)
@@ -0,0 +1,55 @@
+#
+# Edit the following for your setup
+#
+USER=apache
+# CRONTAB not used
+#CRONTAB=/var/spool/cron/tabs/$(USER)
+RRDPATH=/usr/bin
+BINPATH=/usr/local/bin
+RRDDIR=/var/lib/sensors-rrd
+APACHE=/var/www/html
+APACHDIR=$(APACHE)/senspix
+MACH=`uname -n`
+SENSDEV=hwmon0
+SENSDIR=/sys/class/hwmon/$(SENSDEV)/device
+################################################
+# Everything below here should be fine
+#
+RRDB=$(RRDDIR)/sensors.rrd
+
+all: sens_day.cgi sens_week.cgi summ_week.cgi $(SENSDIR)
+
+%.cgi : %.in Makefile
+	sed -e "s#%%RRDPATH%%#$(RRDPATH)#g;s#%%APACHDIR%%#$(APACHDIR)#g;s#%%RRDDIR%%#$(RRDDIR)#g;s#%%MACH%%#$(MACH)#g" $< > $@
+
+$(RRDDIR) :
+	install -d -o $(USER) $(RRDDIR)
+
+$(RRDB) : $(RRDDIR)
+	./sens_create_rrd $(RRDB)
+	chown $(USER) $(RRDB)
+	
+$(SENSDIR) :
+	$(error error - sensor $(SENSDEV) not installed - check SENSDEV definition in Makefile)
+
+$(CRONTAB) :
+	$(error error - crontab for user $(USER) not present - check CRONTAB definition in Makefile)
+
+$(APACHE) :
+	$(error error - Web server directory $(APACHE) not present - check APACHE definition in Makefile)
+
+$(APACHDIR)/pix : $(APACHE)
+	install -d -o $(USER) -m 777 $(APACHDIR)/pix
+
+install: all $(RRDB) $(SENSDIR) $(CRONTAB) $(APACHDIR)/pix
+	install -m 755 sens_update_rrd $(BINPATH)
+	install -m 755 sens_week.cgi $(APACHDIR)
+	install -m 755 sens_day.cgi $(APACHDIR)
+	install -m 755 summ_week.cgi $(APACHDIR)
+#	grep sens_update_rrd $(CRONTAB) > /dev/null 2>&1 || echo '*/5 * * * *	/usr/local/bin/sens_update_rrd' $(RRDB) $(SENSDEV) >> $(CRONTAB)
+	@echo
+	@echo Note!!! You must manually install the following line in the crontab for user $(USER):
+	@echo '   */5 * * * *    ' /usr/local/bin/sens_update_rrd $(RRDB) $(SENSDEV)
+
+clean:
+	rm -f sens_day.cgi sens_week.cgi summ_week.cgi
Index: /lm-sensors/tags/V3-3-0/prog/rrd/README
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/rrd/README	(revision 4372)
+++ /lm-sensors/tags/V3-3-0/prog/rrd/README	(revision 4372)
@@ -0,0 +1,101 @@
+OVERVIEW
+--------
+This directory contains shell and cgi scripts which maintain a
+database of sensor data and generate web pages containing
+graphs of sensor data.
+
+The RRD (Round Robin Database) package is REQUIRED.
+Get this package from
+         http://oss.oetiker.ch/rrdtool/
+
+A web server with CGI support is required.
+
+We have provided these scripts because the RRD package,
+while simple to build and install, is rather difficult
+to configure to store and display data.
+
+RRD, together with lm_sensors and these scripts, provide the
+following advantages:
+	- True round-robin (constant-size) database
+	- On-the-fly graph generation with CGI scripts
+	- Multiple data views using single database
+	- Automatic time stamping, interpolation of missing data,
+	  large data dropouts (during machine downtime) correctly
+	  shown on graphs
+
+If you are looking for something a little simpler,
+that does not require RRD, try ../tellerstats.
+
+The RRD scripts read /sys entries and do not use libsensors.
+Therefore the scale factors in /etc/sensors.conf are not used,
+and you may have to change the scale factors in the cgi scripts.
+
+
+WARNING!
+--------
+The 'make install' script tries to make it easy for you but
+makes a lot of assumptions about your cron and web setup.
+If you have any concerns, do NOT run make install, and
+install the items by hand instead.
+
+
+INSTALLATION INSTRUCTIONS
+-------------------------
+
+	- Make and install lm_sensors. Get it working (run 'sensors')
+	  before you try this!
+	- Get RRD, make and install it
+	- Edit the top of the Makefile. Do NOT skip this step.
+	  The defaults will probably not work for you.
+	  Here are the defaults:
+		Cron user
+			USER=httpd
+		Path to rrdtool and rrdcgi
+			RRDPATH=/usr/bin
+		Place to install sens_update_rrd script
+			BINPATH=/usr/local/bin
+		Place to store the database
+			RRDDIR=/var/lib/sensors-rrd
+		Place to put the cgi script and the graphs
+			APACHE=/usr/local/apache/htdocs
+			APACHDIR=$(APACHE)/senspix
+		The sensor device in your system
+		(isa recommended over i2c if both are available)
+			SENSDEV=hwmon0           (look in /sys/class/hwmon)
+	- make
+	- (as root) make install, which does the following.
+	  If you don't want it to do this, install by hand!!!
+		Creates a database
+		Installs the data gathering script
+		Installs the CGI script in your web server
+	- Manually add the following line to the crontab for
+	  the user specified above, which will call the data gathering
+	  script every 5 minutes (adjust paths as necessary)
+		*/5 * * * *	/usr/local/bin/sens_update_rrd /var/lib/sensors-rrd/sensors.rrd hwmon0
+	- Load http://localhost/senspix/sens_day.cgi in your web browser
+		to test
+
+
+TROUBLESHOOTING
+---------------
+	RRD problems: See the RRD documentation
+
+	Cron problems: check the cron logs, maybe your cron format
+		is different or you need to signal the cron daemon?
+
+	Unused sensors: remove appropriate sections from sens_*.cgi.in
+		and remake and install.
+
+	Sensor scaling factors incorrect (in3-in6): Edit lines in
+		sens_*.cgi.in that start
+		'CDEF:' to change the scale factor.
+
+	CGI problems: Check the apache logs. If CGI is not enabled for
+		the directory add a .htaccess file to the directory
+		including the line
+			Options ExecCGI 
+
+	Removing the "RRDTOOL / TOBI OETIKER" tag in RRD graphs:
+		Comment out the line 
+			gator(gif, (int) im->xgif-5, 5);
+		in src/rrd_graph.c in the RRD package.
Index: /lm-sensors/tags/V3-3-0/prog/detect/sensors-detect
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/detect/sensors-detect	(revision 5946)
+++ /lm-sensors/tags/V3-3-0/prog/detect/sensors-detect	(revision 5946)
@@ -0,0 +1,6665 @@
+#!/usr/bin/perl -w
+#
+#    sensors-detect - Detect hardware monitoring chips
+#    Copyright (C) 1998 - 2002  Frodo Looijaard <frodol@dds.nl>
+#    Copyright (C) 2004 - 2011  Jean Delvare <khali@linux-fr.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.
+#
+#    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.
+#
+
+require 5.004;
+
+use strict;
+use Fcntl qw(:DEFAULT :seek);
+use File::Basename;
+
+# We will call modprobe, which typically lives in either /sbin,
+# /usr/sbin or /usr/local/bin. So make sure these are all in the PATH.
+foreach ('/usr/sbin', '/usr/local/sbin', '/sbin') {
+	$ENV{PATH} = "$_:".$ENV{PATH}
+		unless $ENV{PATH} =~ m/(^|:)$_\/?(:|$)/;
+}
+
+#########################
+# CONSTANT DECLARATIONS #
+#########################
+
+use constant NO_CACHE => 1;
+use vars qw(@pci_adapters @chip_ids @ipmi_ifs @non_hwmon_chip_ids
+	    $i2c_addresses_to_scan $revision @i2c_byte_cache);
+
+$revision = '$Revision$ ($Date$)';
+$revision =~ s/\$\w+: (.*?) \$/$1/g;
+$revision =~ s/ \([^()]*\)//;
+
+# This is the list of SMBus or I2C adapters we recognize by their PCI
+# signature. This is an easy and fast way to determine which SMBus or I2C
+# adapters should be present.
+# Each entry must have a vendid (Vendor ID), devid (Device ID) and
+# procid (Device name) and driver (Device driver).
+@pci_adapters = (
+	{
+		vendid	=> 0x8086,
+		devid	=> 0x7113,
+		procid	=> "Intel 82371AB PIIX4 ACPI",
+		driver	=> "i2c-piix4",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x7603,
+		procid	=> "Intel 82372FB PIIX5 ACPI",
+		driver	=> "to-be-tested",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x719b,
+		procid	=> "Intel 82443MX Mobile",
+		driver	=> "i2c-piix4",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x2413,
+		procid	=> "Intel 82801AA ICH",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x2423,
+		procid	=> "Intel 82801AB ICH0",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x2443,
+		procid	=> "Intel 82801BA ICH2",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x2483,
+		procid	=> "Intel 82801CA/CAM ICH3",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x24C3,
+		procid	=> "Intel 82801DB ICH4",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x24D3,
+		procid	=> "Intel 82801EB ICH5",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x25A4,
+		procid	=> "Intel 6300ESB",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x269B,
+		procid	=> "Intel Enterprise Southbridge - ESB2",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x266A,
+		procid	=> "Intel 82801FB ICH6",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x27DA,
+		procid	=> "Intel 82801G ICH7",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x283E,
+		procid	=> "Intel 82801H ICH8",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x2930,
+		procid	=> "Intel ICH9",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x5032,
+		procid	=> "Intel Tolapai",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x3A30,
+		procid	=> "Intel ICH10",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x3A60,
+		procid	=> "Intel ICH10",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x3B30,
+		procid	=> "Intel 3400/5 Series (PCH)",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x1C22,
+		procid	=> "Intel Cougar Point (PCH)",
+		driver	=> "i2c-i801",
+	}, {
+		vendid	=> 0x8086,
+		devid	=> 0x8119,
+		procid	=> "Intel SCH",
+		driver	=> "i2c-isch",
+	}, {
+		vendid	=> 0x1106,
+		devid	=> 0x3040,
+		procid	=> "VIA Technologies VT82C586B Apollo ACPI",
+		driver	=> "i2c-via",
+	}, {
+		vendid	=> 0x1106,
+		devid	=> 0x3050,
+		procid	=> "VIA Technologies VT82C596 Apollo ACPI",
+		driver	=> "i2c-viapro",
+	}, {
+		vendid	=> 0x1106,
+		devid	=> 0x3051,
+		procid	=> "VIA Technologies VT82C596B ACPI",
+		driver	=> "i2c-viapro",
+	}, {
+		vendid	=> 0x1106,
+		devid	=> 0x3057,
+		procid	=> "VIA Technologies VT82C686 Apollo ACPI",
+		driver	=> "i2c-viapro",
+	}, {
+		vendid	=> 0x1106,
+		devid	=> 0x3074,
+		procid	=> "VIA Technologies VT8233 VLink South Bridge",
+		driver	=> "i2c-viapro",
+	}, {
+		vendid	=> 0x1106,
+		devid	=> 0x3147,
+		procid	=> "VIA Technologies VT8233A South Bridge",
+		driver	=> "i2c-viapro",
+	}, {
+		vendid	=> 0x1106,
+		devid	=> 0x3177,
+		procid	=> "VIA Technologies VT8233A/8235 South Bridge",
+		driver	=> "i2c-viapro",
+	}, {
+		vendid	=> 0x1106,
+		devid	=> 0x3227,
+		procid	=> "VIA Technologies VT8237 South Bridge",
+		driver	=> "i2c-viapro",
+	}, {
+		vendid	=> 0x1106,
+		devid	=> 0x3337,
+		procid	=> "VIA Technologies VT8237A South Bridge",
+		driver	=> "i2c-viapro",
+	}, {
+		vendid	=> 0x1106,
+		devid	=> 0x8235,
+		procid	=> "VIA Technologies VT8231 South Bridge",
+		driver	=> "i2c-viapro",
+	}, {
+		vendid	=> 0x1106,
+		devid	=> 0x3287,
+		procid	=> "VIA Technologies VT8251 South Bridge",
+		driver	=> "i2c-viapro",
+	}, {
+		vendid	=> 0x1106,
+		devid	=> 0x8324,
+		procid	=> "VIA Technologies CX700 South Bridge",
+		driver	=> "i2c-viapro",
+	}, {
+		vendid	=> 0x1106,
+		devid	=> 0x8353,
+		procid	=> "VIA Technologies VX800/VX820 South Bridge",
+		driver	=> "i2c-viapro",
+	}, {
+		vendid	=> 0x1039,
+		devid	=> 0x0008,
+		procid	=> "Silicon Integrated Systems SIS5595",
+		driver	=> "i2c-sis5595",
+	}, {
+		vendid	=> 0x1039,
+		devid	=> 0x0630,
+		procid	=> "Silicon Integrated Systems SIS630",
+		driver	=> "i2c-sis630",
+	}, {
+		vendid	=> 0x1039,
+		devid	=> 0x0730,
+		procid	=> "Silicon Integrated Systems SIS730",
+		driver	=> "i2c-sis630",
+	}, {
+		vendid	=> 0x1039,
+		devid	=> 0x0016,
+		procid	=> "Silicon Integrated Systems SMBus Controller",
+		driver	=> "i2c-sis96x",
+	}, {
+		# Both Ali chips below have same PCI ID. Can't be helped. Only one should load.
+		vendid	=> 0x10b9,
+		devid	=> 0x7101,
+		procid	=> "Acer Labs 1533/1543",
+		driver	=> "i2c-ali15x3",
+	}, {
+		vendid	=> 0x10b9,
+		devid	=> 0x7101,
+		procid	=> "Acer Labs 1535",
+		driver	=> "i2c-ali1535",
+	}, {
+		vendid	=> 0x10b9,
+		devid	=> 0x1563,
+		procid	=> "Acer Labs 1563",
+		driver	=> "i2c-ali1563",
+	}, {
+		vendid	=> 0x1022,
+		devid	=> 0x740b,
+		procid	=> "AMD-756 Athlon ACPI",
+		driver	=> "i2c-amd756",
+	}, {
+		vendid	=> 0x1022,
+		devid	=> 0x7413,
+		procid	=> "AMD-766 Athlon ACPI",
+		driver	=> "i2c-amd756",
+	}, {
+		vendid	=> 0x1022,
+		devid	=> 0x7443,
+		procid	=> "AMD-768 System Management",
+		driver	=> "i2c-amd756",
+	}, {
+		vendid	=> 0x1022,
+		devid	=> 0x746b,
+		procid	=> "AMD-8111 ACPI",
+		driver	=> "i2c-amd756",
+	}, {
+		vendid	=> 0x1022,
+		devid	=> 0x746a,
+		procid	=> "AMD-8111 SMBus 2.0",
+		driver	=> "i2c-amd8111",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x01b4,
+		procid	=> "nVidia nForce SMBus",
+		driver	=> "i2c-amd756",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x0064,
+		procid	=> "nVidia Corporation nForce2 SMBus (MCP)",
+		driver	=> "i2c-nforce2",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x0084,
+		procid	=> "nVidia Corporation nForce2 Ultra 400 SMBus (MCP)",
+		driver	=> "i2c-nforce2",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x00D4,
+		procid	=> "nVidia Corporation nForce3 Pro150 SMBus (MCP)",
+		driver	=> "i2c-nforce2",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x00E4,
+		procid	=> "nVidia Corporation nForce3 250Gb SMBus (MCP)",
+		driver	=> "i2c-nforce2",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x0052,
+		procid	=> "nVidia Corporation nForce4 SMBus (MCP)",
+		driver	=> "i2c-nforce2",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x0034,
+		procid	=> "nVidia Corporation nForce4 SMBus (MCP-04)",
+		driver	=> "i2c-nforce2",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x0264,
+		procid	=> "nVidia Corporation nForce SMBus (MCP51)",
+		driver	=> "i2c-nforce2",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x0368,
+		procid	=> "nVidia Corporation nForce SMBus (MCP55)",
+		driver	=> "i2c-nforce2",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x03eb,
+		procid	=> "nVidia Corporation nForce SMBus (MCP61)",
+		driver	=> "i2c-nforce2",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x0446,
+		procid	=> "nVidia Corporation nForce SMBus (MCP65)",
+		driver	=> "i2c-nforce2",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x0542,
+		procid	=> "nVidia Corporation nForce SMBus (MCP67)",
+		driver	=> "i2c-nforce2",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x07d8,
+		procid	=> "nVidia Corporation nForce SMBus (MCP73)",
+		driver	=> "i2c-nforce2",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x0752,
+		procid	=> "nVidia Corporation nForce SMBus (MCP78S)",
+		driver	=> "i2c-nforce2",
+	}, {
+		vendid	=> 0x10de,
+		devid	=> 0x0aa2,
+		procid	=> "nVidia Corporation nForce SMBus (MCP79)",
+		driver	=> "i2c-nforce2",
+	}, {
+		vendid	=> 0x1166,
+		devid	=> 0x0200,
+		procid	=> "ServerWorks OSB4 South Bridge",
+		driver	=> "i2c-piix4",
+	}, {
+		vendid	=> 0x1055,
+		devid	=> 0x9463,
+		procid	=> "SMSC Victory66 South Bridge",
+		driver	=> "i2c-piix4",
+	}, {
+		vendid	=> 0x1166,
+		devid	=> 0x0201,
+		procid	=> "ServerWorks CSB5 South Bridge",
+		driver	=> "i2c-piix4",
+	}, {
+		vendid	=> 0x1166,
+		devid	=> 0x0203,
+		procid	=> "ServerWorks CSB6 South Bridge",
+		driver	=> "i2c-piix4",
+	}, {
+		vendid	=> 0x1166,
+		devid	=> 0x0205,
+		procid	=> "ServerWorks HT-1000 South Bridge",
+		driver	=> "i2c-piix4",
+	}, {
+		vendid	=> 0x1002,
+		devid	=> 0x4353,
+		procid	=> "ATI Technologies Inc ATI SMBus",
+		driver	=> "i2c-piix4",
+	}, {
+		vendid	=> 0x1002,
+		devid	=> 0x4363,
+		procid	=> "ATI Technologies Inc ATI SMBus",
+		driver	=> "i2c-piix4",
+	}, {
+		vendid	=> 0x1002,
+		devid	=> 0x4372,
+		procid	=> "ATI Technologies Inc IXP SB400 SMBus Controller",
+		driver	=> "i2c-piix4",
+	}, {
+		vendid	=> 0x1002,
+		devid	=> 0x4385,
+		procid	=> "ATI Technologies Inc SB600/SB700/SB800 SMBus",
+		driver	=> "i2c-piix4",
+	}, {
+		vendid	=> 0x1022,
+		devid	=> 0x780b,
+		procid	=> "AMD Hudson-2 SMBus",
+		driver	=> "i2c-piix4",
+	}, {
+		vendid	=> 0x100B,
+		devid	=> 0x0500,
+		procid	=> "SCx200 Bridge",
+		driver	=> "scx200_acb",
+	}, {
+		vendid	=> 0x100B,
+		devid	=> 0x0510,
+		procid	=> "SC1100 Bridge",
+		driver	=> "scx200_acb",
+	}, {
+		vendid	=> 0x100B,
+		devid	=> 0x002B,
+		procid	=> "CS5535 ISA bridge",
+		driver	=> "scx200_acb",
+	}, {
+		vendid	=> 0x1022,
+		devid	=> 0x2090,
+		procid	=> "CS5536 [Geode companion] ISA",
+		driver	=> "scx200_acb",
+	}
+);
+
+# Look-up table to find out an I2C bus' driver based on the bus name.
+# The match field should contain a regular expression matching the I2C
+# bus name as it would appear in sysfs.
+# Note that new drivers probably don't need to be added to this table
+# if they bind to their device, as we will be able to get the driver name
+# from sysfs directly.
+use vars qw(@i2c_adapter_names);
+@i2c_adapter_names = (
+	{ driver => "i2c-piix4",	match => qr/^SMBus PIIX4 adapter at / },
+	{ driver => "i2c-i801",		match => qr/^SMBus I801 adapter at / },
+	{ driver => "i2c-via",		match => qr/^VIA i2c/ },
+	{ driver => "i2c-viapro",	match => qr/^SMBus V(IA|ia) Pro adapter at / },
+	{ driver => "i2c-sis5595",	match => qr/^SMBus SIS5595 adapter at / },
+	{ driver => "i2c-sis630",	match => qr/^SMBus SIS630 adapter at / },
+	{ driver => "i2c-sis96x",	match => qr/^SiS96x SMBus adapter at / },
+	{ driver => "i2c-ali15x3",	match => qr/^SMBus ALI15X3 adapter at / },
+	{ driver => "i2c-ali1535",	match => qr/^SMBus ALI1535 adapter at/ },
+	{ driver => "i2c-ali1563",	match => qr/^SMBus ALi 1563 Adapter @ / },
+	{ driver => "i2c-amd756",	match => qr/^SMBus (AMD756|AMD766|AMD768|AMD8111|nVidia nForce) adapter at / },
+	{ driver => "i2c-amd8111",	match => qr/^SMBus2 AMD8111 adapter at / },
+	{ driver => "i2c-nforce2",	match => qr/^SMBus nForce2 adapter at / },
+	{ driver => "scx200_acb",	match => qr/^(NatSemi SCx200 ACCESS\.bus|SCx200 ACB\d+|CS553[56] ACB\d+)/ },
+);
+
+# This is a list of all recognized I2C and ISA chips.
+# Each entry must have the following fields:
+#  name: The full chip name
+#  driver: The driver name. Put in exactly:
+#      * "to-be-written" if it is not yet available
+#      * "use-isa-instead" if no i2c driver will be written
+#  i2c_addrs (optional): For I2C chips, the list of I2C addresses to
+#      probe.
+#  i2c_detect (optional): For I2C chips, the function to call to detect
+#      this chip. The function will be passed two parameters: an open file
+#      descriptor to access the bus, and the I2C address to probe.
+#  isa_addrs (optional): For ISA chips, the list of port addresses to
+#      probe.
+#  isa_detect (optional): For ISA chips, the function to call to detect
+#      this chip. The function will be passed one parameter: the ISA address
+#      to probe.
+#  alias_detect (optional): For chips which can be both on the ISA and the
+#      I2C bus, a function which detects whether two entries are the same.
+#      The function will be passed three parameters: the ISA address, an
+#      open file descriptor to access the I2C bus, and the I2C address.
+@chip_ids = (
+	{
+		name => "Myson MTP008",
+		driver => "mtp008",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { mtp008_detect(@_); },
+	}, {
+		name => "National Semiconductor LM78",
+		driver => "lm78",
+		i2c_addrs => [0x28..0x2f],
+		i2c_detect => sub { lm78_detect(@_, 0); },
+		isa_addrs => [0x290],
+		isa_detect => sub { lm78_isa_detect(@_, 0); },
+		alias_detect => sub { winbond_alias_detect(@_, 0x2b, 0x3d); },
+	}, {
+		name => "National Semiconductor LM79",
+		driver => "lm78",
+		i2c_addrs => [0x28..0x2f],
+		i2c_detect => sub { lm78_detect(@_, 2); },
+		isa_addrs => [0x290],
+		isa_detect => sub { lm78_isa_detect(@_, 2); },
+		alias_detect => sub { winbond_alias_detect(@_, 0x2b, 0x3d); },
+	}, {
+		name => "National Semiconductor LM75",
+		driver => "lm75",
+		i2c_addrs => [0x48..0x4f],
+		i2c_detect => sub { lm75_detect(@_, 0); },
+	}, {
+		name => "National Semiconductor LM75A",
+		driver => "lm75",
+		i2c_addrs => [0x48..0x4f],
+		i2c_detect => sub { lm75_detect(@_, 2); },
+	}, {
+		name => "Dallas Semiconductor DS75",
+		driver => "lm75",
+		i2c_addrs => [0x48..0x4f],
+		i2c_detect => sub { lm75_detect(@_, 1); },
+	}, {
+		name => "National Semiconductor LM77",
+		driver => "lm77",
+		i2c_addrs => [0x48..0x4b],
+		i2c_detect => sub { lm77_detect(@_); },
+	}, {
+		name => "National Semiconductor LM80",
+		driver => "lm80",
+		i2c_addrs => [0x28..0x2f],
+		i2c_detect => sub { lm80_detect(@_); },
+	}, {
+		name => "National Semiconductor LM85",
+		driver => "lm85",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { lm85_detect(@_, 0); },
+	}, {
+		name => "National Semiconductor LM96000 or PC8374L",
+		driver => "lm85",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { lm85_detect(@_, 1); },
+	}, {
+		name => "Analog Devices ADM1027",
+		driver => "lm85",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { lm85_detect(@_, 2); },
+	}, {
+		name => "Analog Devices ADT7460 or ADT7463",
+		driver => "lm85",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { lm85_detect(@_, 3); },
+	}, {
+		name => "SMSC EMC6D100 or EMC6D101",
+		driver => "lm85",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { lm85_detect(@_, 4); },
+	}, {
+		name => "SMSC EMC6D102",
+		driver => "lm85",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { lm85_detect(@_, 5); },
+	}, {
+		name => "SMSC EMC6D103",
+		driver => "lm85",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { lm85_detect(@_, 6); },
+	}, {
+		name => "SMSC EMC6D103S",
+		driver => "to-be-written",	# lm85
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { lm85_detect(@_, 8); },
+	}, {
+		name => "Winbond WPCD377I",
+		driver => "not-a-sensor",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { lm85_detect(@_, 7); },
+	}, {
+		name => "Analog Devices ADT7462",
+		driver => "adt7462",
+		i2c_addrs => [0x5c, 0x58],
+		i2c_detect => sub { adt7467_detect(@_, 2); },
+	}, {
+		name => "Analog Devices ADT7466",
+		driver => "to-be-written",
+		i2c_addrs => [0x4c],
+		i2c_detect => sub { adt7467_detect(@_, 3); },
+	}, {
+		name => "Analog Devices ADT7467 or ADT7468",
+		driver => "lm85",
+		i2c_addrs => [0x2e],
+		i2c_detect => sub { adt7467_detect(@_, 0); },
+	}, {
+		name => "Analog Devices ADT7470",
+		driver => "adt7470",
+		i2c_addrs => [0x2c, 0x2e, 0x2f],
+		i2c_detect => sub { adt7467_detect(@_, 4); },
+	}, {
+		name => "Analog Devices ADT7473",
+		driver => sub { kernel_version_at_least(2, 6, 33) ? "adt7475" : "adt7473" },
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { adt7473_detect(@_, 0); },
+	}, {
+		name => "Analog Devices ADT7475",
+		driver => "adt7475",
+		i2c_addrs => [0x2e],
+		i2c_detect => sub { adt7473_detect(@_, 1); },
+	}, {
+		name => "Analog Devices ADT7476",
+		driver => "adt7475",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { adt7467_detect(@_, 1); },
+	}, {
+		name => "Analog Devices ADT7490",
+		driver => "adt7475",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { adt7490_detect(@_); },
+	}, {
+		name => "Analog Devices ADT7410",
+		driver => "to-be-written",
+		i2c_addrs => [0x48..0x4b],
+		i2c_detect => sub { adt7410_detect(@_); },
+	}, {
+		name => "Analog Devices ADT7411",
+		driver => "to-be-written",
+		i2c_addrs => [0x48, 0x4a, 0x4b],
+		i2c_detect => sub { adt7411_detect(@_); },
+	}, {
+		name => "Andigilog aSC7511",
+		driver => "to-be-written",
+		i2c_addrs => [0x4c],
+		i2c_detect => sub { andigilog_aSC7511_detect(@_); },
+	}, {
+		name => "Andigilog aSC7512",
+		driver => "to-be-written",
+		i2c_addrs => [0x58],
+		i2c_detect => sub { andigilog_detect(@_, 0); },
+	}, {
+		name => "Andigilog aSC7611",
+		driver => "to-be-written",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { andigilog_detect(@_, 1); },
+	}, {
+		name => "Andigilog aSC7621",
+		driver => "asc7621",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { andigilog_detect(@_, 2); },
+	}, {
+		name => "National Semiconductor LM87",
+		driver => "lm87",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { lm87_detect(@_, 0); },
+	}, {
+		name => "Analog Devices ADM1024",
+		driver => "lm87",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { lm87_detect(@_, 1); },
+	}, {
+		name => "National Semiconductor LM93",
+		driver => "lm93",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { lm93_detect(@_, 0); },
+	}, {
+		name => "National Semiconductor LM94",
+		driver => "to-be-written",	# Most likely lm93
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { lm93_detect(@_, 1); },
+	}, {
+		name => "Winbond W83781D",
+		driver => "w83781d",
+		i2c_addrs => [0x28..0x2f],
+		i2c_detect => sub { w83781d_detect(@_, 0); },
+		isa_addrs => [0x290],
+		isa_detect => sub { w83781d_isa_detect(@_, 0); },
+		alias_detect => sub { winbond_alias_detect(@_, 0x2b, 0x3d); },
+	}, {
+		name => "Winbond W83782D",
+		driver => "w83781d",
+		i2c_addrs => [0x28..0x2f],
+		i2c_detect => sub { w83781d_detect(@_, 1); },
+		isa_addrs => [0x290],
+		isa_detect => sub { w83781d_isa_detect(@_, 1); },
+		alias_detect => sub { winbond_alias_detect(@_, 0x2b, 0x3d); },
+	}, {
+		name => "Winbond W83783S",
+		driver => "w83781d",
+		i2c_addrs => [0x2d],
+		i2c_detect => sub { w83781d_detect(@_, 2); },
+	}, {
+		name => "Winbond W83791D",
+		driver => "w83791d",
+		i2c_addrs => [0x2c..0x2f],
+		i2c_detect => sub { w83781d_detect(@_, 7); },
+	}, {
+		name => "Winbond W83792D",
+		driver => "w83792d",
+		i2c_addrs => [0x2c..0x2f],
+		i2c_detect => sub { w83781d_detect(@_, 8); },
+	}, {
+		name => "Winbond W83793R/G",
+		driver => "w83793",
+		i2c_addrs => [0x2c..0x2f],
+		i2c_detect => sub { w83793_detect(@_); },
+	}, {
+		name => "Nuvoton W83795G/ADG",
+		driver => "w83795",
+		i2c_addrs => [0x2c..0x2f],
+		i2c_detect => sub { w83795_detect(@_); },
+	}, {
+		name => "Winbond W83627HF",
+		driver => "use-isa-instead",
+		i2c_addrs => [0x28..0x2f],
+		i2c_detect => sub { w83781d_detect(@_, 3); },
+	}, {
+		name => "Winbond W83627EHF",
+		driver => "use-isa-instead",
+		i2c_addrs => [0x28..0x2f],
+		i2c_detect => sub { w83781d_detect(@_, 9); },
+	}, {
+		name => "Winbond W83627DHG/W83667HG/W83677HG",
+		driver => "use-isa-instead",
+		i2c_addrs => [0x28..0x2f],
+		i2c_detect => sub { w83781d_detect(@_, 10); },
+	}, {
+		name => "Asus AS99127F (rev.1)",
+		driver => "w83781d",
+		i2c_addrs => [0x28..0x2f],
+		i2c_detect => sub { w83781d_detect(@_, 4); },
+	}, {
+		name => "Asus AS99127F (rev.2)",
+		driver => "w83781d",
+		i2c_addrs => [0x28..0x2f],
+		i2c_detect => sub { w83781d_detect(@_, 5); },
+	}, {
+		name => "Asus ASB100 Bach",
+		driver => "asb100",
+		i2c_addrs => [0x28..0x2f],
+		i2c_detect => sub { w83781d_detect(@_, 6); },
+	}, {
+		name => "Asus Mozart-2",
+		driver => "to-be-written",
+		i2c_addrs => [0x77],
+		i2c_detect => sub { mozart_detect(@_); },
+	}, {
+		name => "Winbond W83L784R/AR/G",
+		driver => "to-be-written",
+		i2c_addrs => [0x2d],
+		i2c_detect => sub { w83l784r_detect(@_, 0); },
+	}, {
+		name => "Winbond W83L785R/G",
+		driver => "to-be-written",
+		i2c_addrs => [0x2d],
+		i2c_detect => sub { w83l784r_detect(@_, 1); },
+	}, {
+		name => "Winbond W83L786NR/NG/R/G",
+		driver => "w83l786ng",
+		i2c_addrs => [0x2e, 0x2f],
+		i2c_detect => sub { w83l784r_detect(@_, 2); },
+	}, {
+		name => "Winbond W83L785TS-S",
+		driver => "w83l785ts",
+		i2c_addrs => [0x2e],
+		i2c_detect => sub { w83l784r_detect(@_, 3); },
+	}, {
+		name => "Genesys Logic GL518SM",
+		driver => "gl518sm",
+		i2c_addrs => [0x2c, 0x2d],
+		i2c_detect => sub { gl518sm_detect(@_, 0); },
+	}, {
+		name => "Genesys Logic GL520SM",
+		driver => "gl520sm",
+		i2c_addrs => [0x2c, 0x2d],
+		i2c_detect => sub { gl518sm_detect(@_, 1); },
+	}, {
+		name => "Genesys Logic GL525SM",
+		driver => "to-be-written",
+		i2c_addrs => [0x2d],
+		i2c_detect => sub { gl525sm_detect(@_); },
+	}, {
+		name => "Analog Devices ADM9240",
+		driver => "adm9240",
+		i2c_addrs => [0x2c..0x2f],
+		i2c_detect => sub { adm9240_detect(@_, 0); },
+	}, {
+		name => "Dallas Semiconductor DS1621/DS1631",
+		driver => "ds1621",
+		i2c_addrs => [0x48..0x4f],
+		i2c_detect => sub { ds1621_detect(@_); },
+	}, {
+		name => "Dallas Semiconductor DS1780",
+		driver => "adm9240",
+		i2c_addrs => [0x2c..0x2f],
+		i2c_detect => sub { adm9240_detect(@_, 1); },
+	}, {
+		name => "National Semiconductor LM81",
+		driver => "adm9240",
+		i2c_addrs => [0x2c..0x2f],
+		i2c_detect => sub { adm9240_detect(@_, 2); },
+	}, {
+		name => "Analog Devices ADM1026",
+		driver => "adm1026",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { adm1026_detect(@_); },
+	}, {
+		name => "Analog Devices ADM1025",
+		driver => "adm1025",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { adm1025_detect(@_, 0); },
+	}, {
+		name => "Philips NE1619",
+		driver => "adm1025",
+		i2c_addrs => [0x2c..0x2d],
+		i2c_detect => sub { adm1025_detect(@_, 1); },
+	}, {
+		name => "Analog Devices ADM1021",
+		driver => "adm1021",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { adm1021_detect(@_, 0); },
+	}, {
+		name => "Analog Devices ADM1021A/ADM1023",
+		driver => "adm1021",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { adm1021_detect(@_, 1); },
+	}, {
+		name => "Maxim MAX1617",
+		driver => "adm1021",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { adm1021_detect(@_, 2); },
+	}, {
+		name => "Maxim MAX1617A",
+		driver => "adm1021",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { adm1021_detect(@_, 3); },
+	}, {
+		name => "Maxim MAX1668",
+		driver => "max1668",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { max1668_detect(@_, 0); },
+	}, {
+		name => "Maxim MAX1805",
+		driver => "max1668",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { max1668_detect(@_, 1); },
+	}, {
+		name => "Maxim MAX1989",
+		driver => "max1668",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { max1668_detect(@_, 2); },
+	}, {
+		name => "Maxim MAX6639",
+		driver => "max6639",
+		i2c_addrs => [0x2c, 0x2e, 0x2f],
+		i2c_detect => sub { max6639_detect(@_); },
+	}, {
+		name => "Maxim MAX6650/MAX6651",
+		driver => "max6650",
+		i2c_addrs => [0x1b, 0x1f, 0x48, 0x4b],
+		i2c_detect => sub { max6650_detect(@_); },
+	}, {
+		name => "Maxim MAX6655/MAX6656",
+		driver => "max6655",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { max6655_detect(@_); },
+	}, {
+		name => "TI THMC10",
+		driver => "adm1021",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { adm1021_detect(@_, 4); },
+	}, {
+		name => "National Semiconductor LM84",
+		driver => "adm1021",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { adm1021_detect(@_, 5); },
+	}, {
+		name => "Genesys Logic GL523SM",
+		driver => "adm1021",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { adm1021_detect(@_, 6); },
+	}, {
+		name => "Onsemi MC1066",
+		driver => "adm1021",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { adm1021_detect(@_, 7); },
+	}, {
+		name => "Maxim MAX1618",
+		driver => "max1619",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { max1619_detect(@_, 1); },
+	}, {
+		name => "Maxim MAX1619",
+		driver => "max1619",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { max1619_detect(@_, 0); },
+	}, {
+		name => "National Semiconductor LM82/LM83",
+		driver => "lm83",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { lm83_detect(@_); },
+	}, {
+		name => "National Semiconductor LM90",
+		driver => "lm90",
+		i2c_addrs => [0x4c],
+		i2c_detect => sub { lm90_detect(@_, 0); },
+	}, {
+		name => "National Semiconductor LM89/LM99",
+		driver => "lm90",
+		i2c_addrs => [0x4c..0x4d],
+		i2c_detect => sub { lm90_detect(@_, 1); },
+	}, {
+		name => "National Semiconductor LM86",
+		driver => "lm90",
+		i2c_addrs => [0x4c],
+		i2c_detect => sub { lm90_detect(@_, 2); },
+	}, {
+		name => "Analog Devices ADM1032",
+		driver => "lm90",
+		i2c_addrs => [0x4c..0x4d],
+		i2c_detect => sub { lm90_detect(@_, 3); },
+	}, {
+		name => "Maxim MAX6654",
+		driver => "to-be-written", # probably lm90
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { lm90_detect(@_, 4); },
+	}, {
+		name => "Maxim MAX6690",
+		driver => "to-be-written", # probably lm90
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { lm90_detect(@_, 12); },
+	}, {
+		name => "Maxim MAX6657/MAX6658/MAX6659",
+		driver => "lm90",
+		i2c_addrs => [0x4c],
+		i2c_detect => sub { max6657_detect(@_); },
+	}, {
+		name => "Maxim MAX6659",
+		driver => "lm90",
+		i2c_addrs => [0x4d..0x4e], # 0x4c is handled above
+		i2c_detect => sub { max6657_detect(@_); },
+	}, {
+		name => "Maxim MAX6646",
+		driver => "lm90",
+		i2c_addrs => [0x4d],
+		i2c_detect => sub { lm90_detect(@_, 6); },
+	}, {
+		name => "Maxim MAX6647",
+		driver => "lm90",
+		i2c_addrs => [0x4e],
+		i2c_detect => sub { lm90_detect(@_, 6); },
+	}, {
+		name => "Maxim MAX6648/MAX6649/MAX6692",
+		driver => "lm90",
+		i2c_addrs => [0x4c],
+		i2c_detect => sub { lm90_detect(@_, 6); },
+	}, {
+		name => "Maxim MAX6680/MAX6681",
+		driver => "lm90",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { max6680_95_detect(@_, 0); },
+	}, {
+		name => "Maxim MAX6695/MAX6696",
+		driver => "lm90",
+		i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
+		i2c_detect => sub { max6680_95_detect(@_, 1); },
+	}, {
+		name => "Winbond W83L771W/G",
+		driver => "to-be-written",
+		i2c_addrs => [0x4c],
+		i2c_detect => sub { lm90_detect(@_, 8); },
+	}, {
+		name => "Winbond W83L771AWG/ASG",
+		driver => "lm90",
+		i2c_addrs => [0x4c],
+		i2c_detect => sub { lm90_detect(@_, 11); },
+	}, {
+		name => "Texas Instruments TMP401",
+		driver => "tmp401",
+		i2c_addrs => [0x4c],
+		i2c_detect => sub { lm90_detect(@_, 9); },
+	}, {
+		name => "Texas Instruments TMP411",
+		driver => "tmp401",
+		i2c_addrs => [0x4c..0x4e],
+		i2c_detect => sub { lm90_detect(@_, 10); },
+	}, {
+		name => "Texas Instruments TMP421",
+		driver => "tmp421",
+		i2c_addrs => [0x1c..0x1f, 0x2a, 0x4c..0x4f],
+		i2c_detect => sub { tmp42x_detect(@_, 0); },
+	}, {
+		name => "Texas Instruments TMP422",
+		driver => "tmp421",
+		i2c_addrs => [0x4c..0x4f],
+		i2c_detect => sub { tmp42x_detect(@_, 1); },
+	}, {
+		name => "Texas Instruments TMP423",
+		driver => "tmp421",
+		i2c_addrs => [0x4c, 0x4d],
+		i2c_detect => sub { tmp42x_detect(@_, 2); },
+	}, {
+		name => "Texas Instruments AMC6821",
+		driver => "amc6821",
+		i2c_addrs => [0x18..0x1a, 0x2c..0x2e, 0x4c..0x4e],
+		i2c_detect => sub { amc6821_detect(@_); },
+	}, {
+		name => "National Semiconductor LM95231",
+		driver => "to-be-written",
+		i2c_addrs => [0x2b, 0x19, 0x2a],
+		i2c_detect => sub { lm95231_detect(@_, 0); },
+	}, {
+		name => "National Semiconductor LM95241",
+		driver => "lm95241",
+		i2c_addrs => [0x2b, 0x19, 0x2a],
+		i2c_detect => sub { lm95231_detect(@_, 1); },
+	}, {
+		name => "National Semiconductor LM63",
+		driver => "lm63",
+		i2c_addrs => [0x4c],
+		i2c_detect => sub { lm63_detect(@_, 1); },
+	}, {
+		name => "National Semiconductor LM64",
+		driver => "lm63",
+		i2c_addrs => [0x18, 0x4e],
+		i2c_detect => sub { lm63_detect(@_, 3); },
+	}, {
+		name => "Fintek F75363SG",
+		driver => "lm63", # Not yet
+		i2c_addrs => [0x4c],
+		i2c_detect => sub { lm63_detect(@_, 2); },
+	}, {
+		name => "National Semiconductor LM73",
+		driver => "lm73",
+		i2c_addrs => [0x48..0x4a, 0x4c..0x4e],
+		i2c_detect => sub { lm73_detect(@_); },
+	}, {
+		name => "National Semiconductor LM92",
+		driver => "lm92",
+		i2c_addrs => [0x48..0x4b],
+		i2c_detect => sub { lm92_detect(@_, 0); },
+	}, {
+		name => "National Semiconductor LM76",
+		driver => "lm92",
+		i2c_addrs => [0x48..0x4b],
+		i2c_detect => sub { lm92_detect(@_, 1); },
+	}, {
+		name => "Maxim MAX6633/MAX6634/MAX6635",
+		driver => "lm92",
+		i2c_addrs => [0x48..0x4f], # The MAX6633 can also use 0x40-0x47 but we
+					   # don't want to probe these addresses, it's
+					   # dangerous.
+		i2c_detect => sub { lm92_detect(@_, 2); },
+	}, {
+		name => "Analog Devices ADT7461",
+		driver => "lm90",
+		i2c_addrs => [0x4c..0x4d],
+		i2c_detect => sub { lm90_detect(@_, 5); },
+	}, {
+		name => "Analog Devices ADT7481",
+		driver => "to-be-written",
+		i2c_addrs => [0x4c, 0x4b],
+		i2c_detect => sub { adt7481_detect(@_); },
+	}, {
+		name => "Analog Devices ADM1029",
+		driver => "adm1029",
+		i2c_addrs => [0x28..0x2f],
+		i2c_detect => sub { adm1029_detect(@_); },
+	}, {
+		name => "Analog Devices ADM1030",
+		driver => "adm1031",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { adm1031_detect(@_, 0); },
+	}, {
+		name => "Analog Devices ADM1031",
+		driver => "adm1031",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { adm1031_detect(@_, 1); },
+	}, {
+		name => "Analog Devices ADM1033",
+		driver => "to-be-written",
+		i2c_addrs => [0x50..0x53],
+		i2c_detect => sub { adm1034_detect(@_, 0); },
+	}, {
+		name => "Analog Devices ADM1034",
+		driver => "to-be-written",
+		i2c_addrs => [0x50..0x53],
+		i2c_detect => sub { adm1034_detect(@_, 1); },
+	}, {
+		name => "Analog Devices ADM1022",
+		driver => "thmc50",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { adm1022_detect(@_, 0); },
+	}, {
+		name => "Texas Instruments THMC50",
+		driver => "thmc50",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { adm1022_detect(@_, 1); },
+	}, {
+		name => "Analog Devices ADM1028",
+		driver => "thmc50",
+		i2c_addrs => [0x2e],
+		i2c_detect => sub { adm1022_detect(@_, 2); },
+	}, {
+		name => "Texas Instruments THMC51",
+		driver => "to-be-written", # thmc50
+		i2c_addrs => [0x2e], # At least (no datasheet)
+		i2c_detect => sub { adm1022_detect(@_, 3); },
+	}, {
+		name => "VIA VT1211 (I2C)",
+		driver => "use-isa-instead",
+		i2c_addrs => [0x2d],
+		i2c_detect => sub { vt1211_i2c_detect(@_); },
+	}, {
+		name => "ITE IT8712F",
+		driver => "it87",
+		i2c_addrs => [0x28..0x2f],
+		i2c_detect => sub { it8712_i2c_detect(@_); },
+	}, {
+		name => "FSC Poseidon I",
+		driver => sub { kernel_version_at_least(2, 6, 24) ? "fschmd" : "fscpos" },
+		i2c_addrs => [0x73],
+		i2c_detect => sub { fsc_detect(@_, 0); },
+	}, {
+		name => "FSC Poseidon II",
+		driver => "to-be-written",
+		i2c_addrs => [0x73],
+		i2c_detect => sub { fsc_detect(@_, 1); },
+	}, {
+		name => "FSC Scylla",
+		driver => "fschmd",
+		i2c_addrs => [0x73],
+		i2c_detect => sub { fsc_detect(@_, 2); },
+	}, {
+		name => "FSC Hermes",
+		driver => sub { kernel_version_at_least(2, 6, 24) ? "fschmd" : "fscher" },
+		i2c_addrs => [0x73],
+		i2c_detect => sub { fsc_detect(@_, 3); },
+	}, {
+		name => "FSC Heimdal",
+		driver => "fschmd",
+		i2c_addrs => [0x73],
+		i2c_detect => sub { fsc_detect(@_, 4); },
+	}, {
+		name => "FSC Heracles",
+		driver => "fschmd",
+		i2c_addrs => [0x73],
+		i2c_detect => sub { fsc_detect(@_, 5); },
+	}, {
+		name => "FSC Hades",
+		driver => "fschmd",
+		i2c_addrs => [0x73],
+		i2c_detect => sub { fsc_detect(@_, 6); },
+	}, {
+		name => "FSC Syleus",
+		driver => "fschmd",
+		i2c_addrs => [0x73],
+		i2c_detect => sub { fsc_detect(@_, 7); },
+	}, {
+		name => "ALi M5879",
+		driver => "to-be-written",
+		i2c_addrs => [0x2c..0x2d],
+		i2c_detect => sub { m5879_detect(@_); },
+	}, {
+		name => "SMSC LPC47M15x/192/292/997",
+		driver => "smsc47m192",
+		i2c_addrs => [0x2c..0x2d],
+		i2c_detect => sub { smsc47m192_detect(@_); },
+	}, {
+		name => "SMSC DME1737",
+		driver => "dme1737",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { dme1737_detect(@_, 1); },
+	}, {
+		name => "SMSC SCH5027D-NW",
+		driver => "dme1737",
+		i2c_addrs => [0x2c..0x2e],
+		i2c_detect => sub { dme1737_detect(@_, 2); },
+	}, {
+		name => "SMSC EMC2103",
+		driver => "emc2103",
+		i2c_addrs => [0x2e],
+		i2c_detect => sub { emc1403_detect(@_, 2); },
+	}, {
+		name => "Fintek F75121R/F75122R/RG (VID+GPIO)",
+		driver => "to-be-written",
+		i2c_addrs => [0x4e], # 0x37 not probed
+		i2c_detect => sub { fintek_detect(@_, 2); },
+	}, {
+		name => "Fintek F75373S/SG",
+		driver => "f75375s",
+		i2c_addrs => [0x2d..0x2e],
+		i2c_detect => sub { fintek_detect(@_, 3); },
+	}, {
+		name => "Fintek F75375S/SP",
+		driver => "f75375s",
+		i2c_addrs => [0x2d..0x2e],
+		i2c_detect => sub { fintek_detect(@_, 4); },
+	}, {
+		name => "Fintek F75387SG/RG",
+		driver => "to-be-written",
+		i2c_addrs => [0x2d..0x2e],
+		i2c_detect => sub { fintek_detect(@_, 5); },
+	}, {
+		name => "Fintek F75383S/M",
+		driver => "to-be-written",
+		i2c_addrs => [0x4c],
+		i2c_detect => sub { fintek_detect(@_, 6); },
+	}, {
+		name => "Fintek F75384S/M",
+		driver => "to-be-written",
+		i2c_addrs => [0x4d],
+		i2c_detect => sub { fintek_detect(@_, 6); },
+	}, {
+		name => "Fintek custom power control IC",
+		driver => "to-be-written",
+		i2c_addrs => [0x2f],
+		i2c_detect => sub { fintek_detect(@_, 7); },
+	}, {
+		name => "SMSC EMC1002",
+		driver => "to-be-written",
+		i2c_addrs => [0x4c, 0x4d], # 0x3c, 0x3d not probed
+		i2c_detect => sub { emc1403_detect(@_, 4); },
+	}, {
+		name => "SMSC EMC1023",
+		driver => "to-be-written",	# emc1023
+		i2c_addrs => [0x48, 0x49, 0x4c, 0x4d],
+		i2c_detect => sub { emc1023_detect(@_, 0); },
+	}, {
+		name => "SMSC EMC1033",
+		driver => "to-be-written",
+		i2c_addrs => [0x4c, 0x4d], # 0x3c, 0x3d not probed
+		i2c_detect => sub { emc1403_detect(@_, 5); },
+	}, {
+		name => "SMSC EMC1043",
+		driver => "to-be-written",	# emc1023
+		i2c_addrs => [0x48, 0x49, 0x4c, 0x4d],
+		i2c_detect => sub { emc1023_detect(@_, 1); },
+	}, {
+		name => "SMSC EMC1046",
+		driver => "to-be-written",
+		i2c_addrs => [0x4c, 0x4d],
+		i2c_detect => sub { emc1403_detect(@_, 6); },
+	}, {
+		name => "SMSC EMC1047",
+		driver => "to-be-written",
+		i2c_addrs => [0x18], # 0x10 not probed
+		i2c_detect => sub { emc1403_detect(@_, 7); },
+	}, {
+		name => "SMSC EMC1053",
+		driver => "to-be-written",	# emc1023
+		i2c_addrs => [0x48, 0x49, 0x4c, 0x4d],
+		i2c_detect => sub { emc1023_detect(@_, 2); },
+	}, {
+		name => "SMSC EMC1063",
+		driver => "to-be-written",	# emc1023
+		i2c_addrs => [0x48, 0x49, 0x4c, 0x4d],
+		i2c_detect => sub { emc1023_detect(@_, 3); },
+	}, {
+		name => "SMSC EMC1072",
+		driver => "to-be-written",
+		i2c_addrs => [0x1c, 0x4c, 0x5c], # 0x2c, 0x3c, 0x6c, 0x7c not probed
+		i2c_detect => sub { emc1403_detect(@_, 8); },
+	}, {
+		name => "SMSC EMC1073",
+		driver => "to-be-written",
+		i2c_addrs => [0x1c, 0x4c, 0x5c], # 0x2c, 0x3c, 0x6c, 0x7c not probed
+		i2c_detect => sub { emc1403_detect(@_, 9); },
+	}, {
+		name => "SMSC EMC1074",
+		driver => "to-be-written",
+		i2c_addrs => [0x1c, 0x4c, 0x5c], # 0x2c, 0x3c, 0x6c, 0x7c not probed
+		i2c_detect => sub { emc1403_detect(@_, 10); },
+	}, {
+		name => "SMSC EMC1402",
+		driver => "to-be-written",
+		i2c_addrs => [0x18, 0x29, 0x4c, 0x4d],
+		i2c_detect => sub { emc1403_detect(@_, 11); },
+	}, {
+		name => "SMSC EMC1403",
+		driver => "emc1403",
+		i2c_addrs => [0x18, 0x29, 0x4c, 0x4d],
+		i2c_detect => sub { emc1403_detect(@_, 0); },
+	}, {
+		name => "SMSC EMC1404",
+		driver => "to-be-written", # emc1403
+		i2c_addrs => [0x18, 0x29, 0x4c, 0x4d],
+		i2c_detect => sub { emc1403_detect(@_, 1); },
+	}, {
+		name => "SMSC EMC1423",
+		driver => "emc1403",
+		i2c_addrs => [0x4c],
+		i2c_detect => sub { emc1403_detect(@_, 3); },
+	}, {
+		name => "SMSC EMC1424",
+		driver => "to-be-written",
+		i2c_addrs => [0x4c],
+		i2c_detect => sub { emc1403_detect(@_, 12); },
+	}, {
+		name => "ST STTS424",
+		driver => "jc42",
+		i2c_addrs => [0x18..0x1f],
+		i2c_detect => sub { jedec_JC42_4_detect(@_, 0); },
+	}, {
+		name => "ST STTS424E",
+		driver => "jc42",
+		i2c_addrs => [0x18..0x1f],
+		i2c_detect => sub { jedec_JC42_4_detect(@_, 10); },
+	}, {
+		name => "NXP SE97/SE97B",
+		driver => "jc42",
+		i2c_addrs => [0x18..0x1f],
+		i2c_detect => sub { jedec_JC42_4_detect(@_, 1); },
+	}, {
+		name => "NXP SE98",
+		driver => "jc42",
+		i2c_addrs => [0x18..0x1f],
+		i2c_detect => sub { jedec_JC42_4_detect(@_, 2); },
+	}, {
+		name => "Analog Devices ADT7408",
+		driver => "jc42",
+		i2c_addrs => [0x18..0x1f],
+		i2c_detect => sub { jedec_JC42_4_detect(@_, 3); },
+	}, {
+		name => "IDT TS3000/TSE2002",
+		driver => "jc42",
+		i2c_addrs => [0x18..0x1f],
+		i2c_detect => sub { jedec_JC42_4_detect(@_, 4); },
+	}, {
+		name => "Maxim MAX6604",
+		driver => "jc42",
+		i2c_addrs => [0x18..0x1f],
+		i2c_detect => sub { jedec_JC42_4_detect(@_, 5); },
+	}, {
+		name => "Microchip MCP98242",
+		driver => "jc42",
+		i2c_addrs => [0x18..0x1f],
+		i2c_detect => sub { jedec_JC42_4_detect(@_, 6); },
+	}, {
+		name => "Microchip MCP98243",
+		driver => "jc42",
+		i2c_addrs => [0x18..0x1f],
+		i2c_detect => sub { jedec_JC42_4_detect(@_, 7); },
+	}, {
+		name => "Microchip MCP9843",
+		driver => "jc42",
+		i2c_addrs => [0x18..0x1f],
+		i2c_detect => sub { jedec_JC42_4_detect(@_, 8); },
+	}, {
+		name => "ON CAT6095/CAT34TS02",
+		driver => "jc42",
+		i2c_addrs => [0x18..0x1f],
+		i2c_detect => sub { jedec_JC42_4_detect(@_, 9); },
+	}, {
+		name => "Smart Battery",
+		driver => "sbs", # ACPI driver, not sure if it always works
+		i2c_addrs => [0x0b],
+		i2c_detect => sub { smartbatt_detect(@_); },
+	}
+);
+
+# IPMI interfaces have their own array now
+@ipmi_ifs = (
+	{
+		name => "IPMI BMC KCS",
+		driver => "ipmisensors",
+		isa_addrs => [0x0ca0],
+		isa_detect => sub { ipmi_detect(@_); },
+	}, {
+		name => "IPMI BMC SMIC",
+		driver => "ipmisensors",
+		isa_addrs => [0x0ca8],
+		isa_detect => sub { ipmi_detect(@_); },
+	}
+);
+
+# Here is a similar list, but for devices which are not hardware monitoring
+# chips. We only list popular devices which happen to live at the same I2C
+# address as recognized hardware monitoring chips. The idea is to make it
+# clear that the chip in question is of no interest for lm-sensors.
+@non_hwmon_chip_ids = (
+	{
+		name => "Winbond W83791SD",
+		i2c_addrs => [0x2c..0x2f],
+		i2c_detect => sub { w83791sd_detect(@_); },
+	}, {
+		name => "Fintek F75111R/RG/N (GPIO)",
+		i2c_addrs => [0x37, 0x4e],
+		i2c_detect => sub { fintek_detect(@_, 1); },
+	}, {
+		name => "ITE IT8201R/IT8203R/IT8206R/IT8266R",
+		i2c_addrs => [0x4e],
+		i2c_detect => sub { ite_overclock_detect(@_); },
+	}, {
+		name => "SPD EEPROM",
+		i2c_addrs => [0x50..0x57],
+		i2c_detect => sub { eeprom_detect(@_); },
+	}, {
+		name => "EDID EEPROM",
+		i2c_addrs => [0x50],
+		i2c_detect => sub { ddcmonitor_detect(@_); },
+	}
+);
+
+# This is a list of all recognized superio chips.
+# Each entry must have the following fields:
+#  name: The full chip name
+#  driver: The driver name. Put in exactly:
+#      * "to-be-written" if it is not yet available
+#      * "not-a-sensor" if the chip doesn't have hardware monitoring
+#	 capabilities (listing such chips here removes the need of manual
+#	 lookup when people report them)
+#      * "via-smbus-only" if this is a Super-I/O chip whose hardware
+#	 monitoring registers can only be accessed via the SMBus
+#  devid: The device ID we have to match (base device)
+#  devid_mask (optional): Bitmask to apply before checking the device ID
+#  regs (optional): Register definitions, where they differ from the standard.
+#  logdev: The logical device containing the sensors
+#  check (optional): A function to refine the detection. Will be passed
+#      the index and data ports as parameters. Must return 1 for a matching
+#      device, 0 otherwise.
+#  features (optional): Features supported by this device, amongst:
+#      * FEAT_IN
+#      * FEAT_FAN
+#      * FEAT_TEMP
+use vars qw(@superio_ids_natsemi @superio_ids_smsc @superio_ids_smsc_ns
+	    @superio_ids_winbond @superio_ids_ite @superio_ids);
+
+use constant FEAT_IN	=> (1 << 0);
+use constant FEAT_FAN	=> (1 << 1);
+use constant FEAT_TEMP	=> (1 << 2);
+use constant FEAT_SMBUS	=> (1 << 7);
+
+@superio_ids_natsemi = (
+	{
+		name => "Nat. Semi. PC8374L Super IO Sensors",	# Also Nuvoton WPCD374L
+		driver => "to-be-written",
+		devid => 0xf1,
+		check => sub {
+			outb($_[0], 0x27);
+			# Guess work; seen so far: 0x11
+			return (inb($_[1]) < 0x80);
+		},
+		logdev => 0x08,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Nuvoton WPCD377I Super IO",	# Also WPCD376I
+		driver => "not-a-sensor",
+		devid => 0xf1,
+		check => sub {
+			outb($_[0], 0x27);
+			# Guess work; seen so far: 0x91 (twice)
+			return (inb($_[1]) >= 0x80);
+		},
+	}, {
+		name => "Nat. Semi. PC87351 Super IO Fan Sensors",
+		driver => "to-be-written",
+		devid => 0xe2,
+		logdev => 0x08,
+	}, {
+		name => "Nat. Semi. PC87360 Super IO Fan Sensors",
+		driver => "pc87360",
+		devid => 0xe1,
+		logdev => 0x09,
+		features => FEAT_FAN,
+	}, {
+		name => "Nat. Semi. PC87363 Super IO Fan Sensors",
+		driver => "pc87360",
+		devid => 0xe8,
+		logdev => 0x09,
+		features => FEAT_FAN,
+	}, {
+		name => "Nat. Semi. PC87364 Super IO Fan Sensors",
+		driver => "pc87360",
+		devid => 0xe4,
+		logdev => 0x09,
+		features => FEAT_FAN,
+	}, {
+		name => "Nat. Semi. PC87365 Super IO Fan Sensors",
+		driver => "pc87360",
+		devid => 0xe5,
+		logdev => 0x09,
+		features => FEAT_FAN,
+	}, {
+		name => "Nat. Semi. PC87365 Super IO Voltage Sensors",
+		driver => "pc87360",
+		devid => 0xe5,
+		logdev => 0x0d,
+		features => FEAT_IN,
+	}, {
+		name => "Nat. Semi. PC87365 Super IO Thermal Sensors",
+		driver => "pc87360",
+		devid => 0xe5,
+		logdev => 0x0e,
+		features => FEAT_TEMP,
+	}, {
+		name => "Nat. Semi. PC87366 Super IO Fan Sensors",
+		driver => "pc87360",
+		devid => 0xe9,
+		logdev => 0x09,
+		features => FEAT_FAN,
+	}, {
+		name => "Nat. Semi. PC87366 Super IO Voltage Sensors",
+		driver => "pc87360",
+		devid => 0xe9,
+		logdev => 0x0d,
+		features => FEAT_IN,
+	}, {
+		name => "Nat. Semi. PC87366 Super IO Thermal Sensors",
+		driver => "pc87360",
+		devid => 0xe9,
+		logdev => 0x0e,
+		features => FEAT_TEMP,
+	}, {
+		name => "Nat. Semi. PC87372 Super IO Fan Sensors",
+		driver => "to-be-written",
+		devid => 0xf0,
+		logdev => 0x09,
+		features => FEAT_FAN,
+	}, {
+		name => "Nat. Semi. PC87373 Super IO Fan Sensors",
+		driver => "to-be-written",
+		devid => 0xf3,
+		logdev => 0x09,
+		features => FEAT_FAN,
+	}, {
+		name => "Nat. Semi. PC87591 Super IO",
+		driver => "to-be-written",
+		devid => 0xec,
+		logdev => 0x0f,
+	}, {
+		name => "Nat. Semi. PC87317 Super IO",
+		driver => "not-a-sensor",
+		devid => 0xd0,
+	}, {
+		name => "Nat. Semi. PC97317 Super IO",
+		driver => "not-a-sensor",
+		devid => 0xdf,
+	}, {
+		name => "Nat. Semi. PC8739x Super IO",
+		driver => "not-a-sensor",
+		devid => 0xea,
+	}, {
+		name => "Nat. Semi. PC8741x Super IO",
+		driver => "not-a-sensor",
+		devid => 0xee,
+	}, {
+		name => "Nat. Semi. PC87427 Super IO Fan Sensors",
+		driver => "pc87427",
+		devid => 0xf2,
+		logdev => 0x09,
+		features => FEAT_FAN,
+	}, {
+		name => "Nat. Semi. PC87427 Super IO Health Sensors",
+		driver => "to-be-written",
+		devid => 0xf2,
+		logdev => 0x14,
+		features => FEAT_IN | FEAT_TEMP,
+	}, {
+		# Shouldn't be in this family, but there it is
+		name => "ITE IT8512E/F Super IO",
+		driver => "not-a-sensor",
+		devid => 0x8512,
+	}
+);
+
+@superio_ids_smsc = (
+	{
+		name => "SMSC DME1737 Super IO",
+		# Hardware monitoring features are accessed on the SMBus
+		driver => "via-smbus-only",
+		devid => 0x78,
+	}, {
+		name => "SMSC DME1737 Super IO",
+		# The DME1737 shows up twice in this list because it can return either
+		# 0x78 or 0x77 as its device ID.
+		# Hardware monitoring features are accessed on the SMBus
+		driver => "via-smbus-only",
+		devid => 0x77,
+	}, {
+		name => "SMSC EMC2700LPC Super IO",
+		# no datasheet
+		devid => 0x67,
+	}, {
+		name => "SMSC FDC37B72x Super IO",
+		driver => "not-a-sensor",
+		devid => 0x4c,
+	}, {
+		name => "SMSC FDC37B78x Super IO",
+		driver => "not-a-sensor",
+		devid => 0x44,
+	}, {
+		name => "SMSC FDC37C672 Super IO",
+		driver => "not-a-sensor",
+		devid => 0x40,
+	}, {
+		name => "SMSC FDC37M707 Super IO",
+		driver => "not-a-sensor",
+		devid => 0x42,
+	}, {
+		name => "SMSC FDC37M81x Super IO",
+		driver => "not-a-sensor",
+		devid => 0x4d,
+	}, {
+		name => "SMSC LPC47B27x Super IO Fan Sensors",
+		driver => "smsc47m1",
+		devid => 0x51,
+		logdev => 0x0a,
+		features => FEAT_FAN,
+	}, {
+		name => "SMSC LPC47B34x Super IO",
+		driver => "not-a-sensor",
+		devid => 0x56,
+	}, {
+		name => "SMSC LPC47B357/M967 Super IO",
+		driver => "not-a-sensor",
+		devid => 0x5d,
+	}, {
+		name => "SMSC LPC47B367-NC Super IO",
+		driver => "not-a-sensor",
+		devid => 0x6d,
+	}, {
+		name => "SMSC LPC47B37x Super IO Fan Sensors",
+		driver => "to-be-written",
+		devid => 0x52,
+		logdev => 0x0a,
+		features => FEAT_FAN,
+	}, {
+		name => "SMSC LPC47B397-NC Super IO",
+		driver => "smsc47b397",
+		devid => 0x6f,
+		logdev => 0x08,
+		features => FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "SMSC LPC47M10x/112/13x Super IO Fan Sensors",
+		driver => "smsc47m1",
+		devid => 0x59,
+		logdev => 0x0a,
+		features => FEAT_FAN,
+	}, {
+		name => "SMSC LPC47M14x Super IO Fan Sensors",
+		driver => "smsc47m1",
+		devid => 0x5f,
+		logdev => 0x0a,
+		features => FEAT_FAN,
+	}, {
+		name => "SMSC LPC47M15x/192/997 Super IO Fan Sensors",
+		driver => "smsc47m1",
+		devid => 0x60,
+		logdev => 0x0a,
+		features => FEAT_FAN,
+	}, {
+		name => "SMSC LPC47M172 Super IO Fan Sensors",
+		driver => "to-be-written",
+		devid => 0x14,
+		logdev => 0x0a,
+		features => FEAT_FAN,
+	}, {
+		name => "SMSC LPC47M182 Super IO Fan Sensors",
+		driver => "to-be-written",
+		devid => 0x74,
+		logdev => 0x0a,
+		features => FEAT_FAN,
+	}, {
+		name => "SMSC LPC47M233 Super IO Sensors",
+		driver => "to-be-written",
+		devid => 0x6b80,
+		devid_mask => 0xff80,
+		logdev => 0x0a,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "SMSC LPC47M292 Super IO Fan Sensors",
+		driver => "smsc47m1",
+		devid => 0x6b00,
+		devid_mask => 0xff80,
+		logdev => 0x0a,
+		features => FEAT_FAN,
+	}, {
+		name => "SMSC LPC47M584-NC Super IO",
+		# No datasheet
+		devid => 0x76,
+	}, {
+		name => "SMSC LPC47N252 Super IO Fan Sensors",
+		driver => "to-be-written",
+		devid => 0x0e,
+		logdev => 0x09,
+		features => FEAT_FAN,
+	}, {
+		name => "SMSC LPC47S42x Super IO Fan Sensors",
+		driver => "to-be-written",
+		devid => 0x57,
+		logdev => 0x0a,
+		features => FEAT_FAN,
+	}, {
+		name => "SMSC LPC47S45x Super IO Fan Sensors",
+		driver => "to-be-written",
+		devid => 0x62,
+		logdev => 0x0a,
+		features => FEAT_FAN,
+	}, {
+		name => "SMSC LPC47U32x Super IO Fan Sensors",
+		driver => "to-be-written",
+		devid => 0x58,
+		logdev => 0x0a,
+		features => FEAT_FAN,
+	}, {
+		name => "SMSC LPC47U33x Super IO Fan Sensors",
+		driver => "to-be-written",
+		devid => 0x54,
+		logdev => 0x0a,
+		features => FEAT_FAN,
+	}, {
+		name => "SMSC SCH3112 Super IO",
+		driver => "dme1737",
+		devid => 0x7c,
+		logdev => 0x0a,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "SMSC SCH3114 Super IO",
+		driver => "dme1737",
+		devid => 0x7d,
+		logdev => 0x0a,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "SMSC SCH3116 Super IO",
+		driver => "dme1737",
+		devid => 0x7f,
+		logdev => 0x0a,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "SMSC SCH4307 Super IO Fan Sensors",
+		driver => "to-be-written",
+		devid => 0x90,
+		logdev => 0x08,
+		features => FEAT_FAN,
+	}, {
+		name => "SMSC SCH5027D-NW Super IO",
+		# Hardware monitoring features are accessed on the SMBus
+		driver => "via-smbus-only",
+		devid => 0x89,
+	}, {
+		name => "SMSC SCH5127 Super IO",
+		driver => "dme1737",
+		devid => 0x86,
+		logdev => 0x0a,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "SMSC SCH5307-NS Super IO",
+		driver => "smsc47b397",
+		devid => 0x81,
+		logdev => 0x08,
+		features => FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "SMSC SCH5317 Super IO",
+		driver => "smsc47b397",
+		devid => 0x85,
+		logdev => 0x08,
+		features => FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "SMSC SCH5317 Super IO",
+		# The SCH5317 shows up twice in this list because it can return either
+		# 0x85 or 0x8c as its device ID.
+		driver => "smsc47b397",
+		devid => 0x8c,
+		logdev => 0x08,
+		features => FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "SMSC SCH5504-NS Super IO",
+		# No datasheet
+		driver => "not-a-sensor",
+		devid => 0x79,
+	}, {
+		name => "SMSC SCH5514D-NS Super IO",
+		# No datasheet
+		driver => "not-a-sensor",
+		devid => 0x83,
+	}, {
+		name => "SMSC SCH5627 Super IO",
+		driver => "sch5627",
+		devid => 0xc6,
+		regs => {
+			basereg_lsb => 0x66,
+			basereg_msb => 0x67,
+		},
+		logdev => 0x0c,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}
+);
+
+# Non-standard SMSC chip list. These chips differ from the standard ones
+# listed above in that the device ID register address is 0x0d instead of
+# 0x20 (as specified by the ISA PNP spec).
+@superio_ids_smsc_ns = (
+	{
+		name => "SMSC FDC37C665 Super IO",
+		driver => "not-a-sensor",
+		devid => 0x65,
+	}, {
+		name => "SMSC FDC37C666 Super IO",
+		driver => "not-a-sensor",
+		devid => 0x66,
+	}, {
+		name => "SMSC FDC37C669 Super IO",
+		driver => "not-a-sensor",
+		devid => 0x03,
+	}, {
+		name => "SMSC FDC37N769 Super IO",
+		driver => "not-a-sensor",
+		devid => 0x28,
+	}, {
+		name => "SMSC LPC47N227 Super IO",
+		driver => "not-a-sensor",
+		devid => 0x5a,
+	}, {
+		name => "SMSC LPC47N237 Super IO",
+		driver => "not-a-sensor",
+		devid => 0x13,
+	}
+);
+
+@superio_ids_winbond = (
+	{
+		name => "VIA VT1211 Super IO Sensors",
+		driver => "vt1211",
+		devid => 0x3c,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "VIA VT1212 Super IO Lite",	# in 100 pin TQFP package
+		driver => "not-a-sensor",
+		devid => 0x3e,
+	}, {
+		name => "VIA VT1212 Super IO Lite",	# in 48 pin LQFP package
+		driver => "not-a-sensor",
+		devid => 0x3f,
+	}, {
+		name => "Winbond W83627HF/F/HG/G Super IO Sensors",
+		# Also SMSC LPC61W492
+		driver => "w83627hf",
+		devid => 0x52,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Winbond W83627THF/THG Super IO Sensors",
+		driver => "w83627hf",
+		devid => 0x82,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Winbond W83637HF/HG Super IO Sensors",
+		driver => "w83627hf",
+		devid => 0x70,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Winbond W83687THF Super IO Sensors",
+		driver => "w83627hf",
+		devid => 0x85,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Winbond W83697HF/F/HG Super IO Sensors",
+		driver => "w83627hf",
+		devid => 0x60,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Winbond W83697SF/UF/UG Super IO PWM",
+		driver => "to-be-written",
+		devid => 0x68,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Winbond W83627EHF/EF/EHG/EG Super IO Sensors",
+		driver => "w83627ehf",
+		# W83627EHF datasheet says 0x886x but 0x8853 was seen, thus the
+		# broader mask. W83627EHG was seen with ID 0x8863.
+		devid => 0x8840,
+		devid_mask => 0xFFC0,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Winbond W83627DHG Super IO Sensors",
+		driver => "w83627ehf",
+		devid => 0xA020,
+		devid_mask => 0xFFF0,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Winbond W83627DHG-P Super IO Sensors",
+		driver => "w83627ehf",
+		devid => 0xB070,
+		devid_mask => 0xFFF0,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Winbond W83627UHG/NCT6627UD Super IO Sensors",
+		driver => "w83627ehf",
+		devid => 0xA230,
+		devid_mask => 0xFFF0,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Winbond W83667HG Super IO Sensors",
+		driver => "w83627ehf",
+		devid => 0xA510,
+		devid_mask => 0xFFF0,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Nuvoton W83667HG-B (NCT5571D) Super IO Sensors",
+		driver => "w83627ehf",
+		devid => 0xB350,
+		devid_mask => 0xFFF0,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Nuvoton W83677HG-I (NCT6771F/NCT6772F/NCT6775F) Super IO Sensors",
+		driver => "to-be-written",	# Probably w83627ehf
+		devid => 0xB470,
+		devid_mask => 0xFFF0,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Winbond W83L517D Super IO",
+		driver => "not-a-sensor",
+		devid => 0x61,
+	}, {
+		name => "Nuvoton NCT5577D Super IO Sensors",
+		driver => "to-be-written",
+		devid => 0xC331,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Nuvoton NCT6776F Super IO Sensors",
+		driver => "to-be-written",
+		devid => 0xC333,
+		logdev => 0x0b,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Fintek F71805F/FG Super IO Sensors",
+		driver => "f71805f",
+		devid => 0x0406,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Fintek F71808E Super IO Sensors",
+		driver => "f71882fg",
+		devid => 0x0901,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Fintek F71808A Super IO Sensors",
+		driver => "to-be-written",
+		devid => 0x1001,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Fintek F71862FG Super IO Sensors",
+		driver => "f71882fg",
+		devid => 0x0601,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Fintek F71869F/E Super IO Sensors",
+		driver => "f71882fg",
+		devid => 0x0814,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Fintek F71869A Super IO Sensors",
+		driver => "to-be-written",
+		devid => 0x1007,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Fintek F71806FG/F71872FG Super IO Sensors",
+		driver => "f71805f",
+		devid => 0x0341,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Fintek F71858DG Super IO Sensors",
+		driver => "f71882fg",
+		devid => 0x0507,
+		logdev => 0x02,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Fintek F71882FG/F71883FG Super IO Sensors",
+		driver => "f71882fg",
+		devid => 0x0541,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Fintek F71889FG/F81801U Super IO Sensors",
+		driver => "f71882fg",
+		devid => 0x0723,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Fintek F71889ED Super IO Sensors",
+		driver => "f71882fg",
+		devid => 0x0909,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Fintek F71889A Super IO Sensors",
+		driver => "to-be-written",
+		devid => 0x1005,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Fintek F81216D Super IO",
+		driver => "not-a-sensor",
+		devid => 0x0208,
+	}, {
+		name => "Fintek F81218D Super IO",
+		driver => "not-a-sensor",
+		devid => 0x0206,
+	}, {
+		name => "Fintek F81865F Super IO Sensors",
+		driver => "to-be-written",
+		devid => 0x0704,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Fintek F81866D Super IO Sensors",
+		driver => "to-be-written",
+		devid => 0x1010,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "Asus F8000 Super IO",
+		driver => "f71882fg",
+		devid => 0x0581,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		# Shouldn't be in this family, but seems to be still.
+		name => "ITE IT8708F Super IO",
+		driver => "not-a-sensor",
+		devid => 0x8708,
+	}, {
+		# Shouldn't be in this family, but seems to be still.
+		name => "ITE IT8710F Super IO",
+		driver => "not-a-sensor",
+		devid => 0x8710,
+	}
+);
+
+@superio_ids_ite = (
+	{
+		name => "ITE IT8702F Super IO Fan Sensors",
+		driver => "to-be-written",
+		devid => 0x8702,
+		logdev => 0x04,
+		features => FEAT_FAN,
+	}, {
+		name => "ITE IT8705F Super IO Sensors",
+		driver => "it87",
+		devid => 0x8705,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "ITE IT8712F Super IO Sensors",
+		driver => "it87",
+		devid => 0x8712,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "ITE IT8716F Super IO Sensors",
+		driver => "it87",
+		devid => 0x8716,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "ITE IT8718F Super IO Sensors",
+		driver => "it87",
+		devid => 0x8718,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "ITE IT8720F Super IO Sensors",
+		driver => "it87",
+		devid => 0x8720,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "ITE IT8721F/IT8758E Super IO Sensors",
+		driver => "it87",
+		devid => 0x8721,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "ITE IT8726F Super IO Sensors",
+		driver => "it87",
+		devid => 0x8726,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}, {
+		name => "ITE IT8728F Super IO Sensors",
+		driver => "it87",
+		devid => 0x8728,
+		logdev => 0x04,
+		features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
+	}
+);
+
+# Entries are grouped by family. Each family entry has the following fields:
+#  family: The family name
+#  guess (optional): Typical logical device address. This lets us do
+#	generic probing if we fail to recognize the chip.
+#  enter: The password sequence to write to the address register
+#  chips: Array of chips
+# The order of families matters, because we stop as soon as one family
+# succeeds. So we have to list families with shorter password sequences
+# first.
+@superio_ids = (
+	{
+		family => "National Semiconductor",
+		enter =>
+		{
+			0x2e => [],
+			0x4e => [],
+		},
+		chips => \@superio_ids_natsemi,
+	}, {
+		family => "SMSC",
+		enter =>
+		{
+			0x2e => [0x55],
+			0x4e => [0x55],
+		},
+		chips => \@superio_ids_smsc,
+		ns_detect => \&smsc_ns_detect_superio,
+		ns_chips => \@superio_ids_smsc_ns,
+	}, {
+		family => "VIA/Winbond/Nuvoton/Fintek",
+		guess => 0x290,
+		enter =>
+		{
+			0x2e => [0x87, 0x87],
+			0x4e => [0x87, 0x87],
+		},
+		chips => \@superio_ids_winbond,
+	}, {
+		family => "ITE",
+		guess => 0x290,
+		enter =>
+		{
+			0x2e => [0x87, 0x01, 0x55, 0x55],
+			0x4e => [0x87, 0x01, 0x55, 0xaa],
+		},
+		chips => \@superio_ids_ite,
+	}
+);
+
+# Drivers for bridge, CPU and memory embedded sensors
+# Each entry must have the following fields:
+#  name: The device name
+#  driver: The driver name. Put "to-be-written" if no driver is available.
+#  detect: Detection callback function. No parameter will be passed to
+#	this function, it must use global lists of PCI devices, CPU,
+#	etc. It must return a confidence value, undef if no supported
+#	CPU is found.
+use vars qw(@cpu_ids);
+
+@cpu_ids = (
+	{
+		name => "Silicon Integrated Systems SIS5595",
+		driver => "sis5595",
+		detect => \&sis5595_pci_detect,
+	}, {
+		name => "VIA VT82C686 Integrated Sensors",
+		driver => "via686a",
+		detect => \&via686a_pci_detect,
+	}, {
+		name => "VIA VT8231 Integrated Sensors",
+		driver => "vt8231",
+		detect => \&via8231_pci_detect,
+	}, {
+		name => "AMD K8 thermal sensors",
+		driver => "k8temp",
+		detect => \&k8temp_pci_detect,
+	}, {
+		name => "AMD Family 10h thermal sensors",
+		driver => "k10temp",
+		detect => \&fam10h_pci_detect,
+	}, {
+		name => "AMD Family 11h thermal sensors",
+		driver => "k10temp",
+		detect => \&fam11h_pci_detect,
+	}, {
+		name => "AMD Family 12h and 14h thermal sensors",
+		driver => "k10temp",
+		detect => \&fam12h_14h_pci_detect,
+	}, {
+		name => "Intel digital thermal sensor",
+		driver => "coretemp",
+		detect => \&coretemp_detect,
+	}, {
+		name => "Intel AMB FB-DIMM thermal sensor",
+		driver => "i5k_amb",
+		detect => \&intel_amb_detect,
+	}, {
+		name => "VIA C7 thermal sensor",
+		driver => "via-cputemp",
+		detect => \&via_c7_detect,
+	}, {
+		name => "VIA Nano thermal sensor",
+		driver => "via-cputemp",
+		detect => \&via_nano_detect,
+	}
+);
+
+#######################
+# AUXILIARY FUNCTIONS #
+#######################
+
+# $_[0] is the sought value
+# $_[1..] is the list to seek in
+# Returns: 1 if found, 0 if not.
+sub contains
+{
+	my $sought = shift;
+	local $_;
+
+	foreach (@_) {
+		return 1 if $sought eq $_;
+	}
+	return 0;
+}
+
+# Address can be decimal or hexadecimal
+sub valid_address
+{
+	my $value = shift;
+
+	if ($value !~ m/^(0x[0-9a-f]+|[0-9]+)$/i) {
+		print "$value is not a valid address, sorry.\n";
+		exit -1;
+	}
+	$value = oct($value) if $value =~ m/^0x/i;
+
+	return $value;
+}
+
+sub parse_not_to_scan
+{
+	my ($min, $max, $to_parse) = @_;
+	my @ranges = split /\s*, \s*/, $to_parse;
+	my @res;
+	my $range;
+
+	foreach $range (@ranges) {
+		my ($start, $end) = split /\s*-\s*/, $range;
+		$start = valid_address($start);
+		if (defined $end) {
+			$end = valid_address($end);
+			if ($end <= $start) {
+				print "$start-$end is not a valid range, sorry.\n";
+				exit -1;
+			}
+			$start = $min if $start < $min;
+			$end = $max if $end > $max;
+			push @res, ($start..$end);
+		} else {
+			push @res, $start if $start >= $min and $start <= $max;
+		}
+	}
+
+	return sort { $a <=> $b } @res;
+}
+
+# $_[0]: Reference to list 1
+# $_[1]: Reference to list 2
+# Result: 0 if they have no elements in common, 1 if they have
+# Elements must be numeric.
+sub any_list_match
+{
+	my ($list1, $list2) = @_;
+	my ($el1, $el2);
+
+	foreach $el1 (@$list1) {
+		foreach $el2 (@$list2) {
+			return 1 if $el1 == $el2;
+		}
+	}
+	return 0;
+}
+
+# $_[0]: Reference to base hash
+# $_[1]: Reference to overlay hash
+# Result: Overlayed hash
+sub overlay_hash
+{
+	my ($base, $overlay) = @_;
+	my %result = %{$base};
+
+	foreach my $key (keys %{$overlay}) {
+		$result{$key} = $overlay->{$key};
+	}
+	return %result;
+}
+
+###################
+# I/O PORT ACCESS #
+###################
+
+sub initialize_ioports
+{
+	sysopen(IOPORTS, "/dev/port", O_RDWR)
+		or die "/dev/port: $!\n";
+	binmode(IOPORTS);
+}
+
+sub close_ioports
+{
+	close(IOPORTS);
+}
+
+# $_[0]: port to read
+# Returns: -1 on failure, read value on success.
+sub inb
+{
+	my ($res, $nrchars);
+	sysseek(IOPORTS, $_[0], 0) or return -1;
+	$nrchars = sysread(IOPORTS, $res, 1);
+	return -1 if not defined $nrchars or $nrchars != 1;
+	$res = unpack("C", $res);
+	return $res;
+}
+
+# $_[0]: port to write
+# $_[1]: value to write
+# We assume this can't fail.
+sub outb
+{
+	sysseek(IOPORTS, $_[0], 0);
+	syswrite(IOPORTS, pack("C", $_[1]), 1);
+}
+
+# $_[0]: Address register
+# $_[1]: Data register
+# $_[2]: Register to read
+# Returns: read value
+sub isa_read_byte
+{
+	outb($_[0], $_[2]);
+	return inb($_[1]);
+}
+
+# $_[0]: Base address
+# $_[1]: Register to read
+# Returns: read value
+# This one can be used for any ISA chip with index register at
+# offset 5 and data register at offset 6.
+sub isa_read_i5d6
+{
+	my ($addr, $reg) = @_;
+	return isa_read_byte($addr + 5, $addr + 6, $reg);
+}
+
+#################
+# AUTODETECTION #
+#################
+
+use vars qw($dev_i2c $sysfs_root);
+
+sub initialize_conf
+{
+	my $use_devfs = 0;
+	open(local *INPUTFILE, "/proc/mounts") or die "Can't access /proc/mounts!";
+	local $_;
+	while (<INPUTFILE>) {
+		if (m@^\w+ /dev devfs @) {
+			$use_devfs = 1;
+			$dev_i2c = '/dev/i2c/';
+		}
+		if (m@^\S+ (/\w+) sysfs @) {
+			$sysfs_root = $1;
+		}
+	}
+	close(INPUTFILE);
+
+	# We need sysfs for many things
+	if (!defined $sysfs_root) {
+		print "Sysfs not mounted?\n";
+		exit -1;
+	}
+
+	my $use_udev = 0;
+	if (open(*INPUTFILE, '/etc/udev/udev.conf')) {
+		while (<INPUTFILE>) {
+			next unless m/^\s*udev_db\s*=\s*\"([^"]*)\"/
+				 || m/^\s*udev_db\s*=\s*(\S+)/;
+			if (-e $1) {
+				$use_udev = 1;
+				$dev_i2c = '/dev/i2c-';
+			}
+			last;
+		}
+		close(INPUTFILE);
+	  }
+
+	if (!$use_udev) {
+		# Try some known default udev db locations, just in case
+		if (-e '/dev/.udev.tdb' || -e '/dev/.udev'
+		 || -e '/dev/.udevdb') {
+			$use_udev = 1;
+			$dev_i2c = '/dev/i2c-';
+		}
+	}
+
+	if (!($use_devfs || $use_udev)) {
+		if (! -c '/dev/i2c-0' && -x '/sbin/MAKEDEV') {
+			system("/sbin/MAKEDEV i2c");
+		}
+		if (! -c '/dev/i2c-0' && -x '/dev/MAKEDEV') {
+			system("/dev/MAKEDEV i2c");
+		}
+		if (-c '/dev/i2c-0') {
+			$dev_i2c = '/dev/i2c-';
+		} else { # default
+			print "No i2c device files found.\n";
+			exit -1;
+		}
+	}
+}
+
+# [0] -> VERSION
+# [1] -> PATCHLEVEL
+# [2] -> SUBLEVEL
+# [3] -> EXTRAVERSION
+#
+use vars qw(@kernel_version $kernel_arch);
+
+sub initialize_kernel_version
+{
+	`uname -r` =~ /(\d+)\.(\d+)\.(\d+)(.*)/;
+	@kernel_version = ($1, $2, $3, $4);
+	chomp($kernel_arch = `uname -m`);
+
+	# We only support kernels >= 2.6.5
+	if (!kernel_version_at_least(2, 6, 5)) {
+		print "Kernel version is unsupported (too old, >= 2.6.5 needed)\n";
+		exit -1;
+	}
+}
+
+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;
+}
+
+# @cpu is a list of reference to hashes, one hash per CPU.
+# Each entry has the following keys: vendor_id, cpu family, model,
+# model name and stepping, directly taken from /proc/cpuinfo.
+use vars qw(@cpu);
+
+sub initialize_cpu_list
+{
+	local $_;
+	my $entry;
+
+	open(local *INPUTFILE, "/proc/cpuinfo") or die "Can't access /proc/cpuinfo!";
+	while (<INPUTFILE>) {
+		if (m/^processor\s*:\s*(\d+)/) {
+			push @cpu, $entry if scalar keys(%{$entry}); # Previous entry
+			$entry = { nr => $1 }; # New entry
+			next;
+		}
+		if (m/^(vendor_id|cpu family|model|model name|stepping|cpuid level)\s*:\s*(.+)$/) {
+			my $k = $1;
+			my $v = $2;
+			$v =~ s/\s+/ /g;	# Merge multiple spaces
+			$v =~ s/ $//;		# Trim trailing space
+			$entry->{$k} = $v;
+			next;
+		}
+	}
+	close(INPUTFILE);
+	push @cpu, $entry if scalar keys(%{$entry}); # Last entry
+}
+
+# @i2c_adapters is a list of references to hashes, one hash per I2C/SMBus
+# adapter present on the system. Each entry has the following keys: path,
+# parent, name (directly taken from sysfs), driver and autoload.
+use vars qw(@i2c_adapters);
+
+# Find out whether the driver would be automatically loaded by the modalias
+# mechanism.
+sub device_driver_autoloads
+{
+	my $device = shift;
+	
+	my $modalias = sysfs_device_attribute($device, "modalias");
+	return 0 unless defined($modalias);
+
+	# At the moment we only handle PCI and USB drivers. Other driver
+	# types, most notably platform and i2c drivers, do not follow the
+	# device driver model to the letter, and often create their own
+	# devices. Such drivers appear to be autoloaded when they are not.
+	return 0 unless $modalias =~ m/^(pci|usb):/;
+
+	my $result = `modprobe -n -v --first-time $modalias 2>&1`;
+	return ($result =~ m/^insmod/ ||
+		$result =~ m/\balready in kernel\b/);
+}
+
+sub initialize_i2c_adapters_list
+{
+	my ($entry, $base_dir, $have_i2c_adapter_class);
+	local $_;
+
+	if (-d "${sysfs_root}/class/i2c-adapter") {
+		$base_dir = "${sysfs_root}/class/i2c-adapter";
+		$have_i2c_adapter_class = 1;
+	} else {
+		$base_dir = "${sysfs_root}/bus/i2c/devices";
+		$have_i2c_adapter_class = 0;
+	}
+	opendir(local *ADAPTERS, $base_dir) or return;
+
+	while (defined($_ = readdir(ADAPTERS))) {
+		next unless m/^i2c-(\d+)$/;
+		my $nr = $1;
+		$entry = {}; # New entry
+
+		# The layout in sysfs has changed over the years
+		if ($have_i2c_adapter_class) {
+			my $link = readlink("${base_dir}/i2c-$nr/device");
+			if (!defined $link) {
+				$entry->{path} = "${base_dir}/i2c-$nr";
+				$entry->{parent} = "${base_dir}/i2c-$nr";
+			} elsif ($link =~ m/^(.*)\/i2c-$nr$/) {
+				$entry->{path} = "${base_dir}/i2c-$nr/device";
+				$entry->{parent} = "${base_dir}/i2c-$nr/$1";
+			} else {
+				$entry->{path} = "${base_dir}/i2c-$nr";
+				$entry->{parent} = "$entry->{path}/device";
+			}
+		} else {
+			my $link = readlink("${base_dir}/i2c-$nr");
+			$link =~ s/\/i2c-$nr$//;
+			$entry->{path} = "${base_dir}/i2c-$nr";
+			$entry->{parent} = "${base_dir}/$link";
+		}
+
+		$entry->{name} = sysfs_device_attribute($entry->{path}, "name");
+		next if $entry->{name} eq "ISA main adapter";
+
+		# First try to get the I2C adapter driver name from sysfs,
+		# and if it fails, fall back to searching our list of known
+		# I2C adapters.
+		$entry->{driver} = sysfs_device_driver($entry->{parent})
+				|| find_i2c_adapter_driver($entry->{name})
+				|| 'UNKNOWN';
+
+		$entry->{autoload} = device_driver_autoloads($entry->{parent});
+		$i2c_adapters[$nr] = $entry;
+	}
+	closedir(ADAPTERS);
+}
+
+# %hwmon_autoloaded is a list of hwmon drivers which are autoloaded
+# (typically by udev.) We don't need to load these drivers ourselves.
+use vars qw(%hwmon_autoloaded);
+
+sub initialize_hwmon_autoloaded
+{
+	my $class_dir = "${sysfs_root}/class/hwmon";
+	opendir(local *HWMON, $class_dir) or return;
+
+	my ($hwmon, $driver);
+	while (defined($hwmon = readdir(HWMON))) {
+		next unless $hwmon =~ m/^hwmon\d+$/;
+
+		$driver = sysfs_device_driver("${class_dir}/$hwmon/device");
+		next unless defined($driver);
+
+		if (device_driver_autoloads("${class_dir}/$hwmon/device")) {
+			$driver =~ tr/-/_/;
+			$hwmon_autoloaded{$driver}++
+		}
+	}
+	closedir(HWMON);
+}
+
+sub hwmon_is_autoloaded
+{
+	my $driver = shift;
+	$driver =~ tr/-/_/;
+	return exists($hwmon_autoloaded{$driver});
+}
+
+###########
+# MODULES #
+###########
+
+use vars qw(%modules_list %modules_supported @modules_we_loaded);
+
+sub initialize_modules_list
+{
+	local $_;
+
+	open(local *INPUTFILE, "/proc/modules") or return;
+	while (<INPUTFILE>) {
+		tr/-/_/; # Probably not needed
+		$modules_list{$1} = 1 if m/^(\S*)/;
+	}
+}
+
+sub is_module_loaded
+{
+	my $module = shift;
+	$module =~ tr/-/_/;
+	return exists $modules_list{$module}
+}
+
+sub load_module
+{
+	my $module = shift;
+
+	return if is_module_loaded($module);
+
+	system("modprobe", $module);
+	if (($? >> 8) != 0) {
+		print "Failed to load module $module.\n";
+		return -1;
+	}
+
+	print "Module $module loaded successfully.\n";
+	push @modules_we_loaded, $module;
+
+	# Update the list of loaded modules
+	my $normalized = $module;
+	$normalized =~ tr/-/_/;
+	$modules_list{$normalized} = 1;
+}
+
+# udev may take some time to create device nodes when loading modules
+sub udev_settle
+{
+	if (!(-x "/sbin/udevadm" && system("/sbin/udevadm settle") == 0)
+	 && !(-x "/sbin/udevsettle" && system("/sbin/udevsettle") == 0)) {
+		sleep(1);
+	}
+}
+
+sub initialize_modules_supported
+{
+	foreach my $chip (@chip_ids) {
+		next if $chip->{driver} eq "to-be-written";
+		next if $chip->{driver} eq "use-isa-instead";
+
+		my $normalized = $chip->{driver};
+		$normalized =~ tr/-/_/;
+		$modules_supported{$normalized}++;
+	}
+}
+
+sub unload_modules
+{
+	return unless @modules_we_loaded;
+
+	# Attempt to unload all kernel drivers we loaded ourselves
+	while (my $module = pop @modules_we_loaded) {
+		print "Unloading $module... ";
+		system("modprobe -r $module 2> /dev/null");
+		if (($? >> 8) == 0) {
+			print "OK\n";
+		} else {
+			print "failed\n";
+		}
+	}
+	print "\n";
+}
+
+############
+# DMI DATA #
+############
+
+use vars qw(%dmi $dmidecode_ok);
+
+# Returns: 1 if dmidecode is recent enough, 0 if not
+# Cache the result in case it is needed again later.
+sub check_dmidecode_version
+{
+	return $dmidecode_ok if defined $dmidecode_ok;
+
+	my $version;
+	if (open(local *DMIDECODE, "dmidecode --version 2>/dev/null |")) {
+		$version = <DMIDECODE>;
+		close(DMIDECODE);
+		chomp $version if defined $version;
+	}
+
+	if (!defined $version
+	 || !($version =~ m/^(\d+).(\d+)$/)
+	 || !(($1 == 2 && $2 >= 7) || $1 > 2)) {
+		print "# DMI data unavailable, please consider installing dmidecode 2.7\n".
+		      "# or later for better results.\n";
+		$dmidecode_ok = 0;
+	} else {
+		$dmidecode_ok = 1;
+	}
+
+	return $dmidecode_ok;
+}
+
+sub initialize_dmi_data
+{
+	my %items = (
+		# sysfs file name => dmidecode string name
+		sys_vendor => 'system-manufacturer',
+		product_name => 'system-product-name',
+		product_version => 'system-version',
+		board_vendor => 'baseboard-manufacturer',
+		board_name => 'baseboard-product-name',
+		board_version => 'baseboard-product-name',
+		chassis_type => 'chassis-type',
+	);
+	# Many BIOS have broken DMI data, filter it out
+	my %fake = (
+		'System Manufacturer' => 1,
+		'System Name' => 1,
+	);
+	my $dmi_id_dir;
+
+	# First try reading the strings from sysfs if available
+	if (-d ($dmi_id_dir = "$sysfs_root/devices/virtual/dmi/id")	# kernel >= 2.6.28
+	 || -d ($dmi_id_dir = "$sysfs_root/class/dmi/id")) {
+		foreach my $k (keys %items) {
+			$dmi{$k} = sysfs_device_attribute($dmi_id_dir, $k);
+		}
+	} else {
+		# Else fallback on calling dmidecode
+		return unless check_dmidecode_version();
+
+		foreach my $k (keys %items) {
+			next unless open(local *DMIDECODE,
+					 "dmidecode -s $items{$k} 2>/dev/null |");
+			$dmi{$k} = <DMIDECODE>;
+			close(DMIDECODE);
+		}
+	}
+
+	# Strip trailing white-space, discard empty field
+	foreach my $k (keys %dmi) {
+		if (!defined $dmi{$k}) {
+			delete $dmi{$k};
+			next;
+		}
+		$dmi{$k} =~ s/\s*$//;
+		delete $dmi{$k} if $dmi{$k} eq '' || exists $fake{$dmi{$k}};
+	}
+}
+
+sub print_dmi_summary
+{
+	my ($system, $board);
+	if (defined $dmi{sys_vendor} && defined $dmi{product_name}) {
+		$system = "$dmi{sys_vendor} $dmi{product_name}";
+	}
+	if (defined $dmi{board_vendor} && defined $dmi{board_name}) {
+		$board = "$dmi{board_vendor} $dmi{board_name}";
+	}
+	
+	if (defined $system) {
+		print "# System: $system";
+		print " (laptop)" if (is_laptop());
+		print "\n";
+	}
+	print "# Board: $board\n" if defined $board
+				  && (!defined $system || $system ne $board);
+}
+
+sub dmi_match
+{
+	my $key = shift;
+	my $value;
+
+	return unless defined $dmi{$key};
+	while (defined ($value = shift)) {
+		return 1 if $dmi{$key} =~ m/\b$value\b/i;
+	}
+	return 0;
+}
+
+sub is_laptop
+{
+	return 0 unless $dmi{chassis_type};
+	return 1 if $dmi{chassis_type} =~ m/(Portable|Laptop|Notebook|Hand Held)/i;
+	return 1 if $dmi{chassis_type} =~ m/^(8|9|10|11|14)$/;
+	return 0;
+}
+
+#################
+# SYSFS HELPERS #
+#################
+
+# From a sysfs device path, return the driver (module) name, or undef
+sub sysfs_device_driver
+{
+	my $device = shift;
+
+	my $link = readlink("$device/driver/module");
+	return unless defined $link;
+	return basename($link);
+}
+
+# From a sysfs device path, return the subsystem name, or undef
+sub sysfs_device_subsystem
+{
+	my $device = shift;
+
+	my $link = readlink("$device/subsystem");
+	return unless defined $link;
+	return basename($link);
+}
+
+# From a sysfs device path and an attribute name, return the attribute
+# value, or undef
+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;
+}
+
+##############
+# PCI ACCESS #
+##############
+
+use vars qw(%pci_list);
+
+# This function returns a list of hashes. Each hash has some PCI information:
+# 'domain', 'bus', 'slot' and 'func' uniquely identify a PCI device in a
+# computer; 'vendid' and 'devid' uniquely identify a type of device.
+# 'class' lets us spot unknown SMBus adapters.
+sub read_sys_dev_pci
+{
+	my $devices = shift;
+	my ($dev, @pci_list);
+
+	opendir(local *DEVICES, "$devices")
+		or die "$devices: $!";
+
+	while (defined($dev = readdir(DEVICES))) {
+		my %record;
+		next unless $dev =~
+			m/^(?:([\da-f]+):)?([\da-f]+):([\da-f]+)\.([\da-f]+)$/;
+
+		$record{domain} = hex $1;
+		$record{bus} = hex $2;
+		$record{slot} = hex $3;
+		$record{func} = hex $4;
+
+		$record{vendid} = oct sysfs_device_attribute("$devices/$dev",
+							     "vendor");
+		$record{devid} = oct sysfs_device_attribute("$devices/$dev",
+							    "device");
+		$record{class} = (oct sysfs_device_attribute("$devices/$dev",
+							     "class")) >> 8;
+
+		push @pci_list, \%record;
+	}
+
+	return \@pci_list;
+}
+
+sub initialize_pci
+{
+	my $pci_list;
+	local $_;
+
+	$pci_list = read_sys_dev_pci("$sysfs_root/bus/pci/devices");
+
+	# Note that we lose duplicate devices at this point, but we don't
+	# really care. What matters to us is which unique devices are present,
+	# not how many of each.
+	%pci_list = map {
+		sprintf("%04x:%04x", $_->{vendid}, $_->{devid}) => $_
+	} @{$pci_list};
+}
+
+#####################
+# ADAPTER DETECTION #
+#####################
+
+# Build and return a PCI device's bus ID
+sub pci_busid
+{
+	my $device = shift;
+	my $busid;
+
+	$busid = sprintf("\%02x:\%02x.\%x",
+			 $device->{bus}, $device->{slot}, $device->{func});
+	$busid = sprintf("\%04x:", $device->{domain}) . $busid
+		if defined $device->{domain};
+
+	return $busid;
+}
+
+sub adapter_pci_detection
+{
+	my ($key, $device, $try, %smbus, $count);
+
+	# Build a list of detected SMBus devices
+	foreach $key (keys %pci_list) {
+		$device = $pci_list{$key};
+		$smbus{$key}++
+			if exists $device->{class} &&
+			   ($device->{class} == 0x0c01 ||	# Access Bus
+			    $device->{class} == 0x0c05);	# SMBus
+	}
+
+	# Loop over the known I2C/SMBus adapters
+	foreach $try (@pci_adapters) {
+		$key = sprintf("%04x:%04x", $try->{vendid}, $try->{devid});
+		next unless exists $pci_list{$key};
+
+		$device = $pci_list{$key};
+		if ($try->{driver} eq "to-be-tested") {
+			print "\nWe are currently looking for testers for this adapter!\n".
+			      "Please check http://www.lm-sensors.org/wiki/Devices\n".
+			      "and/or contact us if you want to help.\n\n".
+			      "Continue... ";
+			<STDIN>;
+			print "\n";
+		}
+
+		if ($try->{driver} =~ m/^to-be-/) {
+			printf "No known driver for device \%s: \%s\n",
+			       pci_busid($device), $try->{procid};
+		} else {
+			printf "Using driver `\%s' for device \%s: \%s\n",
+			       $try->{driver}, pci_busid($device),
+			       $try->{procid};
+			$count++;
+			load_module($try->{driver});
+		}
+
+		# Delete from detected SMBus device list
+		delete $smbus{$key};
+	}
+
+	# Now see if there are unknown SMBus devices left
+	foreach $key (keys %smbus) {
+		$device = $pci_list{$key};
+		printf "Found unknown SMBus adapter \%04x:\%04x at \%s.\n",
+		       $device->{vendid}, $device->{devid}, pci_busid($device);
+	}
+
+	print "Sorry, no supported PCI bus adapters found.\n"
+		unless $count;
+}
+
+# $_[0]: Adapter description as found in sysfs
+sub find_i2c_adapter_driver
+{
+	my $name = shift;
+	my $entry;
+
+	foreach $entry (@i2c_adapter_names) {
+		return $entry->{driver}
+			if $name =~ $entry->{match};
+	}
+}
+
+#############################
+# I2C AND SMBUS /DEV ACCESS #
+#############################
+
+# This should really go into a separate module/package.
+
+# These are copied from <linux/i2c-dev.h>
+
+use constant IOCTL_I2C_SLAVE	=> 0x0703;
+use constant IOCTL_I2C_FUNCS	=> 0x0705;
+use constant IOCTL_I2C_SMBUS	=> 0x0720;
+
+use constant SMBUS_READ		=> 1;
+use constant SMBUS_WRITE	=> 0;
+
+use constant SMBUS_QUICK	=> 0;
+use constant SMBUS_BYTE		=> 1;
+use constant SMBUS_BYTE_DATA	=> 2;
+use constant SMBUS_WORD_DATA	=> 3;
+
+use constant I2C_FUNC_SMBUS_QUICK	=> 0x00010000;
+use constant I2C_FUNC_SMBUS_READ_BYTE	=> 0x00020000;
+use constant I2C_FUNC_SMBUS_READ_BYTE_DATA	=> 0x00080000;
+
+# Get the i2c adapter's functionalities
+# $_[0]: Reference to an opened filehandle
+# Returns: -1 on failure, functionality bitfield on success.
+sub i2c_get_funcs
+{
+	my $file = shift;
+	my $funcs = pack("L", 0); # Allocate space
+
+	ioctl($file, IOCTL_I2C_FUNCS, $funcs) or return -1;
+	$funcs = unpack("L", $funcs);
+
+	return $funcs;
+}
+
+# Select the device to communicate with through its address.
+# $_[0]: Reference to an opened filehandle
+# $_[1]: Address to select
+# Returns: 0 on failure, 1 on success.
+sub i2c_set_slave_addr
+{
+	my ($file, $addr) = @_;
+
+	# Reset register data cache
+	@i2c_byte_cache = ();
+
+	$addr += 0; # Make sure it's a number not a string
+	ioctl($file, IOCTL_I2C_SLAVE, $addr) or return 0;
+	return 1;
+}
+
+# i2c_smbus_access is based upon the corresponding C function (see
+# <linux/i2c-dev.h>). You should not need to call this directly.
+# $_[0]: Reference to an opened filehandle
+# $_[1]: SMBUS_READ for reading, SMBUS_WRITE for writing
+# $_[2]: Command (usually register number)
+# $_[3]: Transaction kind (SMBUS_BYTE, SMBUS_BYTE_DATA, etc.)
+# $_[4]: Reference to an array used for input/output of data
+# Returns: 0 on failure, 1 on success.
+# Note that we need to get back to Integer boundaries through the 'x2'
+# in the pack. This is very compiler-dependent; I wish there was some other
+# way to do this.
+sub i2c_smbus_access
+{
+	my ($file, $read_write, $command, $size, $data) = @_;
+	my $data_array = pack("C32", @$data);
+	my $ioctl_data = pack("C2x2Ip", $read_write, $command, $size,
+			      $data_array);
+
+	ioctl($file, IOCTL_I2C_SMBUS, $ioctl_data) or return 0;
+	@{$_[4]} = unpack("C32", $data_array);
+	return 1;
+}
+
+# $_[0]: Reference to an opened filehandle
+# Returns: -1 on failure, the read byte on success.
+sub i2c_smbus_read_byte
+{
+	my ($file) = @_;
+	my @data;
+
+	i2c_smbus_access($file, SMBUS_READ, 0, SMBUS_BYTE, \@data)
+		or return -1;
+	return $data[0];
+}
+
+# $_[0]: Reference to an opened filehandle
+# $_[1]: Command byte (usually register number)
+# Returns: -1 on failure, the read byte on success.
+# Read byte data values are cached by default. As we keep reading the
+# same registers over and over again in the detection functions, and
+# SMBus can be slow, caching results in a big performance boost.
+sub i2c_smbus_read_byte_data
+{
+	my ($file, $command, $nocache) = @_;
+	my @data;
+
+	return $i2c_byte_cache[$command]
+		if !$nocache && exists $i2c_byte_cache[$command];
+
+	i2c_smbus_access($file, SMBUS_READ, $command, SMBUS_BYTE_DATA, \@data)
+		or return -1;
+	return ($i2c_byte_cache[$command] = $data[0]);
+}
+
+# $_[0]: Reference to an opened filehandle
+# $_[1]: Command byte (usually register number)
+# Returns: -1 on failure, the read word on success.
+# Use this function with care, some devices don't like word reads,
+# so you should do as much of the detection as possible using byte reads,
+# and only start using word reads when there is a good chance that
+# the detection will succeed.
+# Note: some devices use the wrong endianness.
+sub i2c_smbus_read_word_data
+{
+	my ($file, $command) = @_;
+	my @data;
+	i2c_smbus_access($file, SMBUS_READ, $command, SMBUS_WORD_DATA, \@data)
+		or return -1;
+	return $data[0] + 256 * $data[1];
+}
+
+# $_[0]: Reference to an opened filehandle
+# $_[1]: Address
+# $_[2]: Functionalities of this i2c adapter
+# Returns: 1 on successful probing, 0 else.
+# This function is meant to prevent AT24RF08 corruption and write-only
+# chips locks. This is done by choosing the best probing method depending
+# on the address range.
+sub i2c_probe
+{
+	my ($file, $addr, $funcs) = @_;
+
+	if (($addr >= 0x50 && $addr <= 0x5F)
+	 || ($addr >= 0x30 && $addr <= 0x37)) {
+		# This covers all EEPROMs we know of, including page protection
+		# addresses. Note that some page protection addresses will not
+		# reveal themselves with this, because they ack on write only,
+		# but this is probably better since some EEPROMs write-protect
+		# themselves permanently on almost any write to their page
+		# protection address.
+		return 0 unless ($funcs & I2C_FUNC_SMBUS_READ_BYTE);
+		return i2c_smbus_access($file, SMBUS_READ, 0, SMBUS_BYTE, []);
+	} elsif ($addr == 0x73) {
+		# Special case for FSC chips, as at least the Syleus locks
+		# up with our regular probe code. Note that to our current
+		# knowledge only FSC chips live on this address, and for them
+		# this probe method is safe.
+		return 0 unless ($funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA);
+		return i2c_smbus_access($file, SMBUS_READ, 0, SMBUS_BYTE_DATA, []);
+	} else {
+		return 0 unless ($funcs & I2C_FUNC_SMBUS_QUICK);
+		return i2c_smbus_access($file, SMBUS_WRITE, 0, SMBUS_QUICK, []);
+	}
+}
+
+# $_[0]: Reference to an opened file handle
+# Returns: 1 if the device is safe to access, 0 else.
+# This function is meant to prevent access to 1-register-only devices,
+# which are designed to be accessed with SMBus receive byte and SMBus send
+# byte transactions (i.e. short reads and short writes) and treat SMBus
+# read byte as a real write followed by a read. The device detection
+# routines would write random values to the chip with possibly very nasty
+# results for the hardware. Note that this function won't catch all such
+# chips, as it assumes that reads and writes relate to the same register,
+# but that's the best we can do.
+sub i2c_safety_check
+{
+	my ($file) = @_;
+	my $data;
+
+	# First we receive a byte from the chip, and remember it.
+	$data = i2c_smbus_read_byte($file);
+	return 1 if ($data < 0);
+
+	# We receive a byte again; very likely to be the same for
+	# 1-register-only devices.
+	return 1 if (i2c_smbus_read_byte($file) != $data);
+
+	# Then we try a standard byte read, with a register offset equal to
+	# the byte we received; we should receive the same byte value in return.
+	return 1 if (i2c_smbus_read_byte_data($file, $data) != $data);
+
+	# Then we try a standard byte read, with a slightly different register
+	# offset; we should again receive the same byte value in return.
+	return 1 if (i2c_smbus_read_byte_data($file, $data ^ 1) != ($data ^ 1));
+
+	# Apprently this is a 1-register-only device, restore the original
+	# register value and leave it alone.
+	i2c_smbus_read_byte_data($file, $data);
+	return 0;
+}
+
+####################
+# ADAPTER SCANNING #
+####################
+
+use vars qw(%chips_detected);
+
+# We will build a complicated structure %chips_detected here, being a hash
+# where keys are driver names and values are detected chip information in
+# the form of a list of hashes of type 'detect_data'.
+
+# Type detect_data:
+# A hash
+#  with field 'i2c_devnr', contianing the /dev/i2c-* number of this
+#       adapter (if this is an I2C detection)
+#  with field 'i2c_addr', containing the I2C address of the detection;
+#       (if this is an I2C detection)
+#  with field 'i2c_sub_addrs', containing a reference to a list of
+#       other I2C addresses (if this is an I2C detection)
+#  with field 'isa_addr' containing the ISA address this chip is on
+#       (if this is an ISA detection)
+#  with field 'conf', containing the confidence level of this detection
+#  with field 'chipname', containing the chip name
+#  with optional field 'alias_detect', containing a reference to an alias
+#       detection function for this chip
+
+# This adds a detection to the above structure.
+# Not all possibilities of i2c_addr and i2c_sub_addrs are exhausted.
+# In all normal cases, it should be all right.
+# $_[0]: chip driver
+# $_[1]: reference to data hash
+# Returns: Nothing
+sub add_i2c_to_chips_detected
+{
+	my ($chipdriver, $datahash) = @_;
+	my ($i, $detected_ref, $detected_entry, $driver,
+	    $put_in_detected, @hash_addrs, @entry_addrs);
+
+	# Find out whether our new entry should go into the detected list
+	# or not. We compare all i2c addresses; if at least one matches,
+	# but our confidence value is lower, we assume this is a misdetection,
+	# in which case we simply discard our new entry.
+	@hash_addrs = ($datahash->{i2c_addr});
+	push @hash_addrs, @{$datahash->{i2c_sub_addrs}}
+		if exists $datahash->{i2c_sub_addrs};
+	$put_in_detected = 1;
+ FIND_LOOP:
+	foreach $detected_ref (values %chips_detected) {
+		foreach $detected_entry (@{$detected_ref}) {
+			next unless defined $detected_entry->{i2c_addr};
+			@entry_addrs = ($detected_entry->{i2c_addr});
+			push @entry_addrs, @{$detected_entry->{i2c_sub_addrs}}
+				if exists $detected_entry->{i2c_sub_addrs};
+			if ($detected_entry->{i2c_devnr} == $datahash->{i2c_devnr} &&
+			    any_list_match(\@entry_addrs, \@hash_addrs)) {
+				if ($detected_entry->{conf} >= $datahash->{conf}) {
+					$put_in_detected = 0;
+				}
+				last FIND_LOOP;
+			}
+		}
+	}
+
+	return unless $put_in_detected;
+
+	# Here, we discard all entries which match at least in one main or
+	# sub address. This may not be the best idea to do, as it may remove
+	# detections without replacing them with second-best ones. Too bad.
+	foreach $driver (keys %chips_detected) {
+		$detected_ref = $chips_detected{$driver};
+		for ($i = @$detected_ref-1; $i >=0; $i--) {
+			next unless defined $detected_ref->[$i]->{i2c_addr};
+			@entry_addrs = ($detected_ref->[$i]->{i2c_addr});
+			push @entry_addrs, @{$detected_ref->[$i]->{i2c_sub_addrs}}
+				if exists $detected_ref->[$i]->{i2c_sub_addrs};
+			if ($detected_ref->[$i]->{i2c_devnr} == $datahash->{i2c_devnr} &&
+			    any_list_match(\@entry_addrs, \@hash_addrs)) {
+				splice @$detected_ref, $i, 1;
+				delete $chips_detected{$driver}
+					if (!@$detected_ref);
+			}
+		}
+	}
+
+	# Now add the new entry to detected
+	$chips_detected{$chipdriver} = []
+		unless exists $chips_detected{$chipdriver};
+	push @{$chips_detected{$chipdriver}}, $datahash;
+}
+
+# Fake i2c drivers such as "not-a-sensor" or "use-isa-instead" have to be
+# inserted so that confidence comparison can be performed. But then we have
+# to filter them out so that the user doesn't get confused.
+sub filter_out_fake_i2c_drivers
+{
+	delete $chips_detected{"not-a-sensor"};
+	delete $chips_detected{"use-isa-instead"};
+}
+
+# This adds a detection to the above structure.
+# $_[0]: chip driver
+# $_[1]: reference to data hash
+sub add_isa_to_chips_detected
+{
+	my ($chipdriver, $datahash) = @_;
+	my ($i, $new_detected_ref, $detected_ref);
+
+	# First determine where the hash has to be added.
+	$chips_detected{$chipdriver} = []
+		unless exists $chips_detected{$chipdriver};
+	$new_detected_ref = $chips_detected{$chipdriver};
+
+	# Find out whether our new entry should go into the detected list
+	# or not. We only compare main isa_addr here, of course.
+	foreach $detected_ref (values %chips_detected) {
+		for ($i = 0; $i < @{$detected_ref}; $i++) {
+			if (exists $detected_ref->[$i]->{isa_addr} and
+			    exists $datahash->{isa_addr} and
+			    $detected_ref->[$i]->{isa_addr} == $datahash->{isa_addr}) {
+				if ($detected_ref->[$i]->{conf} < $datahash->{conf}) {
+					splice @$detected_ref, $i, 1;
+					push @$new_detected_ref, $datahash;
+				}
+				return;
+			}
+		}
+	}
+
+	# Not found? OK, put it in the detected list
+	push @$new_detected_ref, $datahash;
+}
+
+# $_[0]: reference to an array of chips which may be aliases of each other
+sub find_aliases
+{
+	my $detected = shift;
+	my ($isa, $i2c, $dev, $alias_detect, $is_alias);
+
+	for ($isa = 0; $isa < @{$detected}; $isa++) {
+		# Look for chips with an ISA address but no I2C address
+		next unless defined $detected->[$isa];
+		next unless $detected->[$isa]->{isa_addr};
+		next if defined $detected->[$isa]->{i2c_addr};
+		# Additionally, the chip must possibly have I2C aliases
+		next unless defined $detected->[$isa]->{alias_detect};
+
+		for ($i2c = 0; $i2c < @{$detected}; $i2c++) {
+			# Look for chips with an I2C address but no ISA address
+			next unless defined $detected->[$i2c];
+			next unless defined $detected->[$i2c]->{i2c_addr};
+			next if $detected->[$i2c]->{isa_addr};
+			# Additionally, it must have the same chip name
+			next unless $detected->[$i2c]->{chipname} eq
+				    $detected->[$isa]->{chipname};
+
+			# We have potential aliases, check if they actually are
+			$dev = $dev_i2c.$detected->[$i2c]->{i2c_devnr};
+			open(local *FILE, $dev) or
+				print("Can't open $dev\n"),
+				next;
+			binmode(FILE);
+			i2c_set_slave_addr(\*FILE, $detected->[$i2c]->{i2c_addr}) or
+				print("Can't set I2C address for $dev\n"),
+				next;
+
+			initialize_ioports();
+			$alias_detect = $detected->[$isa]->{alias_detect};
+			$is_alias = &$alias_detect($detected->[$isa]->{isa_addr},
+						   \*FILE,
+						   $detected->[$i2c]->{i2c_addr});
+			close(FILE);
+			close_ioports();
+
+			next unless $is_alias;
+			# This is an alias: copy the I2C data into the ISA
+			# entry, then discard the I2C entry
+			$detected->[$isa]->{i2c_devnr} = $detected->[$i2c]->{i2c_devnr};
+			$detected->[$isa]->{i2c_addr} = $detected->[$i2c]->{i2c_addr};
+			$detected->[$isa]->{i2c_sub_addr} = $detected->[$i2c]->{i2c_sub_addr};
+			undef $detected->[$i2c];
+			last;
+		}
+	}
+
+	# The loops above may have made the chip list sparse, make it
+	# compact again
+	for ($isa = 0; $isa < @{$detected}; ) {
+		if (defined($detected->[$isa])) {
+			$isa++;
+		} else {
+			splice @{$detected}, $isa, 1;
+		}
+	}
+}
+
+# From the list of known I2C/SMBus devices, build a list of I2C addresses
+# which are worth probing. There's no point in probing an address for which
+# we don't know a single device, and probing some addresses has caused
+# random trouble in the past.
+sub i2c_addresses_to_scan
+{
+	my @used;
+	my @addresses;
+	my $addr;
+
+	foreach my $chip (@chip_ids) {
+		next unless defined $chip->{i2c_addrs};
+		foreach $addr (@{$chip->{i2c_addrs}}) {
+			$used[$addr]++;
+		}
+	}
+
+	for ($addr = 0x03; $addr <= 0x77; $addr++) {
+		push @addresses, $addr if $used[$addr];
+	}
+	return \@addresses;
+}
+
+# $_[0]: The number of the adapter to scan
+# $_[1]: Address
+sub add_busy_i2c_address
+{
+	my ($adapter_nr, $addr) = @_;
+	# If the address is busy, we can normally find out which driver
+	# requested it (if the kernel is recent enough, at least 2.6.16 and
+	# later are known to work), and we assume it is the right one.
+	my ($device, $driver, $new_hash);
+
+	$device = sprintf("$sysfs_root/bus/i2c/devices/\%d-\%04x",
+			  $adapter_nr, $addr);
+	$driver = sysfs_device_driver($device);
+
+	if (!defined($driver)) {
+		printf("Client at address 0x%02x can not be probed - ".
+		       "unload all client drivers first!\n", $addr);
+		return;
+	}
+
+	$new_hash = {
+		conf => 6, # Arbitrary confidence
+		i2c_addr => $addr,
+		chipname => sysfs_device_attribute($device, "name")
+			 || "unknown",
+		i2c_devnr => $adapter_nr,
+	};
+
+	printf "Client found at address 0x\%02x\n", $addr;
+	printf "Handled by driver `\%s' (already loaded), chip type `\%s'\n",
+	       $driver, $new_hash->{chipname};
+
+	# Only add it to the list if this is something we would have detected,
+	# else we end up with random i2c chip drivers listed (for example
+	# media/video drivers.)
+	if (exists $modules_supported{$driver}) {
+		add_i2c_to_chips_detected($driver, $new_hash);
+	} else {
+		print "    (note: this is probably NOT a sensor chip!)\n";
+	}
+}
+
+# $_[0]: The number of the adapter to scan
+# $_[1]: Address
+# $_[2]: Chip being probed
+sub probe_free_i2c_address
+{
+	my ($adapter_nr, $addr, $chip) = @_;
+	my ($conf, @other_addr, $new_hash);
+
+	printf("\%-60s", sprintf("Probing for `\%s'... ", $chip->{name}));
+	if (($conf, @other_addr) = &{$chip->{i2c_detect}} (\*FILE, $addr)) {
+		if ($chip->{driver} eq "not-a-sensor") {
+			print "Yes\n",
+			      "    (confidence $conf, not a hardware monitoring chip";
+		} else {
+			print "Success!\n",
+			      "    (confidence $conf, driver `$chip->{driver}'";
+		}
+		if (@other_addr) {
+			print ", other addresses:";
+			@other_addr = sort @other_addr;
+			foreach my $other_addr (@other_addr) {
+				printf(" 0x%02x", $other_addr);
+			}
+		}
+		print ")\n";
+
+		$new_hash = {
+			conf => $conf,
+			i2c_addr => $addr,
+			chipname => $chip->{name},
+			i2c_devnr => $adapter_nr,
+		};
+		if (@other_addr) {
+			my @other_addr_copy = @other_addr;
+			$new_hash->{i2c_sub_addrs} = \@other_addr_copy;
+		}
+		add_i2c_to_chips_detected($chip->{driver}, $new_hash);
+	} else {
+		print "No\n";
+	}
+}
+
+# $_[0]: The device to check (PCI or not)
+# Returns: PCI class of the adapter if available, 0 if not
+sub get_pci_class
+{
+	my ($device) = @_;
+	my ($subsystem, $class);
+
+	$subsystem = sysfs_device_subsystem($device);
+	return 0 unless defined $subsystem && $subsystem eq "pci";
+
+	$class = sysfs_device_attribute($device, "class");
+	return 0 unless defined $class;
+	$class = oct($class) if $class =~ /^0/;
+	return $class >> 8;
+}
+
+# $_[0]: The number of the adapter to scan
+sub scan_i2c_adapter
+{
+	my ($adapter_nr, $smbus_default) = @_;
+	my ($funcs, $chip, $addr, $class, $default, $input, @not_to_scan);
+
+	$class = get_pci_class($i2c_adapters[$adapter_nr]->{parent});
+	if (($class & 0xff00) == 0x0400) {
+		# Do not probe adapters on PCI multimedia cards by default
+		$default = 0;
+	} elsif ($class == 0x0c01 || $class == 0x0c05
+	      || find_i2c_adapter_driver($i2c_adapters[$adapter_nr]->{name})) {
+		$default = $smbus_default;
+	} else {
+		$default = 1;
+	}
+
+	printf "Next adapter: $i2c_adapters[$adapter_nr]->{name} (i2c-$adapter_nr)\n".
+	       "Do you want to scan it? (\%s/selectively): ",
+	       $default ? "YES/no" : "yes/NO";
+
+	$input = <STDIN>;
+	if ($input =~ /^\s*n/i
+	 || (!$default && $input !~ /^\s*[ys]/i)) {
+		print "\n";
+		return;
+	}
+
+	if ($input =~ /^\s*s/i) {
+		print "Please enter one or more addresses not to scan. Separate them with commas.\n",
+		      "You can specify a range by using dashes. Example: 0x58-0x5f,0x69.\n",
+		      "Addresses: ";
+		$input = <STDIN>;
+		chomp($input);
+		@not_to_scan = parse_not_to_scan(0x03, 0x77, $input);
+	}
+
+	open(local *FILE, "$dev_i2c$adapter_nr") or
+		(print "Can't open $dev_i2c$adapter_nr\n"), return;
+	binmode(FILE);
+
+	# Can we probe this adapter?
+	$funcs = i2c_get_funcs(\*FILE);
+	if ($funcs < 0) {
+		print "Adapter failed to provide its functionalities, skipping.\n";
+		return;
+	}
+	if (!($funcs & (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA))) {
+		print "Adapter cannot be probed, skipping.\n";
+		return;
+	}
+	if (~$funcs & (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
+		print "Adapter doesn't support all probing functions.\n",
+		      "Some addresses won't be probed.\n";
+	}
+
+	# Now scan each address in turn
+	foreach $addr (@{$i2c_addresses_to_scan}) {
+		# As the not_to_scan list is sorted, we can check it fast
+		shift @not_to_scan # User skipped an address which we didn't intend to probe anyway
+			while (@not_to_scan and $not_to_scan[0] < $addr);
+		if (@not_to_scan and $not_to_scan[0] == $addr) {
+			shift @not_to_scan;
+			next;
+		}
+
+		if (!i2c_set_slave_addr(\*FILE, $addr)) {
+			add_busy_i2c_address($adapter_nr, $addr);
+			next;
+		}
+
+		next unless i2c_probe(\*FILE, $addr, $funcs);
+		printf "Client found at address 0x%02x\n", $addr;
+		if (!i2c_safety_check(\*FILE)) {
+			print "Seems to be a 1-register-only device, skipping.\n";
+			next;
+		}
+
+		$| = 1;
+		foreach $chip (@chip_ids, @non_hwmon_chip_ids) {
+			next unless exists $chip->{i2c_addrs}
+				 && contains($addr, @{$chip->{i2c_addrs}});
+			probe_free_i2c_address($adapter_nr, $addr, $chip);
+		}
+		$| = 0;
+	}
+	print "\n";
+}
+
+sub scan_isa_bus
+{
+	my $chip_list_ref = shift;
+	my ($chip, $addr, $conf);
+
+	$| = 1;
+	foreach $chip (@{$chip_list_ref}) {
+		next if not exists $chip->{isa_addrs} or not exists $chip->{isa_detect};
+		foreach $addr (@{$chip->{isa_addrs}}) {
+			printf("\%-60s", sprintf("Probing for `\%s' at 0x\%x... ",
+						 $chip->{name}, $addr));
+			$conf = &{$chip->{isa_detect}} ($addr);
+			print("No\n"), next if not defined $conf;
+			print "Success!\n";
+			printf "    (confidence %d, driver `%s')\n", $conf, $chip->{driver};
+			my $new_hash = {
+				conf => $conf,
+				isa_addr => $addr,
+				chipname => $chip->{name},
+				alias_detect => $chip->{alias_detect},
+			};
+			add_isa_to_chips_detected($chip->{driver}, $new_hash);
+		}
+	}
+	$| = 0;
+}
+
+use vars qw(%standard_superio);
+
+# The following are taken from the PNP ISA spec (so it's supposed
+# to be common to all Super I/O chips):
+#  devidreg: The device ID register(s)
+#  logdevreg: The logical device register
+#  actreg: The activation register within the logical device
+#  actmask: The activation bit in the activation register
+#  basereg_*: The I/O base registers within the logical device
+%standard_superio = (
+	devidreg => 0x20,
+	logdevreg => 0x07,
+	actreg => 0x30,
+	actmask => 0x01,
+	basereg_msb => 0x60,
+	basereg_lsb => 0x61,
+);
+
+sub exit_superio
+{
+	my ($addrreg, $datareg) = @_;
+
+	# Some chips (SMSC, Winbond) want this
+	outb($addrreg, 0xaa);
+
+	# Return to "Wait For Key" state (PNP-ISA spec)
+	outb($addrreg, 0x02);
+	outb($datareg, 0x02);
+}
+
+# Guess if an unknown Super-I/O chip has sensors
+sub guess_superio_ld
+{
+	my ($addrreg, $datareg, $typical_addr) = @_;
+	my ($oldldn, $ldn, $addr);
+
+	# Save logical device number
+	outb($addrreg, $standard_superio{logdevreg});
+	$oldldn = inb($datareg);
+
+	for ($ldn = 0; $ldn < 16; $ldn++) {
+		# Select logical device
+		outb($addrreg, $standard_superio{logdevreg});
+		outb($datareg, $ldn);
+
+		# Read base I/O address
+		outb($addrreg, $standard_superio{basereg_msb});
+		$addr = inb($datareg) << 8;
+		outb($addrreg, $standard_superio{basereg_lsb});
+		$addr |= inb($datareg);
+		next unless ($addr & 0xfff8) == $typical_addr;
+
+		printf "    (logical device \%X has address 0x\%x, could be sensors)\n",
+		       $ldn, $addr;
+		last;
+	}
+
+	# Be nice, restore original logical device
+	outb($addrreg, $standard_superio{logdevreg});
+	outb($datareg, $oldldn);
+}
+
+# Returns: features bitmask if device added to chips_detected, 0 if not
+sub probe_superio
+{
+	my ($addrreg, $datareg, $chip) = @_;
+	my ($val, $addr);
+	my %superio;
+
+	# Use chip-specific registers if provided
+	if (exists $chip->{regs}) {
+		%superio = overlay_hash(\%standard_superio, $chip->{regs});
+	} else {
+		%superio = %standard_superio;
+	}
+
+	if (exists $chip->{check}) {
+		return 0 unless $chip->{check}($addrreg, $datareg);
+	}
+
+	printf "\%-60s", "Found `$chip->{name}'";
+
+	# Does it have hardware monitoring capabilities?
+	if (!exists $chip->{driver}) {
+		print "\n    (no information available)\n";
+		return 0;
+	}
+	if ($chip->{driver} eq "not-a-sensor") {
+		print "\n    (no hardware monitoring capabilities)\n";
+		return 0;
+	}
+	if ($chip->{driver} eq "via-smbus-only") {
+		print "\n    (hardware monitoring capabilities accessible via SMBus only)\n";
+		return FEAT_SMBUS;
+	}
+
+	# Switch to the sensor logical device
+	outb($addrreg, $superio{logdevreg});
+	outb($datareg, $chip->{logdev});
+
+	# Get the IO base address
+	outb($addrreg, $superio{basereg_msb});
+	$addr = inb($datareg);
+	outb($addrreg, $superio{basereg_lsb});
+	$addr = ($addr << 8) | inb($datareg);
+
+	# Check the activation register and base address
+	outb($addrreg, $superio{actreg});
+	$val = inb($datareg);
+	if (!($val & $superio{actmask})) {
+		if ($addr && $addr != 0xffff) {
+			printf "\n    (address 0x\%x, but not activated)\n", $addr;
+		} else {
+			print "\n    (but not activated)\n";
+		}
+		return 0;
+	}
+	if ($addr == 0 || $addr == 0xffff) {
+		print "\n    (but no address specified)\n";
+		return 0;
+	}
+
+	print "Success!\n";
+	printf "    (address 0x\%x, driver `%s')\n", $addr, $chip->{driver};
+	my $new_hash = {
+		conf => 9,
+		isa_addr => $addr,
+		chipname => $chip->{name}
+	};
+	add_isa_to_chips_detected($chip->{driver}, $new_hash);
+	return $chip->{features};
+}
+
+# Detection routine for non-standard SMSC Super I/O chips
+# $_[0]: Super I/O LPC config/index port
+# $_[1]: Super I/O LPC data port
+# $_[2]: Reference to array of non-standard chips
+# Return values: 1 if non-standard chip found, 0 otherwise
+sub smsc_ns_detect_superio
+{
+	my ($addrreg, $datareg, $ns_chips) = @_;
+	my ($val, $chip);
+
+	# read alternate device ID register
+	outb($addrreg, 0x0d);
+	$val = inb($datareg);
+	return 0 if $val == 0x00 || $val == 0xff;
+
+	print "Yes\n";
+
+	foreach $chip (@{$ns_chips}) {
+		if ($chip->{devid} == $val) {
+			probe_superio($addrreg, $datareg, $chip);
+			return 1;
+		}
+	}
+
+	printf("Found unknown non-standard chip with ID 0x%02x\n", $val);
+	return 1;
+}
+
+# Returns: features supported by the device added, if any
+sub scan_superio
+{
+	my ($addrreg, $datareg) = @_;
+	my ($val, $found);
+	my $features = 0;
+
+	printf("Probing for Super-I/O at 0x\%x/0x\%x\n", $addrreg, $datareg);
+
+	$| = 1;
+	# reset state to avoid false positives
+	exit_superio($addrreg, $datareg);
+	foreach my $family (@superio_ids) {
+		printf("\%-60s", "Trying family `$family->{family}'... ");
+		# write the password
+		foreach $val (@{$family->{enter}->{$addrreg}}) {
+			outb($addrreg, $val);
+		}
+		# call the non-standard detection routine first if it exists
+		if (defined($family->{ns_detect}) &&
+		    &{$family->{ns_detect}}($addrreg, $datareg, $family->{ns_chips})) {
+			last;
+		}
+
+		# did it work?
+		outb($addrreg, $standard_superio{devidreg});
+		$val = inb($datareg);
+		outb($addrreg, $standard_superio{devidreg} + 1);
+		$val = ($val << 8) | inb($datareg);
+		if ($val == 0x0000 || $val == 0xffff) {
+			print "No\n";
+			next;
+		}
+		print "Yes\n";
+
+		$found = 0;
+		foreach my $chip (@{$family->{chips}}) {
+			if (($chip->{devid} > 0xff &&
+			     ($val & ($chip->{devid_mask} || 0xffff)) == $chip->{devid})
+			 || ($chip->{devid} <= 0xff &&
+			     ($val >> 8) == $chip->{devid})) {
+				$features |= probe_superio($addrreg, $datareg, $chip);
+				$found++;
+			}
+		}
+
+		if (!$found) {
+			printf("Found unknown chip with ID 0x%04x\n", $val);
+			# Guess if a logical device could correspond to sensors
+			guess_superio_ld($addrreg, $datareg, $family->{guess})
+				if defined $family->{guess};
+		}
+		last;
+	}
+	exit_superio($addrreg, $datareg);
+	$| = 0;
+	return $features;
+}
+
+sub scan_cpu
+{
+	my $entry = shift;
+	my $confidence;
+
+	printf("\%-60s", "$entry->{name}... ");
+	if (defined ($confidence = $entry->{detect}())) {
+		print "Success!\n";
+		printf "    (driver `%s')\n", $entry->{driver};
+		my $new_hash = {
+			conf => $confidence,
+			chipname => $entry->{name},
+		};
+		add_isa_to_chips_detected($entry->{driver}, $new_hash);
+	} else {
+		print "No\n";
+	}
+}
+
+##################
+# CHIP DETECTION #
+##################
+
+# This routine allows you to dynamically update the chip detection list.
+# The most common use is to allow for different chip to driver mappings
+# based on different linux kernels
+sub chip_special_cases
+{
+	# Some chip to driver mappings depend on the environment
+	foreach my $chip (@chip_ids) {
+		if (ref($chip->{driver}) eq 'CODE') {
+			$chip->{driver} = $chip->{driver}->();
+		}
+	}
+
+	# Also fill the fake driver name of non-hwmon chips
+	foreach my $chip (@non_hwmon_chip_ids) {
+		$chip->{driver} = "not-a-sensor";
+	}
+}
+
+# Each function returns a confidence value. The higher this value, the more
+# sure we are about this chip. This may help overrule false positives,
+# although we also attempt to prevent false positives in the first place.
+
+# Each function returns a list. The first element is the confidence value;
+# Each element after it is an SMBus address. In this way, we can detect
+# chips with several SMBus addresses. The SMBus address for which the
+# function was called is never returned.
+
+# All I2C detection functions below take at least 2 parameters:
+# $_[0]: Reference to the file descriptor to access the chip
+# $_[1]: Address
+# Some of these functions which can detect more than one type of device,
+# take a third parameter:
+# $_[2]: Chip to detect
+
+# Registers used: 0x58
+sub mtp008_detect
+{
+	my ($file, $addr) = @_;
+	return if i2c_smbus_read_byte_data($file, 0x58) != 0xac;
+	return 3;
+}
+
+# Chip to detect: 0 = LM78, 2 = LM79
+# Registers used:
+#   0x40: Configuration
+#   0x48: Full I2C Address
+#   0x49: Device ID
+sub lm78_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $reg;
+
+	return unless i2c_smbus_read_byte_data($file, 0x48) == $addr;
+	return unless (i2c_smbus_read_byte_data($file, 0x40) & 0x80) == 0x00;
+
+	$reg = i2c_smbus_read_byte_data($file, 0x49);
+	return if $chip == 0 && ($reg != 0x00 && $reg != 0x20 && $reg != 0x40);
+	return if $chip == 2 && ($reg & 0xfe) != 0xc0;
+
+	# Explicitly prevent misdetection of Winbond chips
+	$reg = i2c_smbus_read_byte_data($file, 0x4f);
+	return if $reg == 0xa3 || $reg == 0x5c;
+
+	return 6;
+}
+
+# Chip to detect: 0 = LM75, 1 = DS75, 2 = LM75A
+# Registers used:
+#   0x00: Temperature
+#   0x01: Configuration
+#   0x02: Hysteresis
+#   0x03: Overtemperature Shutdown
+#   0x04-0x07: No registers
+#   0x07: Device ID (LM75A only)
+# The first detection step is based on the fact that the LM75 has only
+# four registers, and cycles addresses over 8-byte boundaries. We use the
+# 0x04-0x07 addresses (unused) to improve the reliability. These are not
+# real registers and will always return the last returned value. This isn't
+# documented.
+# Note that register 0x00 may change, so we can't use the modulo trick on it.
+# The DS75 is a bit different, it doesn't cycle over 8-byte boundaries, and
+# all register addresses from 0x04 to 0x0f behave like 0x04-0x07 do for
+# the LM75.
+# And the LM75A is again different, it cycles over 8-byte boundaries, but
+# registers 0x04-0x06 return 0xff. Thankfully it has a device ID register
+# at 0x07.
+# Not all devices enjoy SMBus read word transactions, so we use read byte
+# transactions even for the 16-bit registers. The low bits aren't very
+# useful for detection anyway.
+sub lm75_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $i;
+	my $cur = i2c_smbus_read_byte_data($file, 0x00);
+	my $conf = i2c_smbus_read_byte_data($file, 0x01);
+	my ($maxreg, $hyst, $os, $dev_id);
+
+	if ($chip == 2) {	# LM75A
+		$dev_id = i2c_smbus_read_byte_data($file, 0x07);
+		return if $dev_id != 0xA1;
+
+		$hyst = i2c_smbus_read_byte_data($file, 0x02);
+		$os = i2c_smbus_read_byte_data($file, 0x03);
+
+		for $i (0x04 .. 0x06) {
+			return if i2c_smbus_read_byte_data($file, $i) != 0xff;
+		}
+	} else {		# LM75 or DS75
+		$maxreg = $chip == 1 ? 0x0f : 0x07;
+		$hyst = i2c_smbus_read_byte_data($file, 0x02, NO_CACHE);
+		for $i (0x04 .. $maxreg) {
+			return if i2c_smbus_read_byte_data($file, $i, NO_CACHE) != $hyst;
+		}
+
+		$os = i2c_smbus_read_byte_data($file, 0x03, NO_CACHE);
+		for $i (0x04 .. $maxreg) {
+			return if i2c_smbus_read_byte_data($file, $i, NO_CACHE) != $os;
+		}
+	}
+
+	if ($chip != 1) {	# LM75 or LM75A
+		for ($i = 8; $i <= 248; $i += 40) {
+			return if i2c_smbus_read_byte_data($file, $i + 0x01) != $conf
+			       or i2c_smbus_read_byte_data($file, $i + 0x02) != $hyst
+			       or i2c_smbus_read_byte_data($file, $i + 0x03) != $os;
+			return if $chip == 2
+			      and i2c_smbus_read_byte_data($file, $i + 0x07) != $dev_id;
+		}
+	}
+
+	# All registers hold the same value, obviously a misdetection
+	return if $conf == $cur and $cur == $hyst and $cur == $os;
+
+	# Unused bits
+	return if $chip != 1 and ($conf & 0xe0);
+	return if $chip == 1 and ($conf & 0x80);
+
+	# Most probable value ranges
+	return 6 if $cur <= 100 and ($hyst >= 10 && $hyst <= 125)
+		and ($os >= 20 && $os <= 127) and $hyst < $os;
+	return 3;
+}
+
+# Registers used:
+#   0x00: Temperature
+#   0x01: Configuration
+#   0x02: High Limit
+#   0x03: Low Limit
+#   0x04: Status
+#   0x07: Manufacturer ID and Product ID
+sub lm73_detect
+{
+	my ($file, $addr) = @_;
+
+	my $conf = i2c_smbus_read_byte_data($file, 0x01);
+	my $status = i2c_smbus_read_byte_data($file, 0x04);
+
+	# Bits that always return 0
+	return if ($conf & 0x0c) or ($status & 0x10);
+
+	return if i2c_smbus_read_word_data($file, 0x07) != 0x9001;
+
+	# Make sure the chip supports SMBus read word transactions
+	my $cur = i2c_smbus_read_word_data($file, 0x00);
+	return if $cur < 0;
+	my $high = i2c_smbus_read_word_data($file, 0x02);
+	return if $high < 0;
+	my $low = i2c_smbus_read_word_data($file, 0x03);
+	return if $low < 0;
+	return if ($cur & 0x0300) or (($high | $low) & 0x1f00);
+
+	return 3;
+}
+
+# Registers used:
+#   0x00: Temperature
+#   0x01: Configuration
+#   0x02: Hysteresis
+#   0x03: Overtemperature Shutdown
+#   0x04: Low limit
+#   0x05: High limit
+#   0x06-0x07: No registers
+# The first detection step is based on the fact that the LM77 has only
+# six registers, and cycles addresses over 8-byte boundaries. We use the
+# 0x06-0x07 addresses (unused) to improve the reliability. These are not
+# real registers and will always return the last returned value. This isn't
+# documented.
+# Note that register 0x00 may change, so we can't use the modulo trick on it.
+# Not all devices enjoy SMBus read word transactions, so we use read byte
+# transactions even for the 16-bit registers at first. We only use read word
+# transactions in the end when we are already almost certain that we have an
+# LM77 chip.
+sub lm77_detect
+{
+	my ($file, $addr) = @_;
+	my $i;
+	my $cur = i2c_smbus_read_byte_data($file, 0x00);
+	my $conf = i2c_smbus_read_byte_data($file, 0x01);
+	my $hyst = i2c_smbus_read_byte_data($file, 0x02);
+	my $os = i2c_smbus_read_byte_data($file, 0x03);
+
+	my $low = i2c_smbus_read_byte_data($file, 0x04, NO_CACHE);
+	return if i2c_smbus_read_byte_data($file, 0x06, NO_CACHE) != $low;
+	return if i2c_smbus_read_byte_data($file, 0x07, NO_CACHE) != $low;
+
+	my $high = i2c_smbus_read_byte_data($file, 0x05, NO_CACHE);
+	return if i2c_smbus_read_byte_data($file, 0x06, NO_CACHE) != $high;
+	return if i2c_smbus_read_byte_data($file, 0x07, NO_CACHE) != $high;
+
+	for ($i = 8; $i <= 248; $i += 40) {
+		return if i2c_smbus_read_byte_data($file, $i + 0x01) != $conf;
+		return if i2c_smbus_read_byte_data($file, $i + 0x02) != $hyst;
+		return if i2c_smbus_read_byte_data($file, $i + 0x03) != $os;
+		return if i2c_smbus_read_byte_data($file, $i + 0x04) != $low;
+		return if i2c_smbus_read_byte_data($file, $i + 0x05) != $high;
+	}
+
+	# All registers hold the same value, obviously a misdetection
+	return if $conf == $cur and $cur == $hyst
+	      and $cur == $os and $cur == $low and $cur == $high;
+
+	# Unused bits
+	return if ($conf & 0xe0)
+	       or (($cur >> 4) != 0 && ($cur >> 4) != 0xf)
+	       or (($hyst >> 4) != 0 && ($hyst >> 4) != 0xf)
+	       or (($os >> 4) != 0 && ($os >> 4) != 0xf)
+	       or (($low >> 4) != 0 && ($low >> 4) != 0xf)
+	       or (($high >> 4) != 0 && ($high >> 4) != 0xf);
+
+	# Make sure the chip supports SMBus read word transactions
+	foreach $i (0x00, 0x02, 0x03, 0x04, 0x05) {
+		return if i2c_smbus_read_word_data($file, $i) < 0;
+	}
+
+	return 3;
+}
+
+# Chip to detect: 0 = LM92, 1 = LM76, 2 = MAX6633/MAX6634/MAX6635
+# Registers used:
+#   0x01: Configuration (National Semiconductor only)
+#   0x02: Hysteresis
+#   0x03: Critical Temp
+#   0x04: Low Limit
+#   0x05: High Limit
+#   0x07: Manufacturer ID (LM92 only)
+# One detection step is based on the fact that the LM92 and clones have a
+# limited number of registers, which cycle modulo 16 address values.
+# Note that register 0x00 may change, so we can't use the modulo trick on it.
+# Not all devices enjoy SMBus read word transactions, so we use read byte
+# transactions even for the 16-bit registers at first. We only use read
+# word transactions in the end when we are already almost certain that we
+# have an LM92 chip or compatible.
+sub lm92_detect
+{
+	my ($file, $addr, $chip) = @_;
+
+	my $conf = i2c_smbus_read_byte_data($file, 0x01);
+	my $hyst = i2c_smbus_read_byte_data($file, 0x02);
+	my $crit = i2c_smbus_read_byte_data($file, 0x03);
+	my $low = i2c_smbus_read_byte_data($file, 0x04);
+	my $high = i2c_smbus_read_byte_data($file, 0x05);
+
+	return if $conf == 0 and $hyst == 0 and $crit == 0
+		and $low == 0 and $high == 0;
+
+	# Unused bits
+	return if ($chip == 0 || $chip == 1)
+	      and ($conf & 0xE0);
+
+	for (my $i = 0; $i <= 240; $i += 16) {
+		return if i2c_smbus_read_byte_data($file, $i + 0x01) != $conf;
+		return if i2c_smbus_read_byte_data($file, $i + 0x02) != $hyst;
+		return if i2c_smbus_read_byte_data($file, $i + 0x03) != $crit;
+		return if i2c_smbus_read_byte_data($file, $i + 0x04) != $low;
+		return if i2c_smbus_read_byte_data($file, $i + 0x05) != $high;
+	}
+
+	return if $chip == 0
+	      and i2c_smbus_read_word_data($file, 0x07) != 0x0180;
+
+	# Make sure the chip supports SMBus read word transactions
+	$hyst = i2c_smbus_read_word_data($file, 0x02);
+	return if $hyst < 0;
+	$crit = i2c_smbus_read_word_data($file, 0x03);
+	return if $crit < 0;
+	$low = i2c_smbus_read_word_data($file, 0x04);
+	return if $low < 0;
+	$high = i2c_smbus_read_word_data($file, 0x05);
+	return if $high < 0;
+
+	foreach my $temp ($hyst, $crit, $low, $high) {
+		return if $chip == 2 and ($temp & 0x7F00);
+		return if $chip != 2 and ($temp & 0x0700);
+	}
+
+	return ($chip == 0) ? 4 : 2;
+}
+
+# Registers used:
+#   0xAA: Temperature
+#   0xA1: High limit
+#   0xA2: Low limit
+#   0xA8: Counter
+#   0xA9: Slope
+#   0xAC: Configuration
+# Detection is weak. We check if bit 4 (NVB) is clear, because it is
+# unlikely to be set (would mean that EEPROM is currently being accessed).
+# We also check the value of the counter and slope registers, the datasheet
+# doesn't mention the possible values but the conversion formula together
+# with experimental evidence suggest possible sanity checks.
+# Not all devices enjoy SMBus read word transactions, so we do as much as
+# possible with read byte transactions first, and only use read word
+# transactions second.
+sub ds1621_detect
+{
+	my ($file, $addr) = @_;
+
+	my $conf = i2c_smbus_read_byte_data($file, 0xAC);
+	return if ($conf & 0x10);
+
+	my $temp = i2c_smbus_read_word_data($file, 0xAA);
+	return if $temp < 0 || ($temp & 0x0f00);
+	# On the DS1631, the following two checks are too strict in theory,
+	# but in practice I very much doubt that anyone will set temperature
+	# limits not a multiple of 0.5 degrees C.
+	my $high = i2c_smbus_read_word_data($file, 0xA1);
+	return if $high < 0 || ($high & 0x7f00);
+	my $low = i2c_smbus_read_word_data($file, 0xA2);
+	return if $low < 0 || ($low & 0x7f00);
+
+	return if ($temp == 0 && $high == 0 && $low == 0 && $conf == 0);
+
+	# Old versions of the DS1621 apparently don't have the counter and
+	# slope registers (or they return crap)
+	my $counter = i2c_smbus_read_byte_data($file, 0xA8);
+	my $slope = i2c_smbus_read_byte_data($file, 0xA9);
+	return ($slope == 0x10 && $counter <= $slope) ? 3 : 2;
+}
+
+# Registers used:
+#   0x00: Configuration register
+#   0x02: Interrupt state register
+#   0x2a-0x3d: Limits registers
+# This one is easily misdetected since it doesn't provide identification
+# registers. So we have to use some tricks:
+#   - 6-bit addressing, so limits readings modulo 0x40 should be unchanged
+#   - positive temperature limits
+#   - limits order correctness
+# Hopefully this should limit the rate of false positives, without increasing
+# the rate of false negatives.
+# Thanks to Lennard Klein for testing on a non-LM80 chip, which was
+# previously misdetected, and isn't anymore. For reference, it scored
+# a final confidence of 0, and changing from strict limit comparisons
+# to loose comparisons did not change the score.
+sub lm80_detect
+{
+	my ($file, $addr) = @_;
+	my ($i, $reg);
+
+	return if (i2c_smbus_read_byte_data($file, 0x00) & 0x80) != 0;
+	return if (i2c_smbus_read_byte_data($file, 0x02) & 0xc0) != 0;
+
+	for ($i = 0x2a; $i <= 0x3d; $i++) {
+		$reg = i2c_smbus_read_byte_data($file, $i);
+		return if i2c_smbus_read_byte_data($file, $i+0x40) != $reg;
+		return if i2c_smbus_read_byte_data($file, $i+0x80) != $reg;
+		return if i2c_smbus_read_byte_data($file, $i+0xc0) != $reg;
+	}
+
+	# Refine a bit by checking whether limits are in the correct order
+	# (min<max for voltages, hyst<max for temperature). Since it is still
+	# possible that the chip is an LM80 with limits not properly set,
+	# a few "errors" are tolerated.
+	my $confidence = 0;
+	for ($i = 0x2a; $i <= 0x3a; $i++) {
+		$confidence++
+			if i2c_smbus_read_byte_data($file, $i) < i2c_smbus_read_byte_data($file, $i+1);
+	}
+	# hot temp<OS temp
+	$confidence++
+		if i2c_smbus_read_byte_data($file, 0x38) < i2c_smbus_read_byte_data($file, 0x3a);
+
+	# Negative temperature limits are unlikely.
+	for ($i = 0x3a; $i <= 0x3d; $i++) {
+		$confidence++ if (i2c_smbus_read_byte_data($file, $i) & 0x80) == 0;
+	}
+
+	# $confidence is between 0 and 14
+	$confidence = ($confidence >> 1) - 4;
+	# $confidence is now between -4 and 3
+
+	return unless $confidence > 0;
+
+	return $confidence;
+}
+
+# Registers used:
+#   0x02: Status 1
+#   0x03: Configuration
+#   0x04: Company ID of LM84
+#   0x35: Status 2
+#   0xfe: Manufacturer ID
+#   0xff: Chip ID / die revision
+# We can use the LM84 Company ID register because the LM83 and the LM82 are
+# compatible with the LM84.
+# The LM83 chip ID is missing from the datasheet and was contributed by
+# Magnus Forsstrom: 0x03.
+# At least some revisions of the LM82 seem to be repackaged LM83, so they
+# have the same chip ID, and temp2/temp4 will be stuck in "OPEN" state.
+# For this reason, we don't even try to distinguish between both chips.
+# Thanks to Ben Gardner for reporting.
+sub lm83_detect
+{
+	my ($file, $addr) = @_;
+	return if i2c_smbus_read_byte_data($file, 0xfe) != 0x01;
+	my $chipid = i2c_smbus_read_byte_data($file, 0xff);
+	return if $chipid != 0x01 && $chipid != 0x03;
+
+	my $confidence = 4;
+	$confidence++
+		if (i2c_smbus_read_byte_data($file, 0x02) & 0xa8) == 0x00;
+	$confidence++
+		if (i2c_smbus_read_byte_data($file, 0x03) & 0x41) == 0x00;
+	$confidence++
+		if i2c_smbus_read_byte_data($file, 0x04) == 0x00;
+	$confidence++
+		if (i2c_smbus_read_byte_data($file, 0x35) & 0x48) == 0x00;
+
+	return $confidence;
+}
+
+# Chip to detect: 0 = MAX6680/81, 1 = MAX6695/96
+# Registers used:
+#   0x03: Configuration
+#   0x04: Conversion rate
+#   0x12: Status2
+#   0x16: Overtemperature 2
+#   0xfe: Manufacturer ID
+#   0xff: Chip ID / die revision
+sub max6680_95_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $cid = i2c_smbus_read_byte_data($file, 0xff);
+	my $conf = i2c_smbus_read_byte_data($file, 0x03);
+	my $mid = i2c_smbus_read_byte_data($file, 0xfe, NO_CACHE);
+	my $emerg = i2c_smbus_read_byte_data($file, 0x16, NO_CACHE);
+	my $rate = i2c_smbus_read_byte_data($file, 0x04, NO_CACHE);
+	my $emerg2 = i2c_smbus_read_byte_data($file, 0x16, NO_CACHE);
+
+	# Check common conditions
+	return if $rate > 0x07;
+	return if $mid != 0x4d;		# Not Maxim
+	return if $cid != 0x01;		# None of the chips we are looking for
+
+	if ($chip == 0) {
+		return if ($conf & 0x03) != 0;
+		return 8 if $emerg != $emerg2;	# MAX6680/MAX6681
+	}
+	if ($chip == 1) {
+		my $status2 = i2c_smbus_read_byte_data($file, 0x12);
+
+		return if ($conf & 0x10) != 0;
+		return if ($status2 & 0x01) != 0;
+		return 8 if $emerg == $emerg2;	# MAX6695/MAX6696
+	}
+	return;
+}
+
+# Chip to detect: 0 = LM90, 1 = LM89/LM99, 2 = LM86, 3 = ADM1032,
+#		  4 = MAX6654, 5 = ADT7461,
+#		  6 = MAX6646/MAX6647/MAX6648/MAX6649/MAX6692,
+#		  8 = W83L771W/G, 9 = TMP401, 10 = TMP411,
+#		  11 = W83L771AWG/ASG, 12 = MAX6690
+# Registers used:
+#   0x03: Configuration
+#   0x04: Conversion rate
+#   0xbf: Configuration 2 (National Semiconductor and Winbond only)
+#   0xfe: Manufacturer ID
+#   0xff: Chip ID / die revision
+sub lm90_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $mid = i2c_smbus_read_byte_data($file, 0xfe);
+	my $cid = i2c_smbus_read_byte_data($file, 0xff);
+	my $conf = i2c_smbus_read_byte_data($file, 0x03);
+	my $rate = i2c_smbus_read_byte_data($file, 0x04);
+	my $conf2 = i2c_smbus_read_byte_data($file, 0xbf);
+
+	if ($chip == 0) {
+		return if ($conf & 0x2a) != 0;
+		return if ($conf2 & 0xf8) != 0;
+		return if $rate > 0x09;
+		return if $mid != 0x01;		# National Semiconductor
+		return 8 if $cid == 0x21;	# LM90
+		return 6 if ($cid & 0x0f) == 0x20;
+	}
+	if ($chip == 1) {
+		return if ($conf & 0x2a) != 0;
+		return if ($conf2 & 0xf8) != 0;
+		return if $rate > 0x09;
+		return if $mid != 0x01;		# National Semiconductor
+		return 8 if $addr == 0x4c and $cid == 0x31; # LM89/LM99
+		return 8 if $addr == 0x4d and $cid == 0x34; # LM89-1/LM99-1
+		return 6 if ($cid & 0x0f) == 0x30;
+	}
+	if ($chip == 2) {
+		return if ($conf & 0x2a) != 0;
+		return if ($conf2 & 0xf8) != 0;
+		return if $rate > 0x09;
+		return if $mid != 0x01;		# National Semiconductor
+		return 8 if $cid == 0x11;	# LM86
+		return 6 if ($cid & 0xf0) == 0x10;
+	}
+	if ($chip == 3) {
+		return if ($conf & 0x3f) != 0;
+		return if $rate > 0x0a;
+		return if $mid != 0x41;		# Analog Devices
+		return 6 if ($cid & 0xf0) == 0x40; # ADM1032
+	}
+	if ($chip == 4) {
+		return if ($conf & 0x07) != 0;
+		return if $rate > 0x07;
+		return if $mid != 0x4d;		# Maxim
+		return 8 if $cid == 0x08;	# MAX6654
+	}
+	if ($chip == 5) {
+		return if ($conf & 0x1b) != 0;
+		return if $rate > 0x0a;
+		return if $mid != 0x41;		# Analog Devices
+		return 8 if $cid == 0x51;	# ADT7461
+	}
+	if ($chip == 6) {
+		return if ($conf & 0x3f) != 0;
+		return if $rate > 0x07;
+		return if $mid != 0x4d;		# Maxim
+		return 8 if $cid == 0x59;	# MAX6648/MAX6692
+	}
+	if ($chip == 8) {
+		return if ($conf & 0x2a) != 0;
+		return if ($conf2 & 0xf8) != 0;
+		return if $rate > 0x09;
+		return if $mid != 0x5c;		# Winbond
+		return 6 if ($cid & 0xfe) == 0x00; # W83L771W/G
+	}
+	if ($chip == 9) {
+		return if ($conf & 0x1B) != 0;
+		return if $rate > 0x0F;
+		return if $mid != 0x55;		# Texas Instruments
+		return 8 if $cid == 0x11;	# TMP401
+	}
+	if ($chip == 10) {
+		return if ($conf & 0x1B) != 0;
+		return if $rate > 0x0F;
+		return if $mid != 0x55;		# Texas Instruments
+		return 6 if ($addr == 0x4c && $cid == 0x12); # TMP411A
+		return 6 if ($addr == 0x4d && $cid == 0x13); # TMP411B
+		return 6 if ($addr == 0x4e && $cid == 0x10); # TMP411C
+	}
+	if ($chip == 11) {
+		return if ($conf & 0x2a) != 0;
+		return if ($conf2 & 0xf8) != 0;
+		return if $rate > 0x08;
+		return if $mid != 0x5c;		# Winbond
+		return 6 if ($cid & 0xfe) == 0x10; # W83L771AWG/ASG
+	}
+	if ($chip == 12) {
+		return if ($conf & 0x07) != 0;
+		return if $rate > 0x07;
+		return if $mid != 0x4d;		# Maxim
+		return 8 if $cid == 0x09;	# MAX6690
+	}
+	return;
+}
+
+# Chip to detect: 0 = TMP421, 1 = TMP422, 2 = TMP423
+# Registers used:
+#   0xfe: Manufactorer ID
+#   0xff: Device ID
+sub tmp42x_detect()
+{
+	my ($file, $addr, $chip) = @_;
+
+	my $mid = i2c_smbus_read_byte_data($file, 0xfe);
+	my $cid = i2c_smbus_read_byte_data($file, 0xff);
+
+	return if ($mid != 0x55);
+
+	return 6 if ($chip == 0 && $cid == 0x21); # TMP421
+	return 6 if ($chip == 1 && $cid == 0x22); # TMP422
+	return 6 if ($chip == 2 && $cid == 0x23); # TMP423
+
+	return;
+}
+
+# Registers used:
+#   0x3d: Device ID
+#   0x3e: Company ID
+sub amc6821_detect()
+{
+	my ($file, $addr) = @_;
+
+	my $dev_id = i2c_smbus_read_byte_data($file, 0x3d);
+	my $comp_id = i2c_smbus_read_byte_data($file, 0x3e);
+
+	return if ($comp_id != 0x49);		# Texas Instruments
+
+	# Bit 7 of register address is ignored
+	return if i2c_smbus_read_byte_data($file, 0x80 | 0x3d) != $dev_id;
+	return if i2c_smbus_read_byte_data($file, 0x80 | 0x3e) != $comp_id;
+
+	return 6 if ($dev_id == 0x21);		# AMC6821
+
+	return;
+}
+
+# Registers used:
+#   0x03: Configuration (no low nibble, returns the previous low nibble)
+#   0x04: Conversion rate
+#   0xfe: Manufacturer ID
+#   0xff: no register
+sub max6657_detect
+{
+	my ($file, $addr) = @_;
+	my $mid = i2c_smbus_read_byte_data($file, 0xfe, NO_CACHE);
+	my $cid = i2c_smbus_read_byte_data($file, 0xff, NO_CACHE);
+	my $conf = i2c_smbus_read_byte_data($file, 0x03, NO_CACHE);
+
+	return if $mid != 0x4d;		# Maxim
+	return if ($conf & 0x1f) != 0x0d;
+	return if $cid != 0x4d;		# No register, returns previous value
+
+	my $rate = i2c_smbus_read_byte_data($file, 0x04, NO_CACHE);
+	return if $rate > 0x09;
+
+	$cid = i2c_smbus_read_byte_data($file, 0xff, NO_CACHE);
+	$conf = i2c_smbus_read_byte_data($file, 0x03, NO_CACHE);
+	return if ($conf & 0x0f) != $rate;
+	return if $cid != $rate;	# No register, returns previous value
+
+	return 5;
+}
+
+# Chip to detect: 0 = LM95231, 1 = LM95241
+# Registers used:
+#   0x02: Status (3 unused bits)
+#   0x03: Configuration (3 unused bits)
+#   0x06: Remote diode filter control (6 unused bits)
+#   0x30: Remote diode model type select (6 unused bits)
+#   0xfe: Manufacturer ID
+#   0xff: Revision ID
+sub lm95231_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $mid = i2c_smbus_read_byte_data($file, 0xfe);
+	my $cid = i2c_smbus_read_byte_data($file, 0xff);
+
+	return if $mid != 0x01;				# National Semiconductor
+	return if $chip == 0 && $cid != 0xa1;		# LM95231
+	return if $chip == 1 && $cid != 0xa4;		# LM95231
+
+	return if i2c_smbus_read_byte_data($file, 0x02) & 0x70;
+	return if i2c_smbus_read_byte_data($file, 0x03) & 0x89;
+	return if i2c_smbus_read_byte_data($file, 0x06) & 0xfa;
+	return if i2c_smbus_read_byte_data($file, 0x30) & 0xfa;
+
+	return 6;
+}
+
+# Registers used:
+#   0x03: Configuration 1
+#   0x24: Configuration 2
+#   0x3d: Manufacturer ID
+#   0x3e: Device ID
+sub adt7481_detect
+{
+	my ($file, $addr) = @_;
+	my $mid = i2c_smbus_read_byte_data($file, 0x3d);
+	my $cid = i2c_smbus_read_byte_data($file, 0x3e);
+	my $conf1 = i2c_smbus_read_byte_data($file, 0x03);
+	my $conf2 = i2c_smbus_read_byte_data($file, 0x24);
+
+	return if ($conf1 & 0x10) != 0;
+	return if ($conf2 & 0x7f) != 0;
+	return if $mid != 0x41;		# Analog Devices
+	return if $cid != 0x81;		# ADT7481
+
+	return 6;
+}
+
+# Chip to detect: 1 = LM63, 2 = F75363SG, 3 = LM64
+# Registers used:
+#   0xfe: Manufacturer ID
+#   0xff: Chip ID / die revision
+#   0x03: Configuration (two or three unused bits)
+#   0x16: Alert mask (two or three unused bits)
+sub lm63_detect
+{
+	my ($file, $addr, $chip) = @_;
+
+	my $mid = i2c_smbus_read_byte_data($file, 0xfe);
+	my $cid = i2c_smbus_read_byte_data($file, 0xff);
+	my $conf = i2c_smbus_read_byte_data($file, 0x03);
+	my $mask = i2c_smbus_read_byte_data($file, 0x16);
+
+	if ($chip == 1) {
+		return if $mid != 0x01		# National Semiconductor
+		       || $cid != 0x41;		# LM63
+		return if ($conf & 0x18) != 0x00
+		       || ($mask & 0xa4) != 0xa4;
+	} elsif ($chip == 2) {
+		return if $mid != 0x23		# Fintek
+		       || $cid != 0x20;		# F75363SG
+		return if ($conf & 0x1a) != 0x00
+		       || ($mask & 0x84) != 0x00;
+	} elsif ($chip == 3) {
+		return if $mid != 0x01		# National Semiconductor
+		       || $cid != 0x51;		# LM64
+		return if ($conf & 0x18) != 0x00
+		       || ($mask & 0xa4) != 0xa4;
+	}
+
+	return 6;
+}
+
+# Registers used:
+#   0x02, 0x03: Fan support
+#   0x06: Temperature support
+#   0x07, 0x08, 0x09: Fan config
+#   0x0d: Manufacturer ID
+#   0x0e: Chip ID / die revision
+sub adm1029_detect
+{
+	my ($file, $addr) = @_;
+	my $mid = i2c_smbus_read_byte_data($file, 0x0d);
+	my $cid = i2c_smbus_read_byte_data($file, 0x0e);
+	my $cfg;
+
+	return unless $mid == 0x41;		# Analog Devices
+	return unless ($cid & 0xF0) == 0x00;	# ADM1029
+
+	# Extra check on unused bits
+	$cfg = i2c_smbus_read_byte_data($file, 0x02);
+	return unless $cfg == 0x03;
+	$cfg = i2c_smbus_read_byte_data($file, 0x06);
+	return unless ($cfg & 0xF9) == 0x01;
+	foreach my $reg (0x03, 0x07, 0x08, 0x09) {
+		$cfg = i2c_smbus_read_byte_data($file, $reg);
+		return unless ($cfg & 0xFC) == 0x00;
+	}
+
+	return 7;
+}
+
+# Chip to detect: 0 = ADM1030, 1 = ADM1031
+# Registers used:
+#   0x01: Config 2
+#   0x03: Status 2
+#   0x0d, 0x0e, 0x0f: Temperature offsets
+#   0x22: Fan speed config
+#   0x3d: Chip ID
+#   0x3e: Manufacturer ID
+#   0x3f: Die revision
+sub adm1031_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $mid = i2c_smbus_read_byte_data($file, 0x3e);
+	my $cid = i2c_smbus_read_byte_data($file, 0x3d);
+	my $drev = i2c_smbus_read_byte_data($file, 0x3f);
+	my $conf2 = i2c_smbus_read_byte_data($file, 0x01);
+	my $stat2 = i2c_smbus_read_byte_data($file, 0x03);
+	my $fsc = i2c_smbus_read_byte_data($file, 0x22);
+	my $lto = i2c_smbus_read_byte_data($file, 0x0d);
+	my $r1to = i2c_smbus_read_byte_data($file, 0x0e);
+	my $r2to = i2c_smbus_read_byte_data($file, 0x0f);
+	my $confidence = 3;
+
+	if ($chip == 0) {
+		return if $mid != 0x41;		# Analog Devices
+		return if $cid != 0x30;		# ADM1030
+		$confidence++ if ($drev & 0x70) == 0x00;
+		$confidence++ if ($conf2 & 0x4A) == 0x00;
+		$confidence++ if ($stat2 & 0x3F) == 0x00;
+		$confidence++ if ($fsc & 0xF0) == 0x00;
+		$confidence++ if ($lto & 0x70) == 0x00;
+		$confidence++ if ($r1to & 0x70) == 0x00;
+		return $confidence;
+	}
+	if ($chip == 1) {
+		return if $mid != 0x41;		# Analog Devices
+		return if $cid != 0x31;		# ADM1031
+		$confidence++ if ($drev & 0x70) == 0x00;
+		$confidence++ if ($lto & 0x70) == 0x00;
+		$confidence++ if ($r1to & 0x70) == 0x00;
+		$confidence++ if ($r2to & 0x70) == 0x00;
+		return $confidence;
+	}
+}
+
+# Chip to detect: 0 = ADM1033, 1 = ADM1034
+# Registers used:
+#   0x3d: Chip ID
+#   0x3e: Manufacturer ID
+#   0x3f: Die revision
+sub adm1034_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $mid = i2c_smbus_read_byte_data($file, 0x3e);
+	my $cid = i2c_smbus_read_byte_data($file, 0x3d);
+	my $drev = i2c_smbus_read_byte_data($file, 0x3f);
+
+	if ($chip == 0) {
+		return if $mid != 0x41;		# Analog Devices
+		return if $cid != 0x33;		# ADM1033
+		return if ($drev & 0xf8) != 0x00;
+		return 6 if $drev == 0x02;
+		return 4;
+	}
+	if ($chip == 1) {
+		return if $mid != 0x41;		# Analog Devices
+		return if $cid != 0x34;		# ADM1034
+		return if ($drev & 0xf8) != 0x00;
+		return 6 if $drev == 0x02;
+		return 4;
+	}
+}
+
+# Chip to detect: 0 = ADT7467/ADT7468, 1 = ADT7476, 2 = ADT7462, 3 = ADT7466,
+#		  4 = ADT7470
+# Registers used:
+#   0x3d: Chip ID
+#   0x3e: Manufacturer ID
+#   0x3f: Die revision
+sub adt7467_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $mid = i2c_smbus_read_byte_data($file, 0x3e);
+	my $cid = i2c_smbus_read_byte_data($file, 0x3d);
+	my $drev = i2c_smbus_read_byte_data($file, 0x3f);
+
+	return if $mid != 0x41;			# Analog Devices
+
+	if ($chip == 0) {
+		return if $cid != 0x68;		# ADT7467/ADT7468
+		return if ($drev & 0xf0) != 0x70;
+		return 7 if $drev == 0x71 || $drev == 0x72;
+		return 5;
+	}
+	if ($chip == 1) {
+		return if $cid != 0x76;		# ADT7476
+		return if ($drev & 0xf0) != 0x60;
+		return 7 if $drev >= 0x69 && $drev <= 0x6b;
+		return 5;
+	}
+	if ($chip == 2) {
+		return if $cid != 0x62;		# ADT7462
+		return if ($drev & 0xf0) != 0x00;
+		return 7 if $drev == 0x04;
+		return 5;
+	}
+	if ($chip == 3) {
+		return if $cid != 0x66;		# ADT7466
+		return if ($drev & 0xf0) != 0x00;
+		return 7 if $drev == 0x02;
+		return 5;
+	}
+	if ($chip == 4) {
+		return if $cid != 0x70;		# ADT7470
+		return if ($drev & 0xf0) != 0x00;
+		return 7 if $drev == 0x00;
+		return 5;
+	}
+}
+
+# Chip to detect: 0 = ADT7473, 1 = ADT7475
+# Registers used:
+#   0x3d: Chip ID
+#   0x3e: Manufacturer ID
+sub adt7473_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $mid = i2c_smbus_read_byte_data($file, 0x3e);
+	my $cid = i2c_smbus_read_byte_data($file, 0x3d);
+
+	return if $mid != 0x41;			# Analog Devices
+
+	return if $chip == 0 && $cid != 0x73;	# ADT7473
+	return if $chip == 1 && $cid != 0x75;	# ADT7475
+	return 5;
+}
+
+# Registers used:
+#   0x3e: Manufacturer ID
+#   0x3f: Chip ID
+sub adt7490_detect
+{
+	my ($file, $addr) = @_;
+	my $mid = i2c_smbus_read_byte_data($file, 0x3e);
+	my $cid = i2c_smbus_read_byte_data($file, 0x3f);
+
+	return if $mid != 0x41;			# Analog Devices
+	return if ($cid & 0xfc) != 0x6c;	# ADT7490
+	return 5;
+}
+
+# Registers used:
+#   0x02: Status
+#   0x0a: Thyst
+#   0x0b: ID
+# We also rely on the fact that only the 5 LSB of the address pointer
+# are considered, so registers cycle over 32 byte boundaries.
+sub adt7410_detect
+{
+	my ($file, $addr) = @_;
+	my $status = i2c_smbus_read_byte_data($file, 0x02);
+	my $thyst = i2c_smbus_read_byte_data($file, 0x0a);
+	my $id = i2c_smbus_read_byte_data($file, 0x0b);
+
+	# Unused bits
+	return if ($status & 0x0f);
+	return if ($thyst & 0xf0);
+
+	# ID register
+	return if $id != 0xcb;
+
+	# Cycling registers
+	for (my $i = 2; $i < 16; $i *= 2) {
+		return if i2c_smbus_read_byte_data($file, 0x0a + $i * 16) != $thyst;
+		return if i2c_smbus_read_byte_data($file, 0x0b + $i * 16) != $id;
+	}
+
+	# Non-existent registers (other devices tend to have ID registers there)
+	return if i2c_smbus_read_byte_data($file, 0x03e) != 0;
+	return if i2c_smbus_read_byte_data($file, 0x03f) != 0;
+	return if i2c_smbus_read_byte_data($file, 0x0fe) != 0;
+	return if i2c_smbus_read_byte_data($file, 0x0ff) != 0;
+
+	return 3;
+}
+
+# Registers used:
+#   0x4d: Device ID
+#   0x4e: Manufacturer ID
+#   0x4e: Silicon revision
+sub adt7411_detect
+{
+	my ($file, $addr) = @_;
+	my $dev_id = i2c_smbus_read_byte_data($file, 0x4d);
+	my $man_id = i2c_smbus_read_byte_data($file, 0x4e);
+	my $revision = i2c_smbus_read_byte_data($file, 0x4f);
+
+	return if $man_id != 0x41;		# Analog Devices
+	return if $dev_id != 0x02;		# ADT7411
+	# The datasheet suggests that the version is in the high nibble, but
+	# a dump from a real ADT7411 chip shows that it is in the low nibble.
+	return if ($revision & 0x0f) != 0x04;	# ADT7411
+	return 5;
+}
+
+# Chip to detect: 0 = aSC7512, 1 = aSC7611, 2 = aSC7621
+# Registers used:
+#   0x3e: Manufacturer ID
+#   0x3f: Version
+sub andigilog_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $mid = i2c_smbus_read_byte_data($file, 0x3e);
+	my $cid = i2c_smbus_read_byte_data($file, 0x3f);
+
+	return if $mid != 0x61;		# Andigilog
+
+	return if $chip == 0 && $cid != 0x62;
+	return if $chip == 1 && $cid != 0x69;
+	return if $chip == 2 && ($cid != 0x6C && $cid != 0x6D);
+	return 5;
+}
+
+# Registers used:
+#   0xfe: Manufacturer ID
+#   0xff: Die Code
+sub andigilog_aSC7511_detect
+{
+	my ($file, $addr) = @_;
+	my $mid = i2c_smbus_read_byte_data($file, 0xfe);
+	my $die = i2c_smbus_read_byte_data($file, 0xff);
+
+	return if $mid != 0x61;		# Andigilog
+	return if $die != 0x00;
+	return 3;
+}
+
+# Chip to detect: 0 = LM85, 1 = LM96000, 2 = ADM1027, 3 = ADT7463,
+#		  4 = EMC6D100/101, 5 = EMC6D102, 6 = EMC6D103,
+#		  7 = WPCD377I (no sensors), 8 = EMC6D103S
+# Registers used:
+#   0x3e: Vendor register
+#   0x3d: Device ID register (Analog Devices only)
+#   0x3f: Version/Stepping register
+sub lm85_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $vendor = i2c_smbus_read_byte_data($file, 0x3e);
+	my $verstep = i2c_smbus_read_byte_data($file, 0x3f);
+
+	if ($chip == 0) {
+		return if $vendor != 0x01;	# National Semiconductor
+		return if $verstep != 0x60	# LM85 C
+		       && $verstep != 0x62;	# LM85 B
+	} elsif ($chip == 1 || $chip == 7) {
+		return if $vendor != 0x01;	# National Semiconductor
+		return if $verstep != 0x68	# LM96000
+		       && $verstep != 0x69;	# LM96000
+	} elsif ($chip == 2) {
+		return if $vendor != 0x41;	# Analog Devices
+		return if $verstep != 0x60;	# ADM1027
+	} elsif ($chip == 3) {
+		return if $vendor != 0x41;	# Analog Devices
+		return if $verstep != 0x62	# ADT7463
+		       && $verstep != 0x6a;	# ADT7463 C
+	} elsif ($chip == 4) {
+		return if $vendor != 0x5c;	# SMSC
+		return if $verstep != 0x60	# EMC6D100/101 A0
+		       && $verstep != 0x61;	# EMC6D100/101 A1
+	} elsif ($chip == 5) {
+		return if $vendor != 0x5c;	# SMSC
+		return if $verstep != 0x65;	# EMC6D102
+	} elsif ($chip == 6) {
+		return if $vendor != 0x5c;	# SMSC
+		return if $verstep != 0x68	# EMC6D103 A0
+		       && $verstep != 0x69;	# EMC6D103 A1
+	} elsif ($chip == 8) {
+		return if $vendor != 0x5c;	# SMSC
+		return if $verstep != 0x6a;	# EMC6D103S
+	}
+
+	if ($vendor == 0x41) {			# Analog Devices
+		return if i2c_smbus_read_byte_data($file, 0x3d) != 0x27;
+		return 8;
+	}
+
+	if ($chip == 1 || $chip == 7) {
+		# Differenciate between real LM96000 and Winbond WPCD377I.
+		# The latter emulate the former except that it has no
+		# hardware monitoring function so the readings are always
+		# 0.
+		my ($in_temp, $fan, $i);
+		
+		for ($i = 0; $i < 8; $i++) {
+			$in_temp = i2c_smbus_read_byte_data($file, 0x20 + $i);
+			$fan = i2c_smbus_read_byte_data($file, 0x28 + $i);
+			if ($in_temp != 0x00 or $fan != 0xff) {
+				return 7 if $chip == 1;
+				return;
+			}
+		}
+		return 7 if $chip == 7;
+		return;
+	}
+
+	return 7;
+}
+
+# Chip to detect: 0 = LM87, 1 = ADM1024
+# Registers used:
+#   0x3e: Company ID
+#   0x3f: Revision
+#   0x40: Configuration
+sub lm87_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $cid = i2c_smbus_read_byte_data($file, 0x3e);
+	my $rev = i2c_smbus_read_byte_data($file, 0x3f);
+
+	if ($chip == 0) {
+		return if $cid != 0x02;		# National Semiconductor
+		return if ($rev & 0xfc) != 0x04; # LM87
+	}
+	if ($chip == 1) {
+		return if $cid != 0x41;		# Analog Devices
+		return if ($rev & 0xf0) != 0x10; # ADM1024
+	}
+
+	my $cfg = i2c_smbus_read_byte_data($file, 0x40);
+	return if ($cfg & 0x80) != 0x00;
+
+	return 7;
+}
+
+# Chip to detect: 0 = W83781D, 1 = W83782D, 2 = W83783S, 3 = W83627HF,
+#		  4 = AS99127F (rev.1), 5 = AS99127F (rev.2), 6 = ASB100,
+#		  7 = W83791D, 8 = W83792D, 9 = W83627EHF,
+#		  10 = W83627DHG/W83667HG/W83677HG
+# Registers used:
+#   0x48: Full I2C Address
+#   0x4a: I2C addresses of emulated LM75 chips
+#   0x4e: Vendor ID byte selection, and bank selection
+#   0x4f: Vendor ID
+#   0x58: Device ID (only when in bank 0)
+# Note: Fails if the W8378xD is not in bank 0!
+# Note: Asus chips do not have their I2C address at register 0x48?
+#       AS99127F rev.1 and ASB100 have 0x00, confirmation wanted for
+#       AS99127F rev.2.
+sub w83781d_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my ($reg1, $reg2, @res);
+
+	return unless (i2c_smbus_read_byte_data($file, 0x48) == $addr)
+		   or ($chip >= 4 && $chip <= 6);
+
+	$reg1 = i2c_smbus_read_byte_data($file, 0x4e);
+	$reg2 = i2c_smbus_read_byte_data($file, 0x4f);
+	if ($chip == 4) {		# Asus AS99127F (rev.1)
+		return unless (($reg1 & 0x80) == 0x00 and $reg2 == 0xc3) or
+			      (($reg1 & 0x80) == 0x80 and $reg2 == 0x12);
+	} elsif ($chip == 6) {		# Asus ASB100
+		return unless (($reg1 & 0x80) == 0x00 and $reg2 == 0x94) or
+			      (($reg1 & 0x80) == 0x80 and $reg2 == 0x06);
+	} else {			# Winbond and Asus AS99127F (rev.2)
+		return unless (($reg1 & 0x80) == 0x00 and $reg2 == 0xa3) or
+			      (($reg1 & 0x80) == 0x80 and $reg2 == 0x5c);
+	}
+
+	return unless ($reg1 & 0x07) == 0x00;
+
+	$reg1 = i2c_smbus_read_byte_data($file, 0x58);
+	return if $chip == 0 and ($reg1 != 0x10 && $reg1 != 0x11);
+	return if $chip == 1 and $reg1 != 0x30;
+	return if $chip == 2 and $reg1 != 0x40;
+	return if $chip == 3 and $reg1 != 0x21;
+	return if $chip == 4 and $reg1 != 0x31;
+	return if $chip == 5 and $reg1 != 0x31;
+	return if $chip == 6 and $reg1 != 0x31;
+	return if $chip == 7 and $reg1 != 0x71;
+	return if $chip == 8 and $reg1 != 0x7a;
+	return if $chip == 9 and ($reg1 != 0x88 && $reg1 != 0xa1);
+	return if $chip == 10 and $reg1 != 0xc1;
+	# Default address is 0x2d
+	@res = ($addr != 0x2d) ? (7) : (8);
+	return @res if $chip >= 9; # No subclients
+
+	$reg1 = i2c_smbus_read_byte_data($file, 0x4a);
+	push @res, ($reg1 & 0x07) + 0x48 unless $reg1 & 0x08;
+	push @res, (($reg1 & 0x70) >> 4) + 0x48
+		unless ($reg1 & 0x80 or $chip == 2);
+	return @res;
+}
+
+# Registers used:
+#   0x0b: Full I2C Address
+#   0x0c: I2C addresses of emulated LM75 chips
+#   0x00: Vendor ID byte selection, and bank selection(Bank 0, 1, 2)
+#   0x0d: Vendor ID(Bank 0, 1, 2)
+#   0x0e: Device ID(Bank 0, 1, 2)
+sub w83793_detect
+{
+	my ($file, $addr) = @_;
+	my ($bank, $reg, @res);
+
+	$bank = i2c_smbus_read_byte_data($file, 0x00);
+	$reg = i2c_smbus_read_byte_data($file, 0x0d);
+
+	return unless (($bank & 0x80) == 0x00 and $reg == 0xa3) or
+		      (($bank & 0x80) == 0x80 and $reg == 0x5c);
+
+	$reg = i2c_smbus_read_byte_data($file, 0x0e);
+	return if $reg != 0x7b;
+
+	# If bank 0 is selected, we can do more checks
+	return 6 unless ($bank & 0x07) == 0;
+	$reg = i2c_smbus_read_byte_data($file, 0x0b);
+	return unless ($reg == ($addr << 1));
+
+	$reg = i2c_smbus_read_byte_data($file, 0x0c);
+	@res = (8);
+	push @res, ($reg & 0x07) + 0x48 unless $reg & 0x08;
+	push @res, (($reg & 0x70) >> 4) + 0x48 unless $reg & 0x80;
+	return @res;
+}
+
+# Registers used:
+#   0xfc: Full I2C Address
+#   0x00: Vendor ID byte selection, and bank selection(Bank 0, 1, 2)
+#   0xfd: Vendor ID(Bank 0, 1, 2)
+#   0xfe: Device ID(Bank 0, 1, 2)
+sub w83795_detect
+{
+	my ($bank, $reg);
+	my ($file, $addr) = @_;
+
+	$bank = i2c_smbus_read_byte_data($file, 0x00);
+	$reg = i2c_smbus_read_byte_data($file, 0xfd);
+
+	return unless (($bank & 0x80) == 0x00 and $reg == 0xa3) or
+		      (($bank & 0x80) == 0x80 and $reg == 0x5c);
+
+	$reg = i2c_smbus_read_byte_data($file, 0xfe);
+	return if $reg != 0x79;
+
+	# If bank 0 is selected, we can do more checks
+	return 6 unless ($bank & 0x07) == 0;
+	$reg = i2c_smbus_read_byte_data($file, 0xfc) & 0x7f;
+	return unless ($reg == $addr);
+
+	return 8;
+}
+
+# Registers used:
+#   0x48: Full I2C Address
+#   0x4e: Vendor ID byte selection
+#   0x4f: Vendor ID
+#   0x58: Device ID
+# Note that the datasheet was useless and this detection routine
+# is based on dumps we received from users. Also, the W83781SD is *NOT*
+# a hardware monitoring chip as far as we know, but we still want to
+# detect it so that people won't keep reporting it as an unknown chip
+# we should investigate about.
+sub w83791sd_detect
+{
+	my ($file, $addr) = @_;
+	my ($reg1, $reg2);
+
+	return unless (i2c_smbus_read_byte_data($file, 0x48) == $addr);
+
+	$reg1 = i2c_smbus_read_byte_data($file, 0x4e);
+	$reg2 = i2c_smbus_read_byte_data($file, 0x4f);
+	return unless (!($reg1 & 0x80) && $reg2 == 0xa3)
+		   || (($reg1 & 0x80) && $reg2 == 0x5c);
+
+	$reg1 = i2c_smbus_read_byte_data($file, 0x58);
+	return unless $reg1 == 0x72;
+
+	return 3;
+}
+
+# Registers used:
+#   0x4e: Vendor ID high byte
+#   0x4f: Vendor ID low byte
+#   0x58: Device ID
+# Note: The values were given by Alex van Kaam, we don't have datasheets
+#       to confirm.
+sub mozart_detect
+{
+	my ($file, $addr) = @_;
+	my ($vid, $dev);
+
+	$vid = (i2c_smbus_read_byte_data($file, 0x4e) << 8)
+	     + i2c_smbus_read_byte_data($file, 0x4f);
+	$dev = i2c_smbus_read_byte_data($file, 0x58);
+
+	return unless ($dev == 0x56 && $vid == 0x9436)	# ASM58
+		   || ($dev == 0x56 && $vid == 0x9406)	# AS2K129R
+		   || ($dev == 0x10 && $vid == 0x5ca3);
+
+	return 5;
+}
+
+# Chip to detect: 0 = GL518SM, 1 = GL520SM
+# Registers used:
+#   0x00: Device ID
+#   0x01: Revision ID
+#   0x03: Configuration
+sub gl518sm_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $reg;
+
+	$reg = i2c_smbus_read_byte_data($file, 0x00);
+	return if $chip == 0 && $reg != 0x80;
+	return if $chip == 1 && $reg != 0x20;
+
+	return unless (i2c_smbus_read_byte_data($file, 0x03) & 0x80) == 0x00;
+	$reg = i2c_smbus_read_byte_data($file, 0x01);
+	return unless $reg == 0x00 or $reg == 0x80;
+	return 6;
+}
+
+# Registers used:
+#   0x00: Device ID
+#   0x03: Configuration
+# Mediocre detection
+sub gl525sm_detect
+{
+	my ($file, $addr) = @_;
+	return unless i2c_smbus_read_byte_data($file, 0x00) == 0x25;
+	return unless (i2c_smbus_read_byte_data($file, 0x03) & 0x80) == 0x00;
+	return 5;
+}
+
+# Chip to detect: 0 = ADM9240, 1 = DS1780, 2 = LM81
+# Registers used:
+#   0x3e: Company ID
+#   0x40: Configuration
+#   0x48: Full I2C Address
+sub adm9240_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $reg;
+	$reg = i2c_smbus_read_byte_data($file, 0x3e);
+	return unless ($chip == 0 and $reg == 0x23) or
+		      ($chip == 1 and $reg == 0xda) or
+		      ($chip == 2 and $reg == 0x01);
+	return unless (i2c_smbus_read_byte_data($file, 0x40) & 0x80) == 0x00;
+	return unless i2c_smbus_read_byte_data($file, 0x48) == $addr;
+
+	return 7;
+}
+
+# Chip to detect: 0 = ADM1022, 1 = THMC50, 2 = ADM1028, 3 = THMC51
+# Registers used:
+#   0x3e: Company ID
+#   0x3f: Revision
+#   0x40: Configuration
+sub adm1022_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $reg;
+	$reg = i2c_smbus_read_byte_data($file, 0x3e);
+	return unless ($chip == 0 and $reg == 0x41) or
+		      ($chip == 1 and $reg == 0x49) or
+		      ($chip == 2 and $reg == 0x41) or
+		      ($chip == 3 and $reg == 0x49);
+	$reg = i2c_smbus_read_byte_data($file, 0x40);
+	return if ($reg & 0x10);			# Soft Reset always reads 0
+	return if ($chip != 0 and ($reg & 0x80));	# Reserved on THMC50 and ADM1028
+	$reg = i2c_smbus_read_byte_data($file, 0x3f) & 0xf0;
+	return unless ($chip == 0 and $reg == 0xc0) or
+		      ($chip == 1 and $reg == 0xc0) or
+		      ($chip == 2 and $reg == 0xd0) or
+		      ($chip == 3 and $reg == 0xd0);
+	return 8;
+}
+
+# Chip to detect: 0 = ADM1025, 1 = NE1619
+# Registers used:
+#   0x3e: Company ID
+#   0x3f: Revision
+#   0x40: Configuration
+#   0x41: Status 1
+#   0x42: Status 2
+sub adm1025_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $reg;
+
+	$reg = i2c_smbus_read_byte_data($file, 0x3e);
+	return if ($chip == 0) and ($reg != 0x41);
+	return if ($chip == 1) and ($reg != 0xA1);
+
+	return unless (i2c_smbus_read_byte_data($file, 0x40) & 0x80) == 0x00;
+	return unless (i2c_smbus_read_byte_data($file, 0x41) & 0xC0) == 0x00;
+	return unless (i2c_smbus_read_byte_data($file, 0x42) & 0xBC) == 0x00;
+	return unless (i2c_smbus_read_byte_data($file, 0x3f) & 0xf0) == 0x20;
+
+	return 8;
+}
+
+# Registers used:
+#   0x16: Company ID
+#   0x17: Revision
+sub adm1026_detect
+{
+	my ($file, $addr) = @_;
+	my $reg;
+	$reg = i2c_smbus_read_byte_data($file, 0x16);
+	return unless ($reg == 0x41);
+	return unless (i2c_smbus_read_byte_data($file, 0x17) & 0xf0) == 0x40;
+	return 8;
+}
+
+# Chip to detect: 0 = ADM1021, 1 = ADM1021A/ADM1023, 2 = MAX1617, 3 = MAX1617A,
+#		  4 = THMC10, 5 = LM84, 6 = GL523, 7 = MC1066
+# Registers used:
+#   0x04: Company ID (LM84 only)
+#   0xfe: Company ID (all but LM84 and MAX1617)
+#   0xff: Revision (ADM1021, ADM1021A/ADM1023 and MAX1617A)
+#   0x02: Status
+#   0x03: Configuration
+#   0x04: Conversion rate
+#   0x00-0x01, 0x05-0x08: Temperatures (MAX1617 and LM84)
+# Note: Especially the MAX1617 has very bad detection; we give it a low
+# confidence value.
+sub adm1021_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $man_id = i2c_smbus_read_byte_data($file, 0xfe);
+	my $rev = i2c_smbus_read_byte_data($file, 0xff);
+	my $conf = i2c_smbus_read_byte_data($file, 0x03);
+	my $status = i2c_smbus_read_byte_data($file, 0x02);
+	my $convrate = i2c_smbus_read_byte_data($file, 0x04);
+
+	# Check manufacturer IDs and product revisions when available
+	return if $chip == 0 and $man_id != 0x41 || ($rev & 0xf0) != 0x00;
+	return if $chip == 1 and $man_id != 0x41 || ($rev & 0xf0) != 0x30;
+	return if $chip == 3 and $man_id != 0x4d || $rev != 0x01;
+	return if $chip == 4 and $man_id != 0x49;
+	return if $chip == 5 and $convrate != 0x00;
+	return if $chip == 6 and $man_id != 0x23;
+	return if $chip == 7 and $man_id != 0x54;
+
+	# Check unused bits
+	if ($chip == 5) {	# LM84
+		return if ($status & 0xab) != 0;
+		return if ($conf & 0x7f) != 0;
+	} else {
+		return if ($status & 0x03) != 0;
+		return if ($conf & 0x3f) != 0;
+		return if ($convrate & 0xf8) != 0;
+	}
+
+	# Extra checks for MAX1617 and LM84, since those are often misdetected.
+	# We verify several assertions (6 for the MAX1617, 4 for the LM84) and
+	# discard the chip if any fail. Note that these checks are not done
+	# by the adm1021 driver.
+	if ($chip == 2 || $chip == 5) {
+		my $lte = i2c_smbus_read_byte_data($file, 0x00);
+		my $rte = i2c_smbus_read_byte_data($file, 0x01);
+		my $lhi = i2c_smbus_read_byte_data($file, 0x05);
+		my $rhi = i2c_smbus_read_byte_data($file, 0x07);
+		my $llo = i2c_smbus_read_byte_data($file, 0x06);
+		my $rlo = i2c_smbus_read_byte_data($file, 0x08);
+
+		# If all registers hold the same value, it has to be a misdetection
+		return if $lte == $rte and $lte == $lhi and $lte == $rhi
+		      and $lte == $llo and $lte == $rlo;
+
+		# Negative temperatures
+		return if ($lte & 0x80) or ($rte & 0x80);
+		# Negative high limits
+		return if ($lhi & 0x80) or ($rhi & 0x80);
+		# Low limits over high limits
+		if ($chip == 2) {
+			$llo -= 256 if ($llo & 0x80);
+			$rlo -= 256 if ($rlo & 0x80);
+			return if ($llo > $lhi) or ($rlo > $rhi);
+		}
+		return 3;
+	}
+
+	return ($chip <= 3) ? 7 : 5;
+}
+
+# Chip to detect: 0 = MAX1668, 1 = MAX1805, 2 = MAX1989
+# Registers used:
+#   0xfe: Company ID
+#   0xff: Device ID
+sub max1668_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $man_id = i2c_smbus_read_byte_data($file, 0xfe);
+	my $dev_id = i2c_smbus_read_byte_data($file, 0xff);
+
+	return if $man_id != 0x4d;
+	return if $chip == 0 and $dev_id != 0x03;
+	return if $chip == 1 and $dev_id != 0x05;
+	return if $chip == 2 and $dev_id != 0x0b;
+
+	return 7;
+}
+
+# Chip to detect: 0 = MAX1619, 1 = MAX1618
+# Registers used:
+#   0xfe: Company ID
+#   0xff: Device ID
+#   0x02: Status
+#   0x03: Configuration
+#   0x04: Conversion rate
+sub max1619_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $man_id = i2c_smbus_read_byte_data($file, 0xfe);
+	my $dev_id = i2c_smbus_read_byte_data($file, 0xff);
+	my $conf = i2c_smbus_read_byte_data($file, 0x03);
+	my $status = i2c_smbus_read_byte_data($file, 0x02);
+	my $convrate = i2c_smbus_read_byte_data($file, 0x04);
+
+	return if $man_id != 0x4D;	# Maxim
+
+	if ($chip == 0) {		# MAX1619
+		return if $dev_id != 0x04
+		       or ($conf & 0x03)
+		       or ($status & 0x61)
+		       or $convrate >= 8;
+	}
+	if ($chip == 1) {		# MAX1618
+		return if $dev_id != 0x02
+		       or ($conf & 0x07)
+		       or ($status & 0x63);
+	}
+
+	return 7;
+}
+
+# Registers used:
+#   0x28: User ID
+#   0x29: User ID2
+sub ite_overclock_detect
+{
+	my ($file, $addr) = @_;
+
+	my $uid1 = i2c_smbus_read_byte_data($file, 0x28);
+	my $uid2 = i2c_smbus_read_byte_data($file, 0x29);
+	return if $uid1 != 0x83 || $uid2 != 0x12;
+
+	return 6;
+}
+
+# Registers used:
+#   0x00: Configuration
+#   0x48: Full I2C Address
+#   0x58: Mfr ID
+#   0x5b: Device ID
+sub it8712_i2c_detect
+{
+	my ($file, $addr) = @_;
+	my $reg;
+	return unless i2c_smbus_read_byte_data($file, 0x48) == $addr;
+	return unless (i2c_smbus_read_byte_data($file, 0x00) & 0x90) == 0x10;
+	return unless i2c_smbus_read_byte_data($file, 0x58) == 0x90;
+	return if i2c_smbus_read_byte_data($file, 0x5b) != 0x12;
+	return 7 + ($addr == 0x2d);
+}
+
+# Registers used:
+#   0-63: SPD Data and Checksum (up to DDR2)
+#   0-127: SPD data and CRC (DDR3)
+sub eeprom_detect
+{
+	my ($file, $addr) = @_;
+	my $device_type = i2c_smbus_read_byte_data($file, 2);
+	my $checksum = 0;
+
+	# Check the checksum or CRC16 for validity
+	if ($device_type >= 1 and $device_type <= 8) {
+		for (my $i = 0; $i < 63; $i++) {
+			$checksum += i2c_smbus_read_byte_data($file, $i);
+		}
+		$checksum &= 0xff;
+
+		return 8 if $checksum == i2c_smbus_read_byte_data($file, 63);
+	} elsif ($device_type >= 9 && $device_type <= 11) {
+		# see JEDEC 21-C 4.1.2.11 2.4
+		my $crc_coverage = i2c_smbus_read_byte_data($file, 0);
+		$crc_coverage = ($crc_coverage & 0x80) ? 117 : 126;
+		for (my $i = 0; $i < $crc_coverage; $i++) {
+			$checksum ^= i2c_smbus_read_byte_data($file, $i) << 8;
+			for (my $bit = 0; $bit < 8; $bit++) {
+				if ($checksum & 0x8000) {
+					$checksum = ($checksum << 1) ^ 0x1021;
+				} else {
+					$checksum <<= 1;
+				}
+			}
+		}
+		$checksum &= 0xffff;
+
+		return 8 if ($checksum & 0xff) == i2c_smbus_read_byte_data($file, 126) and
+			    ($checksum >> 8) == i2c_smbus_read_byte_data($file, 127);
+
+		# note: if bit 7 of byte 32 is set, a jc42 sensor is at $addr-0x38
+	}
+
+	return;
+}
+
+# Registers used:
+#   0x00..0x07: DDC signature
+sub ddcmonitor_detect
+{
+	my ($file, $addr) = @_;
+
+	return unless
+		i2c_smbus_read_byte_data($file, 0x00) == 0x00 and
+		i2c_smbus_read_byte_data($file, 0x01) == 0xFF and
+		i2c_smbus_read_byte_data($file, 0x02) == 0xFF and
+		i2c_smbus_read_byte_data($file, 0x03) == 0xFF and
+		i2c_smbus_read_byte_data($file, 0x04) == 0xFF and
+		i2c_smbus_read_byte_data($file, 0x05) == 0xFF and
+		i2c_smbus_read_byte_data($file, 0x06) == 0xFF and
+		i2c_smbus_read_byte_data($file, 0x07) == 0x00;
+
+	return 8;
+}
+
+# Chip to detect: 0 = Poseidon I, 1 = Poseidon II, 2 = Scylla,
+#		  3 = Hermes, 4 = Heimdal, 5 = Heracles
+# Registers used:
+#   0x00-0x02: Identification (3 capital ASCII letters)
+sub fsc_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $id;
+
+	$id = chr(i2c_smbus_read_byte_data($file, 0x00))
+	    . chr(i2c_smbus_read_byte_data($file, 0x01))
+	    . chr(i2c_smbus_read_byte_data($file, 0x02));
+
+	return if $chip == 0 and $id ne 'PEG';	# Pegasus? aka Poseidon I
+	return if $chip == 1 and $id ne 'POS';	# Poseidon II
+	return if $chip == 2 and $id ne 'SCY';	# Scylla
+	return if $chip == 3 and $id ne 'HER';	# Hermes
+	return if $chip == 4 and $id ne 'HMD';	# Heimdal
+	return if $chip == 5 and $id ne 'HRC';	# Heracles
+	return if $chip == 6 and $id ne 'HDS';	# Hades
+	return if $chip == 7 and $id ne 'SYL';	# Syleus
+
+	return 8;
+}
+
+# Chip to detect: 0 = LM93, 1 = LM94
+# Registers used:
+#   0x3E: Manufacturer ID
+#   0x3F: Version/Stepping
+sub lm93_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my ($man_id, $dev_id);
+
+	$man_id = i2c_smbus_read_byte_data($file, 0x3E);
+	$dev_id = i2c_smbus_read_byte_data($file, 0x3F);
+	
+	if ($chip == 0) {
+		return unless $man_id == 0x01	# National Semiconductor
+			  and $dev_id == 0x73;	# LM93
+	}
+	if ($chip == 1) {
+		return unless $man_id == 0x01	# National Semiconductor
+			  and $dev_id >= 0x79
+			  and $dev_id <= 0x7A;	# LM94
+	}
+	return 5;
+}
+
+# Registers used:
+#   0x3F: Revision ID
+#   0x48: Address
+#   0x4A, 0x4B, 0x4F, 0x57, 0x58: Reserved bits.
+# We do not use 0x49's reserved bits on purpose. The register is named
+# "VID4/Device ID" so it is doubtful bits 7-1 are really unused.
+sub m5879_detect
+{
+	my ($file, $addr) = @_;
+
+	return unless i2c_smbus_read_byte_data($file, 0x3F) == 0x01;
+	return unless i2c_smbus_read_byte_data($file, 0x48) == $addr;
+
+	return unless (i2c_smbus_read_byte_data($file, 0x4A) & 0x06) == 0
+		  and (i2c_smbus_read_byte_data($file, 0x4B) & 0xFC) == 0
+		  and (i2c_smbus_read_byte_data($file, 0x4F) & 0xFC) == 0
+		  and (i2c_smbus_read_byte_data($file, 0x57) & 0xFE) == 0
+		  and (i2c_smbus_read_byte_data($file, 0x58) & 0xEF) == 0;
+
+	return 7;
+}
+
+# Registers used:
+#   0x3E: Manufacturer ID
+#   0x3F: Version/Stepping
+#   0x47: VID (3 reserved bits)
+#   0x49: VID4 (7 reserved bits)
+sub smsc47m192_detect
+{
+	my ($file, $addr) = @_;
+	return unless i2c_smbus_read_byte_data($file, 0x3E) == 0x55
+		  and (i2c_smbus_read_byte_data($file, 0x3F) & 0xF0) == 0x20
+		  and (i2c_smbus_read_byte_data($file, 0x47) & 0x70) == 0x00
+		  and (i2c_smbus_read_byte_data($file, 0x49) & 0xFE) == 0x80;
+	return ($addr == 0x2d ? 6 : 5);
+}
+
+# Chip to detect: 1 = DME1737, 2 = SCH5027
+# Registers used:
+#   0x3E: Manufacturer ID
+#   0x3F: Version/Stepping
+#   0x73: Read-only test register (4 test bits)
+#   0x8A: Read-only test register (7 test bits)
+#   0xBA: Read-only test register (8 test bits)
+sub dme1737_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $vendor = i2c_smbus_read_byte_data($file, 0x3E);
+	my $verstep = i2c_smbus_read_byte_data($file, 0x3F);
+
+	return unless $vendor == 0x5C; # SMSC
+
+	if ($chip == 1) { # DME1737
+		return unless ($verstep & 0xF8) == 0x88 and
+			      (i2c_smbus_read_byte_data($file, 0x73) & 0x0F) == 0x09 and
+			      (i2c_smbus_read_byte_data($file, 0x8A) & 0x7F) == 0x4D;
+	} elsif ($chip == 2) { # SCH5027
+		return unless $verstep >= 0x69 and $verstep <= 0x6F and
+			      i2c_smbus_read_byte_data($file, 0xBA) == 0x0F;
+	}
+
+	return ($addr == 0x2e ? 6 : 5);
+}
+
+# Chip to detect: 1 = F75111R/RG/N, 2 = F75121R/F75122R/RG, 3 = F75373S/SG,
+#		  4 = F75375S/SP, 5 = F75387SG/RG, 6 = F75383M/S/F75384M/S,
+#		  7 = custom power control IC
+# Registers used:
+#   0x5A-0x5B: Chip ID
+#   0x5D-0x5E: Vendor ID
+sub fintek_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $chipid = (i2c_smbus_read_byte_data($file, 0x5A) << 8)
+		   | i2c_smbus_read_byte_data($file, 0x5B);
+	my $vendid = (i2c_smbus_read_byte_data($file, 0x5D) << 8)
+		   | i2c_smbus_read_byte_data($file, 0x5E);
+
+	return unless $vendid == 0x1934; # Fintek ID
+
+	if ($chip == 1) { # F75111R/RG/N
+		return unless $chipid == 0x0300;
+	} elsif ($chip == 2) { # F75121R/F75122R/RG
+		return unless $chipid == 0x0301;
+	} elsif ($chip == 3) { # F75373S/SG
+		return unless $chipid == 0x0204;
+	} elsif ($chip == 4) { # F75375S/SP
+		return unless $chipid == 0x0306;
+	} elsif ($chip == 5) { # F75387SG/RG
+		return unless $chipid == 0x0410;
+	} elsif ($chip == 6) { # F75383M/S/F75384M/S
+		# The datasheet has 0x0303, but Fintek say 0x0413 is also
+		# possible
+		return unless $chipid == 0x0303 || $chipid == 0x0413;
+	} elsif ($chip == 7) { # custom power control IC
+		return unless $chipid == 0x0302;
+	}
+
+	return 7;
+}
+
+# Chips to detect: 0 = EMC1023, 1 = EMC1043, 2 = EMC1053, 3 = EMC1063
+# Registers used:
+#   0xed: Device ID register
+#   0xfe: Vendor ID register
+#   0xff: Revision register
+sub emc1023_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $dev_id = i2c_smbus_read_byte_data($file, 0xed);
+	my $man_id = i2c_smbus_read_byte_data($file, 0xfe);
+	my $rev = i2c_smbus_read_byte_data($file, 0xff);
+
+	return unless $man_id == 0x5d;  # SMSC
+	return unless $rev <= 1;
+
+	if ($chip == 0) {
+		return if ($addr == 0x4c) && ($dev_id != 0x04);	# EMC1023-1
+		return if ($addr == 0x4d) && ($dev_id != 0x05);	# EMC1023-2
+		return if ($addr == 0x48) && ($dev_id != 0x06);	# EMC1023-3
+		return if ($addr == 0x49) && ($dev_id != 0x07);	# EMC1023-4
+	} elsif ($chip == 1) {
+		if ($addr == 0x4c) {				# EMC1043-1, EMC1043-5
+			return unless ($dev_id == 0x0c) || ($dev_id == 0x2c);
+		}
+		return if ($addr == 0x4d) && ($dev_id != 0x0d);	# EMC1043-2
+		return if ($addr == 0x48) && ($dev_id != 0x0e);	# EMC1043-3
+		return if ($addr == 0x49) && ($dev_id != 0x0f);	# EMC1043-4
+	} elsif ($chip == 2) {
+		return if ($addr == 0x4c) && ($dev_id != 0x3c);	# EMC1053-1
+		return if ($addr == 0x4d) && ($dev_id != 0x3d);	# EMC1053-2
+		return if ($addr == 0x48) && ($dev_id != 0x3e);	# EMC1053-3
+		return if ($addr == 0x49) && ($dev_id != 0x3f);	# EMC1053-4
+	} elsif ($chip == 3) {
+		return if ($addr == 0x4c) && ($dev_id != 0x30);	# EMC1063-1
+		return if ($addr == 0x4d) && ($dev_id != 0x31);	# EMC1063-2
+		return if ($addr == 0x48) && ($dev_id != 0x32);	# EMC1063-3
+		return if ($addr == 0x49) && ($dev_id != 0x33);	# EMC1063-4
+	}
+
+	return 7;
+}
+
+# Chip to detect: 0 = EMC1403, 1 = EMC1404, 2 = EMC2103, 3 = EMC1423,
+#	4 = EMC1002, 5 = EMC1033, 6 = EMC1046, 7 = EMC1047, 8 = EMC1072,
+#	9 = EMC1073, 10 = EMC1074, 11 = EMC1402, 12 = EMC1424
+# Registers used:
+#   0xfd: Device ID register
+#   0xfe: Vendor ID register
+#   0xff: Revision register
+sub emc1403_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my $dev_id = i2c_smbus_read_byte_data($file, 0xfd);
+	my $man_id = i2c_smbus_read_byte_data($file, 0xfe);
+	my $rev = i2c_smbus_read_byte_data($file, 0xff);
+
+	return unless $man_id == 0x5d;	# SMSC
+
+	if ($chip == 0) {		# EMC1403
+		return unless $dev_id == 0x21;
+		return unless $rev == 0x01;
+	} elsif ($chip == 1) {		# EMC1404
+		return unless $dev_id == 0x25;
+		return unless $rev == 0x01;
+	} elsif ($chip == 2) {		# EMC2103
+		return unless ($dev_id == 0x24) || ($dev_id == 0x26);
+		return unless $rev == 0x01;
+	} elsif ($chip == 3) {		# EMC1423
+		return unless $dev_id == 0x23;
+		return unless $rev == 0x01;
+	} elsif ($chip == 4) {		# EMC1002
+		return unless ($dev_id == 0x02) || ($dev_id == 0x03);
+		return unless $rev == 0x01;
+	} elsif ($chip == 5) {		# EMC1033
+		return unless ($dev_id == 0x0a) || ($dev_id == 0x0b);
+		return unless $rev == 0x01;
+	} elsif ($chip == 6) {		# EMC1046
+		return unless $dev_id == 0x1a;
+		return unless $rev == 0x01;
+	} elsif ($chip == 7) {		# EMC1047
+		return unless $dev_id == 0x1c;
+		return unless $rev == 0x01;
+	} elsif ($chip == 8) {		# EMC1072
+		return unless $dev_id == 0x20;
+		return unless $rev == 0x03;
+	} elsif ($chip == 9) {		# EMC1073
+		return unless $dev_id == 0x21;
+		return unless $rev == 0x03;
+	} elsif ($chip == 10) {		# EMC1074
+		return unless $dev_id == 0x25;
+		return unless $rev == 0x03;
+	} elsif ($chip == 11) {		# EMC1402
+		return unless $dev_id == 0x20;
+		return unless $rev == 0x01;
+	} elsif ($chip == 12) {		# EMC1424
+		return unless $dev_id == 0x27;
+		return unless $rev == 0x01;
+	}
+
+	return 6;
+}
+
+# This checks for non-FFFF values for temperature, voltage, and current.
+# The address (0x0b) is specified by the SMBus standard so it's likely
+# that this really is a smart battery.
+sub smartbatt_detect
+{
+	my ($file, $addr) = @_;
+
+	return if i2c_smbus_read_word_data($file, 0x08) == 0xffff
+	       || i2c_smbus_read_word_data($file, 0x09) == 0xffff
+	       || i2c_smbus_read_word_data($file, 0x0a) == 0xffff;
+	return 5;
+}
+
+# Chip to detect: 0 = W83L784R/AR/G, 1 = W83L785R/G, 2 = W83L786NR/NG/R/G,
+#		  3 = W83L785TS-S
+# Registers used:
+#   0x40: Configuration
+#   0x4a: Full I2C Address (W83L784R only)
+#   0x4b: I2C addresses of emulated LM75 chips (W83L784R only)
+#   0x4c: Winbond Vendor ID (Low Byte)
+#   0x4d: Winbond Vendor ID (High Byte)
+#   0x4e: Chip ID
+sub w83l784r_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my ($reg, @res);
+
+	return unless (i2c_smbus_read_byte_data($file, 0x40) & 0x80) == 0x00;
+	return if $chip == 0
+	      and i2c_smbus_read_byte_data($file, 0x4a) != $addr;
+	return unless i2c_smbus_read_byte_data($file, 0x4c) == 0xa3;
+	return unless i2c_smbus_read_byte_data($file, 0x4d) == 0x5c;
+
+	$reg = i2c_smbus_read_byte_data($file, 0x4e);
+	return if $chip == 0 and $reg != 0x50;
+	return if $chip == 1 and $reg != 0x60;
+	return if $chip == 2 and $reg != 0x80;
+	return if $chip == 3 and $reg != 0x70;
+
+	return 8 if $chip != 0; # No subclients
+
+	@res = (8);
+	$reg = i2c_smbus_read_byte_data($file, 0x4b);
+	push @res, ($reg & 0x07) + 0x48 unless $reg & 0x08;
+	push @res, (($reg & 0x70) >> 4) + 0x48 unless $reg & 0x80;
+	return @res;
+}
+
+# Chip to detect: MAX6639
+# Registers used:
+#   0x3d: Device ID
+#   0x3e: Manufacturer ID
+#   0x3f: Chip revision
+sub max6639_detect
+{
+	my ($file, $addr) = @_;
+	my ($man_id, $dev_id, $rev);
+
+	$dev_id = i2c_smbus_read_byte_data($file, 0x3d);
+	$man_id = i2c_smbus_read_byte_data($file, 0x3e);
+	$rev = i2c_smbus_read_byte_data($file, 0x3f);
+	
+	return unless $man_id == 0x4d;	# Maxim
+	return unless $dev_id == 0x58;	# MAX6639
+	return unless $rev == 0x00;
+
+	return 6;
+}
+
+# The max6650 has no device ID register. However, a few registers have
+# spare bits, which are documented as being always zero on read. We read
+# all of these registers check the spare bits. Any non-zero means this
+# is not a max6650/1.
+#
+# The always zero bits are:
+#   configuration byte register (0x02) - top 2 bits
+#   gpio status register (0x14) - top 3 bits
+#   alarm enable register (0x08) - top 3 bits
+#   alarm status register (0x0A) - top 3 bits
+#   tachometer count time register (0x16) - top 6 bits
+# Additionally, not all values are possible for lower 3 bits of
+# the configuration register.
+sub max6650_detect
+{
+	my ($file, $addr) = @_;
+
+	my $conf = i2c_smbus_read_byte_data($file, 0x02);
+
+	return if i2c_smbus_read_byte_data($file, 0x16) & 0xFC;
+	return if i2c_smbus_read_byte_data($file, 0x0A) & 0xE0;
+	return if i2c_smbus_read_byte_data($file, 0x08) & 0xE0;
+	return if i2c_smbus_read_byte_data($file, 0x14) & 0xE0;
+	return if ($conf & 0xC0) or ($conf & 0x07) > 4;
+
+	return 2;
+}
+
+sub max6655_detect
+{
+	my ($file, $addr) = @_;
+
+	# checking RDID (Device ID)
+	return unless i2c_smbus_read_byte_data($file, 0xfe) == 0x0a;
+	# checking RDRV (Manufacturer ID)
+	return unless i2c_smbus_read_byte_data($file, 0xff) == 0x4d;
+	# checking unused bits (conversion rate, extended temperature)
+	return unless i2c_smbus_read_byte_data($file, 0x04) & 0xf8;
+	return unless i2c_smbus_read_byte_data($file, 0x10) & 0x1f;
+	return unless i2c_smbus_read_byte_data($file, 0x11) & 0x1f;
+	return unless i2c_smbus_read_byte_data($file, 0x12) & 0x1f;
+
+	return 6;
+}
+
+# Chip to detect: 0 = STTS424, 1 = SE97/SE97B, 2 = SE98, 3 = ADT7408,
+#                 4 = TS3000/TSE2002, 5 = MAX6604, 6 = MCP98242,
+#                 7 = MCP98243, 8 = MCP9843, 9 = CAT6095 / CAT34TS02,
+#                 10 = STTS424E
+# Registers used:
+#   0x00: Capabilities
+#   0x01: Configuration
+#   0x06: Manufacturer ID
+#   0x07: Device ID
+sub jedec_JC42_4_detect
+{
+	my ($file, $addr, $chip) = @_;
+	my ($reg, $manid, $devid);
+
+	# We test the MSB only at first, because not all chips enjoy
+	# word access.
+
+	# Check for unused bits
+	$reg = i2c_smbus_read_byte_data($file, 0x00);
+	return if $reg & 0xff;
+	$reg = i2c_smbus_read_byte_data($file, 0x01);
+	return if $reg & 0xf8;
+
+	# Check for manufacturer and device ID
+	$manid = i2c_smbus_read_byte_data($file, 0x06);
+	$devid = i2c_smbus_read_byte_data($file, 0x07);
+
+	if ($chip == 0) {
+		return unless $manid == 0x10;		# STMicrolectronics
+		return unless $devid == 0x01;		# STTS424
+	} elsif ($chip == 1) {
+		return unless $manid == 0x11;		# NXP
+		return unless $devid == 0xa2;		# SE97
+	} elsif ($chip == 2) {
+		return unless $manid == 0x11;		# NXP
+		return unless $devid == 0xa1;		# SE98
+	} elsif ($chip == 3) {
+		return unless $manid == 0x11;		# ADT
+		return unless $devid == 0x08;		# ADT7408
+	} elsif ($chip == 4) {
+		return unless $manid == 0x00;		# IDT
+		return unless $devid == 0x29;		# TS3000/TSE2002
+	} elsif ($chip == 5) {
+		return unless $manid == 0x00;		# MAXIM
+		return unless $devid == 0x3e;		# MAX6604
+	} elsif ($chip == 6) {
+		return unless $manid == 0x00;		# MCP
+		return unless $devid == 0x20;		# MCP98242
+	} elsif ($chip == 7) {
+		return unless $manid == 0x00;		# MCP
+		return unless $devid == 0x21;		# MCP98243
+	} elsif ($chip == 8) {
+		return unless $manid == 0x00;		# MCP
+		return unless $devid == 0x00;		# MCP9843
+	} elsif ($chip == 9) {
+		return unless $manid == 0x1b;		# ONS
+		return unless $devid == 0x08;		# CAT6095 / CAT34TS02
+	} elsif ($chip == 10) {
+		return unless $manid == 0x10;		# STMicrolectronics
+		return unless $devid == 0x00;		# STTS424E02
+	}
+
+	# Now, do it all again with words. Note that we get
+	# the MSB first, so all value checks are byte-swapped.
+
+	# Check for unused bits
+	$reg = i2c_smbus_read_word_data($file, 0x00);
+	return if $reg < 0 || $reg & 0x00ff;
+
+	$manid = i2c_smbus_read_word_data($file, 0x06);
+	$devid = i2c_smbus_read_word_data($file, 0x07);
+	return if $manid < 0 || $devid < 0;
+	if ($chip == 0) {
+		return unless $manid == 0x4a10;		# STMicrolectronics
+		return unless ($devid & 0xfeff) == 0x0001; # STTS424
+	} elsif ($chip == 1) {
+		return unless $manid == 0x3111;		# NXP
+		return unless ($devid & 0xfcff) == 0x00a2; # SE97
+	} elsif ($chip == 2) {
+		return unless $manid == 0x3111;		# NXP
+		return unless ($devid & 0xfcff) == 0x00a1; # SE98
+	} elsif ($chip == 3) {
+		return unless $manid == 0xd411;		# ADT
+		return unless $devid == 0x0108;		# ADT7408
+	} elsif ($chip == 4) {
+		return unless $manid == 0xb300;		# IDT
+		return unless $devid == 0x0329;		# TS3000/TSE2002
+	} elsif ($chip == 5) {
+		return unless $manid == 0x4d00;		# MAXIM
+		return unless $devid == 0x003e;		# MAX6604
+	} elsif ($chip == 6) {
+		return unless $manid == 0x5400;		# MCP
+		return unless ($devid & 0xfcff) == 0x0020; # MCP98242
+	} elsif ($chip == 7) {
+		return unless $manid == 0x5400;		# MCP
+		return unless ($devid & 0xfcff) == 0x0021; # MCP98243
+	} elsif ($chip == 8) {
+		return unless $manid == 0x5400;		# MCP
+		return unless ($devid & 0xfcff) == 0x0000; # MCP9843
+	} elsif ($chip == 9) {
+		return unless $manid == 0x091b;		# ONS
+		return unless ($devid & 0xe0ff) == 0x0008; # CAT6095 / CAT34TS02
+	} elsif ($chip == 10) {
+		return unless $manid == 0x4a10;		# STMicrolectronics
+		return unless ($devid & 0xfeff) == 0x0000; # STTS424E02
+	}
+
+	return 5;
+}
+
+# This isn't very good detection.
+# Verify the i2c address, and the stepping ID (which is 0xb0 on
+# my chip but could be different for others...
+sub vt1211_i2c_detect
+{
+	my ($file, $addr) = @_;
+	return unless (i2c_smbus_read_byte_data($file, 0x48) & 0x7f) == $addr;
+	return unless i2c_smbus_read_byte_data($file, 0x3f) == 0xb0;
+	return 2;
+}
+
+# All ISA detection functions below take at least 1 parameter:
+# $_[0]: Address
+# Some of these functions which can detect more than one type of device,
+# take a second parameter:
+# $_[1]: Chip to detect
+
+# Chip to detect: 0 = LM78, 2 = LM79
+sub lm78_isa_detect
+{
+	my ($addr, $chip) = @_;
+	my $val = inb($addr + 1);
+	return if inb($addr + 2) != $val or inb($addr + 3) != $val or
+		  inb($addr + 7) != $val;
+
+	$val = inb($addr + 5);
+	outb($addr + 5, ~$val & 0x7f);
+	if ((inb($addr+5) & 0x7f) != (~ $val & 0x7f)) {
+		outb($addr+5, $val);
+		return;
+	}
+
+	return unless (isa_read_i5d6($addr, 0x40) & 0x80) == 0x00;
+	my $reg = isa_read_i5d6($addr, 0x49);
+	return if $chip == 0 && ($reg != 0x00 && $reg != 0x20 && $reg != 0x40);
+	return if $chip == 2 && ($reg & 0xfe) != 0xc0;
+
+	# Explicitly prevent misdetection of Winbond chips
+	$reg = isa_read_i5d6($addr, 0x4f);
+	return if $reg == 0xa3 || $reg == 0x5c;
+
+	# Explicitly prevent misdetection of ITE chips
+	$reg = isa_read_i5d6($addr, 0x58);
+	return if $reg == 0x90;
+
+	return 6;
+}
+
+# Chip to detect: 0 = W83781D, 1 = W83782D
+sub w83781d_isa_detect
+{
+	my ($addr, $chip) = @_;
+	my ($reg1, $reg2);
+	my $val = inb($addr + 1);
+	return if inb($addr + 2) != $val or inb($addr + 3) != $val or
+		  inb($addr + 7) != $val;
+
+	$val = inb($addr + 5);
+	outb($addr+5, ~$val & 0x7f);
+	if ((inb($addr+5) & 0x7f) != (~ $val & 0x7f)) {
+		outb($addr+5, $val);
+		return;
+	}
+
+	$reg1 = isa_read_i5d6($addr, 0x4e);
+	$reg2 = isa_read_i5d6($addr, 0x4f);
+	return unless (($reg1 & 0x80) == 0x00 and $reg2 == 0xa3) or
+		      (($reg1 & 0x80) == 0x80 and $reg2 == 0x5c);
+	return unless ($reg1 & 0x07) == 0x00;
+	$reg1 = isa_read_i5d6($addr, 0x58);
+	return if $chip == 0 && ($reg1 & 0xfe) != 0x10;
+	return if $chip == 1 && ($reg1 & 0xfe) != 0x30;
+
+	return 8;
+}
+
+########
+# IPMI #
+########
+
+# Returns: number of IPMI interfaces found
+sub ipmi_from_smbios
+{
+	my ($version, $if, @ipmi_if);
+
+	return 0 unless check_dmidecode_version();
+
+	# Parse the output of dmidecode into an array of IPMI interfaces.
+	# Each entry is a hash with the following keys: type and addr.
+	$if = -1;
+	open(local *DMIDECODE, "dmidecode -t 38 2>/dev/null |") or return 0;
+	while (<DMIDECODE>) {
+		if (m/^IPMI Device Information/) {
+			$if++;
+			next;
+		}
+		next unless $if >= 0;
+
+		if (m/^\tInterface Type: (.*)$/) {
+			$ipmi_if[$if]->{type} = "IPMI BMC $1";
+			$ipmi_if[$if]->{type} =~ s/ \(.*//;
+			next;
+		}
+		if (m/^\tBase Address: (0x[0-9A-Fa-f]+) \(I\/O\)$/) {
+			$ipmi_if[$if]->{addr} = oct($1);
+			next;
+		}
+	}
+	close(DMIDECODE);
+
+	foreach $if (@ipmi_if) {
+		if (exists $if->{addr}) {
+			printf("\%-60s", sprintf("Found `\%s' at 0x\%x... ",
+						 $if->{type}, $if->{addr}));
+		} else {
+			printf("\%-60s", sprintf("Found `\%s'... ",
+						 $if->{type}));
+		}
+		print "Success!\n".
+		      "    (confidence 8, driver `ipmisensors')\n";
+		my $new_hash = {
+			conf => 8,
+			isa_addr => $if->{addr} || 0,
+			chipname => $if->{type},
+		};
+		add_isa_to_chips_detected("ipmisensors", $new_hash);
+	}
+
+	return scalar @ipmi_if;
+}
+
+# We simply look for a register at standard locations.
+# For KCS, use the STATUS register. For SMIC, use the FLAGS register.
+# Incidentally they live at the same offset.
+sub ipmi_detect
+{
+	my ($addr) = @_;
+	return if inb($addr + 3) == 0xff;
+	return 4;
+}
+
+###################
+# ALIAS DETECTION #
+###################
+
+# These functions take at least 3 parameters:
+# $_[0]: ISA/LPC address
+# $_[1]: I2C file handle
+# $_[2]: I2C address
+# Some of these functions may take extra parameters.
+# They return 1 if both devices are the same, 0 if not.
+
+# Extra parameters:
+# $_[3]: First limit register to compare
+# $_[4]: Last limit register to compare
+sub winbond_alias_detect
+{
+	my ($isa_addr, $file, $i2c_addr, $first, $last) = @_;
+	my $i;
+
+	return 0 unless isa_read_i5d6($isa_addr, 0x48) == $i2c_addr;
+	for ($i = $first; $i <= $last; $i++) {
+		return 0 unless isa_read_i5d6($isa_addr, $i) ==
+				i2c_smbus_read_byte_data($file, $i);
+	}
+	return 1;
+}
+
+############################
+# PCI CHIP / CPU DETECTION #
+############################
+
+sub sis5595_pci_detect
+{
+	return unless exists $pci_list{'1039:0008'};
+	return 9;
+}
+
+sub via686a_pci_detect
+{
+	return unless exists $pci_list{'1106:3057'};
+	return 9;
+}
+
+sub via8231_pci_detect
+{
+	return unless exists $pci_list{'1106:8235'};
+	return 9;
+}
+
+sub k8temp_pci_detect
+{
+	return unless exists $pci_list{'1022:1103'};
+	return 9;
+}
+
+sub fam10h_pci_detect
+{
+	return unless exists $pci_list{'1022:1203'};
+
+	# Errata 319 (Inaccurate Temperature Measurement) affects
+	# revisions DR-BA, DR-B2 and DR-B3, all have model number = 2.
+	# Revisions RB-C2 and HY-D0 are also affected.
+	my $probecpu;
+	foreach $probecpu (@cpu) {
+		next unless $probecpu->{vendor_id} eq 'AuthenticAMD' &&
+			    $probecpu->{'cpu family'} == 0x10;
+
+		next if $probecpu->{model} < 4;		# DR-B*
+		next if $probecpu->{model} == 8;	# HY-D0
+		if ($probecpu->{model} == 4 && $probecpu->{stepping} <= 2) {	# RB-C2
+			my @dram_cfg = split /\n/, `setpci -d 1022:1202 94.L 2>/dev/null`;
+			next unless @dram_cfg >= 1;
+			next unless hex($dram_cfg[0]) & 0x00000100;	# DDR3
+		}
+
+		return 9;
+	}
+	
+	return;
+}
+
+sub fam11h_pci_detect
+{
+	return unless exists $pci_list{'1022:1303'};
+	return 9;
+}
+
+sub fam12h_14h_pci_detect
+{
+	return unless exists $pci_list{'1022:1703'};
+	return 9;
+}
+
+sub intel_amb_detect
+{
+	if ((exists $pci_list{'8086:25f0'}) ||	# Intel 5000
+	    (exists $pci_list{'8086:4030'})) {	# Intel 5400
+		return 9;
+	}
+	return;
+}
+
+sub cpuid
+{
+	my ($cpu_nr, $eax) = @_;
+
+	sysopen(CPUID, "/dev/cpu/$cpu_nr/cpuid", O_RDONLY) or return;
+	binmode CPUID;
+	sysseek(CPUID, $eax, SEEK_SET)
+		or die "Cannot seek /dev/cpu/$cpu_nr/cpuid";
+	sysread(CPUID, my $data, 16)
+		or die "Cannot read /dev/cpu/$cpu_nr/cpuid";
+	close CPUID;
+
+	return unpack("L4", $data);
+}
+
+sub coretemp_detect
+{
+	my $probecpu;
+
+	foreach $probecpu (@cpu) {
+		next unless $probecpu->{vendor_id} eq 'GenuineIntel' &&
+			    $probecpu->{'cpuid level'} >= 6;
+
+		# Now we check for the DTS flag
+		my @regs = cpuid($probecpu->{nr}, 6);
+		return unless @regs == 4;
+		return 9 if ($regs[0] & (1 << 0));	# eax, bit 0
+	}
+	return;
+}
+
+sub via_c7_detect
+{
+	my $probecpu;
+	foreach $probecpu (@cpu) {
+		if ($probecpu->{vendor_id} eq 'CentaurHauls' &&
+				$probecpu->{'cpu family'} == 6 &&
+				($probecpu->{model} == 0xa ||
+				 $probecpu->{model} == 0xd)) {
+			return 9;
+		}
+	}
+	return;
+}
+
+sub via_nano_detect
+{
+	my $probecpu;
+	foreach $probecpu (@cpu) {
+		if ($probecpu->{vendor_id} eq 'CentaurHauls' &&
+				$probecpu->{'cpu family'} == 6 &&
+				$probecpu->{model} == 0xf) {
+			return 9;
+		}
+	}
+	return;
+}
+
+#################
+# SPECIAL MODES #
+#################
+
+sub show_i2c_stats
+{
+	my ($chip, $addr, %histo, $chips);
+
+	# Gather the data
+	foreach $chip (@chip_ids) {
+		next unless defined $chip->{i2c_addrs};
+		$chips++;
+		foreach my $addr (@{$chip->{i2c_addrs}}) {
+			$histo{$addr}++;
+		}
+	}
+
+	# Display the data
+	printf "\%d I2C chips known, \%d I2C addresses probed\n\n",
+		$chips, scalar keys %histo;
+	print "     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f\n".
+	      "00:         ";
+	for (my $addr = 0x03; $addr <= 0x77; $addr++) {
+		printf("\n\%02x:", $addr) if ($addr % 16) == 0;
+		if (defined $histo{$addr}) {
+			printf ' %02d', $histo{$addr};
+		} else {
+			print ' --';
+		}
+	}
+	print "\n";
+}
+
+################
+# MAIN PROGRAM #
+################
+
+# $_[0]: reference to a list of chip hashes
+sub print_chips_report
+{
+	my ($listref) = @_;
+	my $data;
+
+	foreach $data (@$listref) {
+		my $is_i2c = exists $data->{i2c_addr};
+		my $is_isa = exists $data->{isa_addr};
+		print "  * ";
+		if ($is_i2c) {
+			printf "Bus `%s'\n", $i2c_adapters[$data->{i2c_devnr}]->{name};
+			printf "    Busdriver `%s', I2C address 0x%02x",
+			       $i2c_adapters[$data->{i2c_devnr}]->{driver}, $data->{i2c_addr};
+			if (exists $data->{i2c_sub_addrs}) {
+				print " (and";
+				my $sub_addr;
+				foreach $sub_addr (@{$data->{i2c_sub_addrs}}) {
+					printf " 0x%02x", $sub_addr;
+				}
+				print ")"
+			}
+			print "\n    ";
+		}
+		if ($is_isa) {
+			print "ISA bus";
+			if ($data->{isa_addr}) {
+				printf ", address 0x%x", $data->{isa_addr};
+			}
+			print " (Busdriver `i2c-isa')"
+				unless kernel_version_at_least(2, 6, 18);
+			print "\n    ";
+		}
+		printf "Chip `%s' (confidence: %d)\n",
+		       $data->{chipname}, $data->{conf};
+	}
+}
+
+sub generate_modprobes
+{
+	my ($driver, $detection, $adap);
+	my ($configfile, %bus_modules, %hwmon_modules);
+
+	foreach $driver (keys %chips_detected) {
+		foreach $detection (@{$chips_detected{$driver}}) {
+			# Tag adapters which host hardware monitoring chips we want to access
+			if (exists $detection->{i2c_devnr}
+			 && !exists $detection->{isa_addr}) {
+				$i2c_adapters[$detection->{i2c_devnr}]->{used}++;
+			}
+
+			# i2c-isa is loaded automatically (as a dependency)
+			# since 2.6.14, and will soon be gone.
+			if (exists $detection->{isa_addr}
+			 && !kernel_version_at_least(2, 6, 18)) {
+				$bus_modules{"i2c-isa"}++
+			}
+		}
+		if ($driver eq "ipmisensors") {
+			$bus_modules{"ipmi-si"}++;
+		}
+	}
+
+	# Handle aliases
+	# As of kernel 2.6.28, alias detection is handled by kernel drivers
+	# directly, so module options are no longer needed.
+	unless (kernel_version_at_least(2, 6, 28)) {
+		foreach $driver (keys %chips_detected) {
+			my @optionlist = ();
+			foreach $detection (@{$chips_detected{$driver}}) {
+				next unless exists $detection->{i2c_addr}
+					 && exists $detection->{isa_addr}
+					 && $i2c_adapters[$detection->{i2c_devnr}]->{used};
+
+				push @optionlist, sprintf("%d,0x%02x",
+							  $detection->{i2c_devnr},
+							  $detection->{i2c_addr});
+			}
+
+			next if not @optionlist;
+			$configfile = "# hwmon module options\n"
+				unless defined $configfile;
+			$configfile .= "options $driver ignore=".
+				       (join ",", @optionlist)."\n";
+		}
+	}
+
+	# If we added any module option to handle aliases, we need to load all
+	# the adapter drivers so that the numbers will be the same. If not, then
+	# we only load the adapter drivers which are useful.
+	foreach $adap (@i2c_adapters) {
+		next unless defined $adap;
+		next if $adap->{autoload};
+		next if $adap->{driver} eq 'UNKNOWN';
+		next if not defined $configfile and not $adap->{used};
+		$bus_modules{$adap->{driver}}++;
+	}
+
+	# Now determine the chip probe lines
+	foreach $driver (keys %chips_detected) {
+		next if not @{$chips_detected{$driver}};
+		if ($driver eq "to-be-written") {
+			print "Note: there is no driver for ${$chips_detected{$driver}}[0]{chipname} yet.\n".
+			      "Check http://www.lm-sensors.org/wiki/Devices for updates.\n\n";
+		} else {
+			open(local *INPUTFILE, "modprobe -l $driver 2>/dev/null |");
+			local $_;
+			my $modulefound = 0;
+			while (<INPUTFILE>) {
+				if (m@/@) {
+					$modulefound = 1;
+					last;
+				}
+			}
+			close(INPUTFILE);
+			# Check return value from modprobe in case modprobe -l
+			# isn't supported
+			if ((($? >> 8) == 0) && ! $modulefound) {
+				print "Warning: the required module $driver is not currently installed\n".
+				      "on your system. If it is built into the kernel then it's OK.\n".
+				      "Otherwise, check http://www.lm-sensors.org/wiki/Devices for\n".
+				      "driver availability.\n\n";
+			} else {
+				$hwmon_modules{$driver}++
+					unless hwmon_is_autoloaded($driver);
+			}
+		}
+	}
+
+	my @bus_modules = sort keys %bus_modules;
+	my @hwmon_modules = sort keys %hwmon_modules;
+	return ($configfile, \@bus_modules, \@hwmon_modules);
+}
+
+sub write_config
+{
+	my ($configfile, $bus_modules, $hwmon_modules) = @_;
+
+	if (defined $configfile) {
+		my $have_modprobe_d = -d '/etc/modprobe.d';
+		printf "Do you want to \%s /etc/modprobe.d/lm_sensors.conf? (\%s): ",
+		       (-e '/etc/modprobe.d/lm_sensors.conf' ? 'overwrite' : 'generate'),
+		       ($have_modprobe_d ? 'YES/no' : 'yes/NO');
+		$_ = <STDIN>;
+		if (($have_modprobe_d and not m/^\s*n/i) or m/^\s*y/i) {
+			unless ($have_modprobe_d) {
+				mkdir('/etc/modprobe.d', 0777)
+					or die "Sorry, can't create /etc/modprobe.d ($!)";
+			}
+			open(local *MODPROBE_D, ">/etc/modprobe.d/lm_sensors.conf")
+				or die "Sorry, can't create /etc/modprobe.d/lm_sensors.conf ($!)";
+			print MODPROBE_D "# Generated by sensors-detect on " . scalar localtime() . "\n";
+			print MODPROBE_D $configfile;
+			close(MODPROBE_D);
+		} else {
+			print "To make the sensors modules behave correctly, add these lines to\n".
+			      "/etc/modprobe.conf:\n\n";
+			print "#----cut here----\n".
+			      $configfile.
+			      "#----cut here----\n\n";
+		}
+	}
+
+	my $have_sysconfig = -d '/etc/sysconfig';
+	printf "Do you want to \%s /etc/sysconfig/lm_sensors? (\%s): ",
+	       (-e '/etc/sysconfig/lm_sensors' ? 'overwrite' : 'generate'),
+	       ($have_sysconfig ? 'YES/no' : 'yes/NO');
+	$_ = <STDIN>;
+	if (($have_sysconfig and not m/^\s*n/i) or m/^\s*y/i) {
+		unless ($have_sysconfig) {
+			mkdir('/etc/sysconfig', 0777)
+				or die "Sorry, can't create /etc/sysconfig ($!)";
+		}
+		open(local *SYSCONFIG, ">/etc/sysconfig/lm_sensors")
+			or die "Sorry, can't create /etc/sysconfig/lm_sensors ($!)";
+		print SYSCONFIG "# Generated by sensors-detect on " . scalar localtime() . "\n";
+		print SYSCONFIG <<'EOT';
+# This file is sourced by /etc/init.d/lm_sensors and defines the modules to
+# be loaded/unloaded.
+#
+# The format of this file is a shell script that simply defines variables:
+# HWMON_MODULES for hardware monitoring driver modules, and optionally
+# BUS_MODULES for any required bus driver module (for example for I2C or SPI).
+
+EOT
+		print SYSCONFIG "BUS_MODULES=\"", join(" ", @{$bus_modules}), "\"\n"
+			if @{$bus_modules};
+		print SYSCONFIG "HWMON_MODULES=\"", join(" ", @{$hwmon_modules}), "\"\n";
+
+		print SYSCONFIG <<'EOT';
+
+# For compatibility reasons, modules are also listed individually as variables
+#    MODULE_0, MODULE_1, MODULE_2, etc.
+# You should use BUS_MODULES and HWMON_MODULES instead if possible.
+
+EOT
+		my $i = 0;
+		foreach (@{$bus_modules}, @{$hwmon_modules}) {
+			print SYSCONFIG "MODULE_$i=$_\n";
+			$i++;
+		}
+		close(SYSCONFIG);
+
+		print "Copy prog/init/lm_sensors.init to /etc/init.d/lm_sensors\n".
+		      "for initialization at boot time.\n"
+			unless -f "/etc/init.d/lm_sensors";
+
+		if (-x "/sbin/insserv" && -f "/etc/init.d/lm_sensors") {
+			system("/sbin/insserv", "/etc/init.d/lm_sensors");
+		} elsif (-x "/sbin/chkconfig" && -f "/etc/init.d/lm_sensors") {
+			system("/sbin/chkconfig", "lm_sensors", "on");
+			if (-x "/sbin/service") {
+				system("/sbin/service", "lm_sensors", "start");
+			}
+		} else {
+			print "You should now start the lm_sensors service to load the required\n".
+			      "kernel modules.\n\n";
+		}
+	} else {
+		print "To load everything that is needed, add this to one of the system\n".
+		      "initialization scripts (e.g. /etc/rc.d/rc.local):\n\n";
+		print "#----cut here----\n";
+		if (@{$bus_modules}) {
+			print "# Adapter drivers\n";
+			print "modprobe $_\n" foreach (@{$bus_modules});
+		}
+		print "# Chip drivers\n";
+		print "modprobe $_\n" foreach (@{$hwmon_modules});
+		print((-e '/usr/bin/sensors' ?
+		       "/usr/bin/sensors -s\n" :
+		       "/usr/local/bin/sensors -s\n").
+		      "#----cut here----\n\n");
+
+		print "If you have some drivers built into your kernel, the list above will\n".
+		      "contain too many modules. Skip the appropriate ones! You really\n".
+		      "should try these commands right now to make sure everything is\n".
+		      "working properly. Monitoring programs won't work until the needed\n".
+		      "modules are loaded.\n\n";
+	}
+
+}
+
+sub main
+{
+	my ($input, $superio_features);
+
+	# Handle special command line cases first
+	if (defined $ARGV[0] && $ARGV[0] eq "--stat") {
+		show_i2c_stats();
+		exit 0;
+	}
+
+	# We won't go very far if not root
+	unless ($> == 0) {
+		print "You need to be root to run this script.\n";
+		exit -1;
+	}
+
+	if (-x "/sbin/service" && -f "/etc/init.d/lm_sensors" &&
+	    -f "/var/lock/subsys/lm_sensors") {
+		system("/sbin/service", "lm_sensors", "stop");
+	}
+
+	initialize_kernel_version();
+	initialize_conf();
+	initialize_pci();
+	initialize_modules_list();
+	# Make sure any special case chips are added to the chip_ids list
+	# before making the support modules list
+	chip_special_cases();
+	initialize_modules_supported();
+	initialize_cpu_list();
+
+	print "# sensors-detect revision $revision\n";
+	initialize_dmi_data();
+	print_dmi_summary();
+	print "\n";
+	print "This program will help you determine which kernel modules you need\n",
+	      "to load to use lm_sensors most effectively. It is generally safe\n",
+	      "and recommended to accept the default answers to all questions,\n",
+	      "unless you know what you're doing.\n\n";
+
+	print "Some south bridges, CPUs or memory controllers contain embedded sensors.\n".
+	      "Do you want to scan for them? This is totally safe. (YES/no): ";
+	unless (<STDIN> =~ /^\s*n/i) {
+		# Load the cpuid driver if needed
+		unless (-e "/dev/cpu/$cpu[0]->{nr}/cpuid") {
+			load_module("cpuid");
+			udev_settle();
+		}
+
+		$| = 1;
+		foreach my $entry (@cpu_ids) {
+			scan_cpu($entry);
+		}
+		$| = 0;
+	}
+	print "\n";
+
+	$superio_features = 0;
+	# Skip "random" I/O port probing on PPC
+	if ($kernel_arch ne 'ppc'
+	 && $kernel_arch ne 'ppc64') {
+		print "Some Super I/O chips contain embedded sensors. We have to write to\n".
+		      "standard I/O ports to probe them. This is usually safe.\n";
+		print "Do you want to scan for Super I/O sensors? (YES/no): ";
+		unless (<STDIN> =~ /^\s*n/i) {
+			initialize_ioports();
+			$superio_features |= scan_superio(0x2e, 0x2f);
+			$superio_features |= scan_superio(0x4e, 0x4f);
+			close_ioports();
+		}
+		print "\n";
+
+		unless (is_laptop()) {
+			print "Some systems (mainly servers) implement IPMI, a set of common interfaces\n".
+			      "through which system health data may be retrieved, amongst other things.\n".
+			      "We first try to get the information from SMBIOS. If we don't find it\n".
+			      "there, we have to read from arbitrary I/O ports to probe for such\n".
+			      "interfaces. This is normally safe. Do you want to scan for IPMI\n".
+			      "interfaces? (YES/no): ";
+			unless (<STDIN> =~ /^\s*n/i) {
+				if (!ipmi_from_smbios()) {
+					initialize_ioports();
+					scan_isa_bus(\@ipmi_ifs);
+					close_ioports();
+				}
+			}
+			print "\n";
+		}
+
+		printf "Some hardware monitoring chips are accessible through the ISA I/O ports.\n".
+		       "We have to write to arbitrary I/O ports to probe them. This is usually\n".
+		       "safe though. Yes, you do have ISA I/O ports even if you do not have any\n".
+		       "ISA slots! Do you want to scan the ISA I/O ports? (\%s): ",
+		       $superio_features ? "yes/NO" : "YES/no";
+		$input = <STDIN>;
+		unless ($input =~ /^\s*n/i
+		     || ($superio_features && $input !~ /^\s*y/i)) {
+			initialize_ioports();
+			scan_isa_bus(\@chip_ids);
+			close_ioports();
+		}
+		print "\n";
+	}
+
+	print "Lastly, we can probe the I2C/SMBus adapters for connected hardware\n".
+	      "monitoring devices. This is the most risky part, and while it works\n".
+	      "reasonably well on most systems, it has been reported to cause trouble\n".
+	      "on some systems.\n".
+	      "Do you want to probe the I2C/SMBus adapters now? (YES/no): ";
+
+	unless (<STDIN> =~ /^\s*n/i) {
+		adapter_pci_detection();
+		load_module("i2c-dev") unless -e "$sysfs_root/class/i2c-dev";
+		initialize_i2c_adapters_list();
+		$i2c_addresses_to_scan = i2c_addresses_to_scan();
+		print "\n";
+
+		# Skip SMBus probing by default if Super-I/O has all the features
+		my $by_default = ~$superio_features & (FEAT_IN | FEAT_FAN | FEAT_TEMP);
+		# Except on Asus and Tyan boards which often have more than
+		# one hardware monitoring chip
+		$by_default = 1 if dmi_match('board_vendor', 'asustek', 'tyan',
+					     'supermicro');
+
+		udev_settle();
+		for (my $dev_nr = 0; $dev_nr < @i2c_adapters; $dev_nr++) {
+			next unless exists $i2c_adapters[$dev_nr];
+			scan_i2c_adapter($dev_nr, $by_default);
+		}
+		filter_out_fake_i2c_drivers();
+	}
+
+	if (!keys %chips_detected) {
+		print "Sorry, no sensors were detected.\n";
+		if (is_laptop() && -d "$sysfs_root/firmware/acpi") {
+			print "This is relatively common on laptops, where thermal management is\n".
+			      "handled by ACPI rather than the OS.\n";
+		} else {
+			print "Either your system has no sensors, or they are not supported, or\n".
+			      "they are connected to an I2C or SMBus adapter that is not\n".
+			      "supported. If you find out what chips are on your board, check\n".
+			      "http://www.lm-sensors.org/wiki/Devices for driver status.\n";
+		}
+		exit;
+	}
+
+	print "Now follows a summary of the probes I have just done.\n".
+	      "Just press ENTER to continue: ";
+	<STDIN>;
+
+	initialize_hwmon_autoloaded();
+	foreach my $driver (keys %chips_detected) {
+		next unless @{$chips_detected{$driver}};
+		find_aliases($chips_detected{$driver});
+		print "\nDriver `$driver'";
+		print " (autoloaded)" if hwmon_is_autoloaded($driver);
+		print ":\n";
+		print_chips_report($chips_detected{$driver});
+	}
+	print "\n";
+
+	my ($configfile, $bus_modules, $hwmon_modules) = generate_modprobes();
+
+	if (@{$hwmon_modules}) {
+		write_config($configfile, $bus_modules, $hwmon_modules);
+	} else {
+		print "No modules to load, skipping modules configuration.\n\n";
+	}
+
+	unload_modules();
+}
+
+sub cleanup_on_int
+{
+	print "\n";
+	unload_modules();
+	exit;
+}
+
+$SIG{INT} = \&cleanup_on_int;
+
+main;
Index: /lm-sensors/tags/V3-3-0/prog/detect/sensors-detect.8
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/detect/sensors-detect.8	(revision 5515)
+++ /lm-sensors/tags/V3-3-0/prog/detect/sensors-detect.8	(revision 5515)
@@ -0,0 +1,52 @@
+.TH SENSORS-DETECT 8 "December 2008" "lm-sensors 3"
+.SH NAME
+sensors-detect \- detect hardware monitoring chips
+
+.SH SYNOPSIS
+.B sensors-detect
+
+.SH DESCRIPTION
+sensors-detect is an interactive program that will walk you through the
+process of scanning your system for various hardware monitoring chips,
+or sensors, supported by libsensors(3), or more generally by the lm_sensors
+tool suite.
+
+sensors-detect will look for the following devices, in order:
+.IP \(bu
+Sensors embedded in CPUs, south bridges and memory controllers.
+.IP \(bu
+Sensors embedded in Super I/O chips.
+.IP \(bu
+Hardware monitoring chips accessed through ISA I/O ports.
+.IP \(bu
+Hardware monitoring chips reachable over the SMBus or more generally
+any I2C bus on your system.
+.PP
+As the last two detection steps can cause trouble on some systems, they
+are normally not attempted if the second detection step led to the
+discovery of a Super I/O chip with complete hardware monitoring features.
+However, the user is always free to ask for all detection steps if so is
+his/her wish. This can be useful if a given system has more than one
+hardware monitoring chip. Some vendors are known to do this, most notably
+Asus and Tyan.
+
+.SH WARNING
+sensors-detect needs to access the hardware for most of the chip detections.
+By definition, it doesn't know which chips are there before it manages to
+identify them. This means that it can access chips in a way these chips do
+not like, causing problems ranging from SMBus lockup to permanent hardware
+damage (a rare case, thankfully.)
+
+The authors made their best to make the detection as safe as possible, and
+it turns out to work just fine in most cases, however it is impossible to
+guarantee that sensors-detect will not lock or kill a specific system. So,
+as a rule of thumb, you should not run sensors-detect on production servers,
+and you should not run sensors-detect if can't afford replacing a random
+part of your system. Also, it is recommended to not force a detection step
+which would have been skipped by default, unless you know what you are doing.
+
+.SH SEE ALSO
+sensors(1), libsensors(3)
+
+.SH AUTHOR
+Frodo Looijaard and Jean Delvare
Index: /lm-sensors/tags/V3-3-0/prog/detect/Module.mk
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/detect/Module.mk	(revision 5163)
+++ /lm-sensors/tags/V3-3-0/prog/detect/Module.mk	(revision 5163)
@@ -0,0 +1,49 @@
+#  Module.mk - Makefile for a Linux module for reading sensor data.
+#  Copyright (c) 1998, 1999  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.
+
+# Note that MODULE_DIR (the directory in which this file resides) is a
+# 'simply expanded variable'. That means that its value is substituted
+# verbatim in the rules, until it is redefined. 
+MODULE_DIR := prog/detect
+PROGDETECTDIR := $(MODULE_DIR)
+
+PROGDETECTMAN8DIR := $(MANDIR)/man8
+PROGDETECTMAN8FILES := $(MODULE_DIR)/sensors-detect.8
+
+# Regrettably, even 'simply expanded variables' will not put their currently
+# defined value verbatim into the command-list of rules...
+PROGDETECTSBININSTALL := $(MODULE_DIR)/sensors-detect
+
+REMOVEDETECTBIN := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(SBINDIR)/%,$(PROGDETECTSBININSTALL))
+REMOVEDETECTMAN := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(PROGDETECTMAN8DIR)/%,$(PROGDETECTMAN8FILES))
+
+all-prog-detect:
+user :: all-prog-detect
+
+install-prog-detect: all-prog-detect
+	$(MKDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(PROGDETECTMAN8DIR)
+	$(INSTALL) -m 755 $(PROGDETECTSBININSTALL) $(DESTDIR)$(SBINDIR)
+	$(INSTALL) -m 644 $(PROGDETECTMAN8FILES) $(DESTDIR)$(PROGDETECTMAN8DIR)
+user_install :: install-prog-detect
+
+user_uninstall::
+	$(RM) $(REMOVEDETECTBIN)
+	$(RM) $(REMOVEDETECTMAN)
+
+clean-prog-detect:
+clean :: clean-prog-detect
Index: /lm-sensors/tags/V3-3-0/prog/sensors/main.c
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensors/main.c	(revision 5801)
+++ /lm-sensors/tags/V3-3-0/prog/sensors/main.c	(revision 5801)
@@ -0,0 +1,350 @@
+/*
+    main.c - Part of sensors, a user-space program for hardware monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    Copyright (C) 2007        Jean Delvare <khali@linux-fr.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.
+
+    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 <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <errno.h>
+#include <locale.h>
+#include <langinfo.h>
+
+#ifndef __UCLIBC__
+#include <iconv.h>
+#define HAVE_ICONV
+#endif
+
+#include "lib/sensors.h"
+#include "lib/error.h"
+#include "main.h"
+#include "chips.h"
+#include "version.h"
+
+#define PROGRAM			"sensors"
+#define VERSION			LM_VERSION
+
+static int do_sets, do_raw, hide_adapter;
+
+int fahrenheit;
+char degstr[5]; /* store the correct string to print degrees */
+
+static void print_short_help(void)
+{
+	printf("Try `%s -h' for more information\n", PROGRAM);
+}
+
+static void print_long_help(void)
+{
+	printf("Usage: %s [OPTION]... [CHIP]...\n", PROGRAM);
+	puts("  -c, --config-file     Specify a config file\n"
+	     "  -h, --help            Display this help text\n"
+	     "  -s, --set             Execute `set' statements (root only)\n"
+	     "  -f, --fahrenheit      Show temperatures in degrees fahrenheit\n"
+	     "  -A, --no-adapter      Do not show adapter for each chip\n"
+	     "      --bus-list        Generate bus statements for sensors.conf\n"
+	     "  -u                    Raw output (debugging only)\n"
+	     "  -v, --version         Display the program version\n"
+	     "\n"
+	     "Use `-' after `-c' to read the config file from stdin.\n"
+	     "If no chips are specified, all chip info will be printed.\n"
+	     "Example chip names:\n"
+	     "\tlm78-i2c-0-2d\t*-i2c-0-2d\n"
+	     "\tlm78-i2c-0-*\t*-i2c-0-*\n"
+	     "\tlm78-i2c-*-2d\t*-i2c-*-2d\n"
+	     "\tlm78-i2c-*-*\t*-i2c-*-*\n"
+	     "\tlm78-isa-0290\t*-isa-0290\n"
+	     "\tlm78-isa-*\t*-isa-*\n"
+	     "\tlm78-*");
+}
+
+static void print_version(void)
+{
+	printf("%s version %s with libsensors version %s\n", PROGRAM, VERSION,
+	       libsensors_version);
+}
+
+/* Return 0 on success, and an exit error code otherwise */
+static int read_config_file(const char *config_file_name)
+{
+	FILE *config_file;
+	int err;
+
+	if (config_file_name) {
+		if (!strcmp(config_file_name, "-"))
+			config_file = stdin;
+		else
+			config_file = fopen(config_file_name, "r");
+
+		if (!config_file) {
+			fprintf(stderr, "Could not open config file\n");
+			perror(config_file_name);
+			return 1;
+		}
+	} else {
+		/* Use libsensors default */
+		config_file = NULL;
+	}
+
+	err = sensors_init(config_file);
+	if (err) {
+		fprintf(stderr, "sensors_init: %s\n", sensors_strerror(err));
+		if (config_file)
+			fclose(config_file);
+		return 1;
+	}
+
+	if (config_file && fclose(config_file) == EOF)
+		perror(config_file_name);
+
+	return 0;
+}
+
+static void set_degstr(void)
+{
+	const char *deg_default_text[2] = { " C", " F" };
+
+#ifdef HAVE_ICONV
+	/* Size hardcoded for better performance.
+	   Don't forget to count the trailing \0! */
+	size_t deg_latin1_size = 3;
+	char deg_latin1_text[2][3] = { "\260C", "\260F" };
+	char *deg_latin1_ptr = deg_latin1_text[fahrenheit];
+	size_t nconv;
+	size_t degstr_size = sizeof(degstr);
+	char *degstr_ptr = degstr;
+
+	iconv_t cd = iconv_open(nl_langinfo(CODESET), "ISO-8859-1");
+	if (cd != (iconv_t) -1) {
+		nconv = iconv(cd, &deg_latin1_ptr, &deg_latin1_size,
+			      &degstr_ptr, &degstr_size);
+		iconv_close(cd);
+
+		if (nconv != (size_t) -1)
+			return;
+	}
+#endif /* HAVE_ICONV */
+
+	/* There was an error during the conversion, use the default text */
+	strcpy(degstr, deg_default_text[fahrenheit]);
+}
+
+static const char *sprintf_chip_name(const sensors_chip_name *name)
+{
+#define BUF_SIZE 200
+	static char buf[BUF_SIZE];
+
+	if (sensors_snprintf_chip_name(buf, BUF_SIZE, name) < 0)
+		return NULL;
+	return buf;
+}
+
+static void do_a_print(const sensors_chip_name *name)
+{
+	printf("%s\n", sprintf_chip_name(name));
+	if (!hide_adapter) {
+		const char *adap = sensors_get_adapter_name(&name->bus);
+		if (adap)
+			printf("Adapter: %s\n", adap);
+		else
+			fprintf(stderr, "Can't get adapter name\n");
+	}
+	if (do_raw)
+		print_chip_raw(name);
+	else
+		print_chip(name);
+	printf("\n");
+}
+
+/* returns 1 on error */
+static int do_a_set(const sensors_chip_name *name)
+{
+	int err;
+
+	if ((err = sensors_do_chip_sets(name))) {
+		if (err == -SENSORS_ERR_KERNEL) {
+			fprintf(stderr, "%s: %s\n",
+				sprintf_chip_name(name),
+				sensors_strerror(err));
+			fprintf(stderr, "Run as root?\n");
+			return 1;
+		} else if (err == -SENSORS_ERR_ACCESS_W) {
+			fprintf(stderr,
+				"%s: At least one \"set\" statement failed\n",
+				sprintf_chip_name(name));
+		} else {
+			fprintf(stderr, "%s: %s\n", sprintf_chip_name(name),
+				sensors_strerror(err));
+		}
+	}
+	return 0;
+}
+
+/* returns number of chips found */
+static int do_the_real_work(const sensors_chip_name *match, int *err)
+{
+	const sensors_chip_name *chip;
+	int chip_nr;
+	int cnt = 0;
+
+	chip_nr = 0;
+	while ((chip = sensors_get_detected_chips(match, &chip_nr))) {
+		if (do_sets) {
+			if (do_a_set(chip))
+				*err = 1;
+		} else
+			do_a_print(chip);
+		cnt++;
+	}
+	return cnt;
+}
+
+/* List the buses in a format suitable for sensors.conf. We only list
+   bus types for which bus statements are actually useful and supported.
+   Known bug: i2c buses with number >= 32 or 64 could be listed several
+   times. Very unlikely to ever happen, though. */
+static void print_bus_list(void)
+{
+	const sensors_chip_name *chip;
+	int chip_nr;
+	unsigned long seen_i2c = 0;
+
+	chip_nr = 0;
+	while ((chip = sensors_get_detected_chips(NULL, &chip_nr))) {
+		switch (chip->bus.type) {
+		case SENSORS_BUS_TYPE_I2C:
+			if (chip->bus.nr < (int)sizeof(unsigned long) * 8) {
+				if (seen_i2c & (1 << chip->bus.nr))
+					break;
+				seen_i2c |= 1 << chip->bus.nr;
+			}
+			printf("bus \"i2c-%d\" \"%s\"\n", chip->bus.nr,
+			       sensors_get_adapter_name(&chip->bus));
+			break;
+		}
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	int c, i, err, do_bus_list;
+	const char *config_file_name = NULL;
+
+	struct option long_opts[] =  {
+		{ "help", no_argument, NULL, 'h' },
+		{ "set", no_argument, NULL, 's' },
+		{ "version", no_argument, NULL, 'v'},
+		{ "fahrenheit", no_argument, NULL, 'f' },
+		{ "no-adapter", no_argument, NULL, 'A' },
+		{ "config-file", required_argument, NULL, 'c' },
+		{ "bus-list", no_argument, NULL, 'B' },
+		{ 0, 0, 0, 0 }
+	};
+
+	setlocale(LC_CTYPE, "");
+
+	do_raw = 0;
+	do_sets = 0;
+	do_bus_list = 0;
+	hide_adapter = 0;
+	while (1) {
+		c = getopt_long(argc, argv, "hsvfAc:u", long_opts, NULL);
+		if (c == EOF)
+			break;
+		switch(c) {
+		case ':':
+		case '?':
+			print_short_help();
+			exit(1);
+		case 'h':
+			print_long_help();
+			exit(0);
+		case 'v':
+			print_version();
+			exit(0);
+		case 'c':
+			config_file_name = optarg;
+			break;
+		case 's':
+			do_sets = 1;
+			break;
+		case 'f':
+			fahrenheit = 1;
+			break;
+		case 'A':
+			hide_adapter = 1;
+			break;
+		case 'u':
+			do_raw = 1;
+			break;
+		case 'B':
+			do_bus_list = 1;
+			break;
+		default:
+			fprintf(stderr,
+				"Internal error while parsing options!\n");
+			exit(1);
+		}
+	}
+
+	err = read_config_file(config_file_name);
+	if (err)
+		exit(err);
+
+	/* build the degrees string */
+	set_degstr();
+
+	if (do_bus_list) {
+		print_bus_list();
+	} else if (optind == argc) { /* No chip name on command line */
+		if (!do_the_real_work(NULL, &err)) {
+			fprintf(stderr,
+				"No sensors found!\n"
+				"Make sure you loaded all the kernel drivers you need.\n"
+				"Try sensors-detect to find out which these are.\n");
+			err = 1;
+		}
+	} else {
+		int cnt = 0;
+		sensors_chip_name chip;
+
+		for (i = optind; i < argc; i++) {
+			if (sensors_parse_chip_name(argv[i], &chip)) {
+				fprintf(stderr,
+					"Parse error in chip name `%s'\n",
+					argv[i]);
+				print_short_help();
+				err = 1;
+				goto exit;
+			}
+			cnt += do_the_real_work(&chip, &err);
+			sensors_free_chip_name(&chip);
+		}
+
+		if (!cnt) {
+			fprintf(stderr, "Specified sensor(s) not found!\n");
+			err = 1;
+		}
+	}
+
+exit:
+	sensors_cleanup();
+	exit(err);
+}
Index: /lm-sensors/tags/V3-3-0/prog/sensors/chips.c
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensors/chips.c	(revision 5944)
+++ /lm-sensors/tags/V3-3-0/prog/sensors/chips.c	(revision 5944)
@@ -0,0 +1,758 @@
+/*
+    chips.c - Part of sensors, a user-space program for hardware monitoring
+    Copyright (C) 1998-2003  Frodo Looijaard <frodol@dds.nl> and
+                             Mark D. Studebaker <mdsxyz123@yahoo.com>
+    Copyright (C) 2007-2010  Jean Delvare <khali@linux-fr.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.
+
+    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 <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "main.h"
+#include "chips.h"
+#include "lib/sensors.h"
+#include "lib/error.h"
+
+#define ARRAY_SIZE(arr) (int)(sizeof(arr) / sizeof((arr)[0]))
+
+void print_chip_raw(const sensors_chip_name *name)
+{
+	int a, b, err;
+	const sensors_feature *feature;
+	const sensors_subfeature *sub;
+	char *label;
+	double val;
+
+	a = 0;
+	while ((feature = sensors_get_features(name, &a))) {
+		if (!(label = sensors_get_label(name, feature))) {
+			fprintf(stderr, "ERROR: Can't get label of feature "
+				"%s!\n", feature->name);
+			continue;
+		}
+		printf("%s:\n", label);
+		free(label);
+
+		b = 0;
+		while ((sub = sensors_get_all_subfeatures(name, feature, &b))) {
+			if (sub->flags & SENSORS_MODE_R) {
+				if ((err = sensors_get_value(name, sub->number,
+							     &val)))
+					fprintf(stderr, "ERROR: Can't get "
+						"value of subfeature %s: %s\n",
+						sub->name,
+						sensors_strerror(err));
+				else
+					printf("  %s: %.3f\n", sub->name, val);
+			} else
+				printf("(%s)\n", label);
+		}
+	}
+}
+
+static const char hyst_str[] = "hyst";
+
+static inline double deg_ctof(double cel)
+{
+	return cel * (9.0F / 5.0F) + 32.0F;
+}
+
+static void print_label(const char *label, int space)
+{
+	int len = strlen(label)+1;
+	printf("%s:%*s", label, space - len, "");
+}
+
+static double get_value(const sensors_chip_name *name,
+			const sensors_subfeature *sub)
+{
+	double val;
+	int err;
+
+	err = sensors_get_value(name, sub->number, &val);
+	if (err) {
+		fprintf(stderr, "ERROR: Can't get value of subfeature %s: %s\n",
+			sub->name, sensors_strerror(err));
+		val = 0;
+	}
+	return val;
+}
+
+/* A variant for input values, where we want to handle errors gracefully */
+static int get_input_value(const sensors_chip_name *name,
+			   const sensors_subfeature *sub,
+			   double *val)
+{
+	int err;
+
+	err = sensors_get_value(name, sub->number, val);
+	if (err && err != -SENSORS_ERR_ACCESS_R) {
+		fprintf(stderr, "ERROR: Can't get value of subfeature %s: %s\n",
+			sub->name, sensors_strerror(err));
+	}
+	return err;
+}
+
+static int get_label_size(const sensors_chip_name *name)
+{
+	int i;
+	const sensors_feature *iter;
+	char *label;
+	unsigned int max_size = 11;	/* 11 as minumum label width */
+
+	i = 0;
+	while ((iter = sensors_get_features(name, &i))) {
+		if ((label = sensors_get_label(name, iter)) &&
+		    strlen(label) > max_size)
+			max_size = strlen(label);
+		free(label);
+	}
+
+	/* One more for the colon, and one more to guarantee at least one
+	   space between that colon and the value */
+	return max_size + 2;
+}
+
+static void print_alarms(struct sensor_subfeature_data *alarms, int alarm_count,
+			 int leading_spaces)
+{
+	int i, printed;
+
+	printf("%*s", leading_spaces + 7, "ALARM");
+	if (alarm_count > 1 || alarms[0].name) {
+		printf(" (");
+		for (i = printed = 0; i < alarm_count; i++) {
+			if (alarms[i].name) {
+				if (printed)
+					printf(", ");
+				printf("%s", alarms[i].name);
+				printed = 1;
+			}
+		}
+		printf(")");
+	}
+}
+
+static void print_limits(struct sensor_subfeature_data *limits,
+			 int limit_count,
+			 struct sensor_subfeature_data *alarms,
+			 int alarm_count, int label_size,
+			 const char *fmt)
+{
+	int i, slot, skip;
+	int alarms_printed = 0;
+
+	/*
+	 * We print limits on two columns, filling lines first, except for
+	 * hysteresis which must always go on the right column, with the
+	 * limit it relates to being in the left column on the same line.
+	 */
+	for (i = slot = 0; i < limit_count; i++, slot++) {
+		if (!(slot & 1)) {
+			if (slot)
+				printf("\n%*s", label_size + 10, "");
+			printf("(");
+		} else {
+			printf(", ");
+		}
+		printf(fmt, limits[i].name, limits[i].value,
+			     limits[i].unit);
+
+		/* If needed, skip one slot to avoid hyst on first column */
+		skip = i + 2 < limit_count && limits[i + 2].name == hyst_str &&
+		       !(slot & 1);
+
+		if (((slot + skip) & 1) || i == limit_count - 1) {
+			printf(")");
+			if (alarm_count && !alarms_printed) {
+				print_alarms(alarms, alarm_count,
+					     (slot & 1) ? 0 : 16);
+				alarms_printed = 1;
+			}
+		}
+		slot += skip;
+	}
+	if (alarm_count && !alarms_printed)
+		print_alarms(alarms, alarm_count, 32);
+}
+
+/*
+ * Get sensor limit information.
+ * *num_limits and *num_alarms must be initialized by the caller.
+ */
+static void get_sensor_limit_data(const sensors_chip_name *name,
+				  const sensors_feature *feature,
+				  const struct sensor_subfeature_list *sfl,
+				  struct sensor_subfeature_data *limits,
+				  int max_limits,
+				  int *num_limits,
+				  struct sensor_subfeature_data *alarms,
+				  int max_alarms,
+				  int *num_alarms)
+{
+	const sensors_subfeature *sf;
+
+	for (; sfl->subfeature >= 0; sfl++) {
+		sf = sensors_get_subfeature(name, feature, sfl->subfeature);
+		if (sf) {
+			if (sfl->alarm) {
+				/*
+				 * Only queue alarm subfeatures if the alarm
+				 * is active, and don't store the alarm value
+				 * (it is implied to be active if queued).
+				 */
+				if (get_value(name, sf)) {
+					if (*num_alarms >= max_alarms) {
+						fprintf(stderr,
+							"Not enough %s buffers (%d)\n",
+							"alarm", max_alarms);
+					} else {
+						alarms[*num_alarms].name = sfl->name;
+						(*num_alarms)++;
+					}
+				}
+			} else {
+				/*
+				 * Always queue limit subfeatures with their value.
+				 */
+				if (*num_limits >= max_limits) {
+					fprintf(stderr,
+						"Not enough %s buffers (%d)\n",
+						"limit", max_limits);
+				} else {
+					limits[*num_limits].value = get_value(name, sf);
+					limits[*num_limits].name = sfl->name;
+					(*num_limits)++;
+				}
+			}
+			if (sfl->exists) {
+				get_sensor_limit_data(name, feature, sfl->exists,
+						      limits, max_limits, num_limits,
+						      alarms, max_alarms, num_alarms);
+			}
+		}
+	}
+}
+
+static const struct sensor_subfeature_list temp_max_sensors[] = {
+	{ SENSORS_SUBFEATURE_TEMP_MAX_HYST, NULL, 0, hyst_str },
+	{ -1, NULL, 0, NULL }
+};
+
+static const struct sensor_subfeature_list temp_crit_sensors[] = {
+	{ SENSORS_SUBFEATURE_TEMP_CRIT_HYST, NULL, 0, hyst_str },
+	{ -1, NULL, 0, NULL }
+};
+
+static const struct sensor_subfeature_list temp_emergency_sensors[] = {
+	{ SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST, NULL, 0,
+	    hyst_str },
+	{ -1, NULL, 0, NULL }
+};
+
+static const struct sensor_subfeature_list temp_sensors[] = {
+	{ SENSORS_SUBFEATURE_TEMP_ALARM, NULL, 1, NULL },
+	{ SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM, NULL, 1, "LCRIT" },
+	{ SENSORS_SUBFEATURE_TEMP_MIN_ALARM, NULL, 1, "LOW" },
+	{ SENSORS_SUBFEATURE_TEMP_MAX_ALARM, NULL, 1, "HIGH" },
+	{ SENSORS_SUBFEATURE_TEMP_CRIT_ALARM, NULL, 1, "CRIT" },
+	{ SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM, NULL, 1, "EMERGENCY" },
+	{ SENSORS_SUBFEATURE_TEMP_MIN, NULL, 0, "low" },
+	{ SENSORS_SUBFEATURE_TEMP_MAX, temp_max_sensors, 0, "high" },
+	{ SENSORS_SUBFEATURE_TEMP_LCRIT, NULL, 0, "crit low" },
+	{ SENSORS_SUBFEATURE_TEMP_CRIT, temp_crit_sensors, 0, "crit" },
+	{ SENSORS_SUBFEATURE_TEMP_EMERGENCY, temp_emergency_sensors, 0,
+	    "emerg" },
+	{ -1, NULL, 0, NULL }
+};
+
+static void print_chip_temp(const sensors_chip_name *name,
+			    const sensors_feature *feature,
+			    int label_size)
+{
+	struct sensor_subfeature_data sensors[8];
+	struct sensor_subfeature_data alarms[5];
+	int sensor_count, alarm_count;
+	const sensors_subfeature *sf;
+	double val;
+	char *label;
+	int i;
+
+	if (!(label = sensors_get_label(name, feature))) {
+		fprintf(stderr, "ERROR: Can't get label of feature %s!\n",
+			feature->name);
+		return;
+	}
+	print_label(label, label_size);
+	free(label);
+
+	sf = sensors_get_subfeature(name, feature,
+				    SENSORS_SUBFEATURE_TEMP_FAULT);
+	if (sf && get_value(name, sf)) {
+		printf("   FAULT  ");
+	} else {
+		sf = sensors_get_subfeature(name, feature,
+					    SENSORS_SUBFEATURE_TEMP_INPUT);
+		if (sf && get_input_value(name, sf, &val) == 0) {
+			get_input_value(name, sf, &val);
+			if (fahrenheit)
+				val = deg_ctof(val);
+			printf("%+6.1f%s  ", val, degstr);
+		} else
+			printf("     N/A  ");
+	}
+
+	sensor_count = alarm_count = 0;
+	get_sensor_limit_data(name, feature, temp_sensors,
+			      sensors, ARRAY_SIZE(sensors), &sensor_count,
+			      alarms, ARRAY_SIZE(alarms), &alarm_count);
+
+	for (i = 0; i < sensor_count; i++) {
+		if (fahrenheit)
+			sensors[i].value = deg_ctof(sensors[i].value);
+		sensors[i].unit = degstr;
+	}
+
+	print_limits(sensors, sensor_count, alarms, alarm_count, label_size,
+		     "%-4s = %+5.1f%s");
+
+	/* print out temperature sensor info */
+	sf = sensors_get_subfeature(name, feature,
+				    SENSORS_SUBFEATURE_TEMP_TYPE);
+	if (sf) {
+		int sens = (int)get_value(name, sf);
+
+		/* older kernels / drivers sometimes report a beta value for
+		   thermistors */
+		if (sens > 1000)
+			sens = 4;
+
+		printf("  sensor = %s", sens == 0 ? "disabled" :
+		       sens == 1 ? "diode" :
+		       sens == 2 ? "transistor" :
+		       sens == 3 ? "thermal diode" :
+		       sens == 4 ? "thermistor" :
+		       sens == 5 ? "AMD AMDSI" :
+		       sens == 6 ? "Intel PECI" : "unknown");
+	}
+	printf("\n");
+}
+
+static const struct sensor_subfeature_list voltage_sensors[] = {
+	{ SENSORS_SUBFEATURE_IN_ALARM, NULL, 1, NULL },
+	{ SENSORS_SUBFEATURE_IN_LCRIT_ALARM, NULL, 1, "LCRIT" },
+	{ SENSORS_SUBFEATURE_IN_MIN_ALARM, NULL, 1, "MIN" },
+	{ SENSORS_SUBFEATURE_IN_MAX_ALARM, NULL, 1, "MAX" },
+	{ SENSORS_SUBFEATURE_IN_CRIT_ALARM, NULL, 1, "CRIT" },
+	{ SENSORS_SUBFEATURE_IN_LCRIT, NULL, 0, "crit min" },
+	{ SENSORS_SUBFEATURE_IN_MIN, NULL, 0, "min" },
+	{ SENSORS_SUBFEATURE_IN_MAX, NULL, 0, "max" },
+	{ SENSORS_SUBFEATURE_IN_CRIT, NULL, 0, "crit max" },
+	{ -1, NULL, 0, NULL }
+};
+
+static void print_chip_in(const sensors_chip_name *name,
+			  const sensors_feature *feature,
+			  int label_size)
+{
+	const sensors_subfeature *sf;
+	char *label;
+	struct sensor_subfeature_data sensors[4];
+	struct sensor_subfeature_data alarms[4];
+	int sensor_count, alarm_count;
+	double val;
+
+	if (!(label = sensors_get_label(name, feature))) {
+		fprintf(stderr, "ERROR: Can't get label of feature %s!\n",
+			feature->name);
+		return;
+	}
+	print_label(label, label_size);
+	free(label);
+
+	sf = sensors_get_subfeature(name, feature,
+				    SENSORS_SUBFEATURE_IN_INPUT);
+	if (sf && get_input_value(name, sf, &val) == 0)
+		printf("%+6.2f V  ", val);
+	else
+		printf("     N/A  ");
+
+	sensor_count = alarm_count = 0;
+	get_sensor_limit_data(name, feature, voltage_sensors,
+			      sensors, ARRAY_SIZE(sensors), &sensor_count,
+			      alarms, ARRAY_SIZE(alarms), &alarm_count);
+
+	print_limits(sensors, sensor_count, alarms, alarm_count, label_size,
+		     "%s = %+6.2f V");
+
+	printf("\n");
+}
+
+static void print_chip_fan(const sensors_chip_name *name,
+			   const sensors_feature *feature,
+			   int label_size)
+{
+	const sensors_subfeature *sf, *sfmin, *sfdiv;
+	double val;
+	char *label;
+
+	if (!(label = sensors_get_label(name, feature))) {
+		fprintf(stderr, "ERROR: Can't get label of feature %s!\n",
+			feature->name);
+		return;
+	}
+	print_label(label, label_size);
+	free(label);
+
+	sf = sensors_get_subfeature(name, feature,
+				    SENSORS_SUBFEATURE_FAN_FAULT);
+	if (sf && get_value(name, sf))
+		printf("   FAULT");
+	else {
+		sf = sensors_get_subfeature(name, feature,
+					    SENSORS_SUBFEATURE_FAN_INPUT);
+		if (sf && get_input_value(name, sf, &val) == 0)
+			printf("%4.0f RPM", val);
+		else
+			printf("     N/A");
+	}
+
+	sfmin = sensors_get_subfeature(name, feature,
+				       SENSORS_SUBFEATURE_FAN_MIN);
+	sfdiv = sensors_get_subfeature(name, feature,
+				       SENSORS_SUBFEATURE_FAN_DIV);
+	if (sfmin && sfdiv)
+		printf("  (min = %4.0f RPM, div = %1.0f)",
+		       get_value(name, sfmin),
+		       get_value(name, sfdiv));
+	else if (sfmin)
+		printf("  (min = %4.0f RPM)",
+		       get_value(name, sfmin));
+	else if (sfdiv)
+		printf("  (div = %1.0f)",
+		       get_value(name, sfdiv));
+
+	sf = sensors_get_subfeature(name, feature,
+				    SENSORS_SUBFEATURE_FAN_ALARM);
+	if (sf && get_value(name, sf)) {
+		printf("  ALARM");
+	}
+
+	printf("\n");
+}
+
+struct scale_table {
+	double upper_bound;
+	const char *unit;
+};
+
+static void scale_value(double *value, const char **prefixstr)
+{
+	double abs_value = fabs(*value);
+	double divisor = 1e-9;
+	static struct scale_table prefix_scales[] = {
+		{1e-6, "n"},
+		{1e-3, "u"},
+		{1,    "m"},
+		{1e3,   ""},
+		{1e6,  "k"},
+		{1e9,  "M"},
+		{0,    "G"}, /* no upper bound */
+	};
+	struct scale_table *scale = prefix_scales;
+
+	if (abs_value == 0) {
+		*prefixstr = "";
+		return;
+	}
+
+	while (scale->upper_bound && abs_value > scale->upper_bound) {
+		divisor = scale->upper_bound;
+		scale++;
+	}
+
+	*value /= divisor;
+	*prefixstr = scale->unit;
+}
+
+static const struct sensor_subfeature_list power_common_sensors[] = {
+	{ SENSORS_SUBFEATURE_POWER_ALARM, NULL, 1, NULL },
+	{ SENSORS_SUBFEATURE_POWER_MAX_ALARM, NULL, 1, "MAX" },
+	{ SENSORS_SUBFEATURE_POWER_CRIT_ALARM, NULL, 1, "CRIT" },
+	{ SENSORS_SUBFEATURE_POWER_CAP_ALARM, NULL, 1, "CAP" },
+	{ SENSORS_SUBFEATURE_POWER_MAX, NULL, 0, "max" },
+	{ SENSORS_SUBFEATURE_POWER_CRIT, NULL, 0, "crit" },
+	{ SENSORS_SUBFEATURE_POWER_CAP, NULL, 0, "cap" },
+	{ -1, NULL, 0, NULL }
+};
+
+static const struct sensor_subfeature_list power_inst_sensors[] = {
+	{ SENSORS_SUBFEATURE_POWER_INPUT_LOWEST, NULL, 0, "lowest" },
+	{ SENSORS_SUBFEATURE_POWER_INPUT_HIGHEST, NULL, 0, "highest" },
+	{ -1, NULL, 0, NULL }
+};
+
+static const struct sensor_subfeature_list power_avg_sensors[] = {
+	{ SENSORS_SUBFEATURE_POWER_AVERAGE_LOWEST, NULL, 0, "lowest" },
+	{ SENSORS_SUBFEATURE_POWER_AVERAGE_HIGHEST, NULL, 0, "highest" },
+	{ SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL, NULL, 0,
+		"interval" },
+	{ -1, NULL, 0, NULL }
+};
+
+static void print_chip_power(const sensors_chip_name *name,
+			     const sensors_feature *feature,
+			     int label_size)
+{
+	double val;
+	const sensors_subfeature *sf;
+	struct sensor_subfeature_data sensors[6];
+	struct sensor_subfeature_data alarms[3];
+	int sensor_count, alarm_count;
+	char *label;
+	const char *unit;
+	int i;
+
+	if (!(label = sensors_get_label(name, feature))) {
+		fprintf(stderr, "ERROR: Can't get label of feature %s!\n",
+			feature->name);
+		return;
+	}
+	print_label(label, label_size);
+	free(label);
+
+	sensor_count = alarm_count = 0;
+
+	/* Power sensors come in 2 flavors: instantaneous and averaged.
+	   To keep things simple, we assume that each sensor only implements
+	   one flavor. */
+	sf = sensors_get_subfeature(name, feature,
+				    SENSORS_SUBFEATURE_POWER_INPUT);
+	get_sensor_limit_data(name, feature,
+			      sf ? power_inst_sensors : power_avg_sensors,
+			      sensors, ARRAY_SIZE(sensors), &sensor_count,
+			      alarms, ARRAY_SIZE(alarms), &alarm_count);
+	/* Add sensors common to both flavors. */
+	get_sensor_limit_data(name, feature,
+			      power_common_sensors,
+			      sensors, ARRAY_SIZE(sensors), &sensor_count,
+			      alarms, ARRAY_SIZE(alarms), &alarm_count);
+	if (!sf)
+		sf = sensors_get_subfeature(name, feature,
+					    SENSORS_SUBFEATURE_POWER_AVERAGE);
+
+	if (sf && get_input_value(name, sf, &val) == 0) {
+		scale_value(&val, &unit);
+		printf("%6.2f %sW  ", val, unit);
+	} else
+		printf("     N/A  ");
+
+	for (i = 0; i < sensor_count; i++)
+		scale_value(&sensors[i].value, &sensors[i].unit);
+
+	print_limits(sensors, sensor_count, alarms, alarm_count,
+		     label_size, "%s = %6.2f %sW");
+
+	printf("\n");
+}
+
+static void print_chip_energy(const sensors_chip_name *name,
+			      const sensors_feature *feature,
+			      int label_size)
+{
+	double val;
+	const sensors_subfeature *sf;
+	char *label;
+	const char *unit;
+
+	if (!(label = sensors_get_label(name, feature))) {
+		fprintf(stderr, "ERROR: Can't get label of feature %s!\n",
+			feature->name);
+		return;
+	}
+	print_label(label, label_size);
+	free(label);
+
+	sf = sensors_get_subfeature(name, feature,
+				    SENSORS_SUBFEATURE_ENERGY_INPUT);
+	if (sf && get_input_value(name, sf, &val) == 0) {
+		scale_value(&val, &unit);
+		printf("%6.2f %sJ", val, unit);
+	} else
+		printf("     N/A");
+
+	printf("\n");
+}
+
+static void print_chip_vid(const sensors_chip_name *name,
+			   const sensors_feature *feature,
+			   int label_size)
+{
+	char *label;
+	const sensors_subfeature *subfeature;
+	double vid;
+
+	subfeature = sensors_get_subfeature(name, feature,
+					    SENSORS_SUBFEATURE_VID);
+	if (!subfeature)
+		return;
+
+	if ((label = sensors_get_label(name, feature))
+	 && !sensors_get_value(name, subfeature->number, &vid)) {
+		print_label(label, label_size);
+		printf("%+6.3f V\n", vid);
+	}
+	free(label);
+}
+
+static void print_chip_beep_enable(const sensors_chip_name *name,
+				   const sensors_feature *feature,
+				   int label_size)
+{
+	char *label;
+	const sensors_subfeature *subfeature;
+	double beep_enable;
+
+	subfeature = sensors_get_subfeature(name, feature,
+					    SENSORS_SUBFEATURE_BEEP_ENABLE);
+	if (!subfeature)
+		return;
+
+	if ((label = sensors_get_label(name, feature))
+	 && !sensors_get_value(name, subfeature->number, &beep_enable)) {
+		print_label(label, label_size);
+		printf("%s\n", beep_enable ? "enabled" : "disabled");
+	}
+	free(label);
+}
+
+static const struct sensor_subfeature_list current_sensors[] = {
+	{ SENSORS_SUBFEATURE_CURR_ALARM, NULL, 1, NULL },
+	{ SENSORS_SUBFEATURE_CURR_LCRIT_ALARM, NULL, 1, "LCRIT" },
+	{ SENSORS_SUBFEATURE_CURR_MIN_ALARM, NULL, 1, "MIN" },
+	{ SENSORS_SUBFEATURE_CURR_MAX_ALARM, NULL, 1, "MAX" },
+	{ SENSORS_SUBFEATURE_CURR_CRIT_ALARM, NULL, 1, "CRIT" },
+	{ SENSORS_SUBFEATURE_CURR_LCRIT, NULL, 0, "crit min" },
+	{ SENSORS_SUBFEATURE_CURR_MIN, NULL, 0, "min" },
+	{ SENSORS_SUBFEATURE_CURR_MAX, NULL, 0, "max" },
+	{ SENSORS_SUBFEATURE_CURR_CRIT, NULL, 0, "crit max" },
+	{ -1, NULL, 0, NULL }
+};
+
+static void print_chip_curr(const sensors_chip_name *name,
+			    const sensors_feature *feature,
+			    int label_size)
+{
+	const sensors_subfeature *sf;
+	double val;
+	char *label;
+	struct sensor_subfeature_data sensors[4];
+	struct sensor_subfeature_data alarms[4];
+	int sensor_count, alarm_count;
+
+	if (!(label = sensors_get_label(name, feature))) {
+		fprintf(stderr, "ERROR: Can't get label of feature %s!\n",
+			feature->name);
+		return;
+	}
+	print_label(label, label_size);
+	free(label);
+
+	sf = sensors_get_subfeature(name, feature,
+				    SENSORS_SUBFEATURE_CURR_INPUT);
+	if (sf && get_input_value(name, sf, &val) == 0)
+		printf("%+6.2f A  ", val);
+	else
+		printf("     N/A  ");
+
+	sensor_count = alarm_count = 0;
+	get_sensor_limit_data(name, feature, current_sensors,
+			      sensors, ARRAY_SIZE(sensors), &sensor_count,
+			      alarms, ARRAY_SIZE(alarms), &alarm_count);
+
+	print_limits(sensors, sensor_count, alarms, alarm_count, label_size,
+		     "%s = %+6.2f A");
+
+	printf("\n");
+}
+
+static void print_chip_intrusion(const sensors_chip_name *name,
+				 const sensors_feature *feature,
+				 int label_size)
+{
+	char *label;
+	const sensors_subfeature *subfeature;
+	double alarm;
+
+	subfeature = sensors_get_subfeature(name, feature,
+					    SENSORS_SUBFEATURE_INTRUSION_ALARM);
+	if (!subfeature)
+		return;
+
+	if ((label = sensors_get_label(name, feature))
+	 && !sensors_get_value(name, subfeature->number, &alarm)) {
+		print_label(label, label_size);
+		printf("%s\n", alarm ? "ALARM" : "OK");
+	}
+	free(label);
+}
+
+void print_chip(const sensors_chip_name *name)
+{
+	const sensors_feature *feature;
+	int i, label_size;
+
+	label_size = get_label_size(name);
+
+	i = 0;
+	while ((feature = sensors_get_features(name, &i))) {
+		switch (feature->type) {
+		case SENSORS_FEATURE_TEMP:
+			print_chip_temp(name, feature, label_size);
+			break;
+		case SENSORS_FEATURE_IN:
+			print_chip_in(name, feature, label_size);
+			break;
+		case SENSORS_FEATURE_FAN:
+			print_chip_fan(name, feature, label_size);
+			break;
+		case SENSORS_FEATURE_VID:
+			print_chip_vid(name, feature, label_size);
+			break;
+		case SENSORS_FEATURE_BEEP_ENABLE:
+			print_chip_beep_enable(name, feature, label_size);
+			break;
+		case SENSORS_FEATURE_POWER:
+			print_chip_power(name, feature, label_size);
+			break;
+		case SENSORS_FEATURE_ENERGY:
+			print_chip_energy(name, feature, label_size);
+			break;
+		case SENSORS_FEATURE_CURR:
+			print_chip_curr(name, feature, label_size);
+			break;
+		case SENSORS_FEATURE_INTRUSION:
+			print_chip_intrusion(name, feature, label_size);
+			break;
+		default:
+			continue;
+		}
+	}
+}
Index: /lm-sensors/tags/V3-3-0/prog/sensors/main.h
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensors/main.h	(revision 5163)
+++ /lm-sensors/tags/V3-3-0/prog/sensors/main.h	(revision 5163)
@@ -0,0 +1,27 @@
+/*
+    main.h - Part of sensors, a user-space program for hardware monitoring
+    Copyright (C) 2007       Jean Delvare <khali@linux-fr.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.
+
+    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 PROG_SENSORS_MAIN_H
+#define PROG_SENSORS_MAIN_H
+
+extern int fahrenheit;
+extern char degstr[5];
+
+#endif /* PROG_SENSORS_MAIN_H */
Index: /lm-sensors/tags/V3-3-0/prog/sensors/Module.mk
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensors/Module.mk	(revision 5163)
+++ /lm-sensors/tags/V3-3-0/prog/sensors/Module.mk	(revision 5163)
@@ -0,0 +1,61 @@
+#  Module.mk - Makefile for a Linux module for reading sensor data.
+#  Copyright (c) 1998, 1999  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.
+
+# Note that MODULE_DIR (the directory in which this file resides) is a
+# 'simply expanded variable'. That means that its value is substituted
+# verbatim in the rules, until it is redefined. 
+MODULE_DIR := prog/sensors
+PROGSENSORSDIR := $(MODULE_DIR)
+
+PROGSENSORSMAN1DIR := $(MANDIR)/man1
+PROGSENSORSMAN1FILES := $(MODULE_DIR)/sensors.1
+
+# Regrettably, even 'simply expanded variables' will not put their currently
+# defined value verbatim into the command-list of rules...
+PROGSENSORSTARGETS := $(MODULE_DIR)/sensors
+PROGSENSORSSOURCES := $(MODULE_DIR)/main.c $(MODULE_DIR)/chips.c
+
+# Include all dependency files. We use '.rd' to indicate this will create
+# executables.
+INCLUDEFILES += $(PROGSENSORSSOURCES:.c=.rd)
+
+REMOVESENSORSBIN := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(BINDIR)/%,$(PROGSENSORSTARGETS))
+REMOVESENSORSMAN := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(PROGSENSORSMAN1DIR)/%,$(PROGSENSORSMAN1FILES))
+
+LIBICONV := $(shell if /sbin/ldconfig -p | grep -q libiconv\\.so ; then echo \-liconv; else echo; fi)
+
+$(PROGSENSORSTARGETS): $(PROGSENSORSSOURCES:.c=.ro) lib/$(LIBSHBASENAME)
+	$(CC) $(EXLDFLAGS) -o $@ $(PROGSENSORSSOURCES:.c=.ro) $(LIBICONV) -Llib -lsensors
+
+all-prog-sensors: $(PROGSENSORSTARGETS)
+user :: all-prog-sensors
+
+install-prog-sensors: all-prog-sensors
+	$(MKDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(PROGSENSORSMAN1DIR)
+	$(INSTALL) -m 755 $(PROGSENSORSTARGETS) $(DESTDIR)$(BINDIR)
+	$(INSTALL) -m 644 $(PROGSENSORSMAN1FILES) $(DESTDIR)$(PROGSENSORSMAN1DIR)
+user_install :: install-prog-sensors
+
+user_uninstall::
+	$(RM) $(REMOVESENSORSBIN)
+	$(RM) $(REMOVESENSORSMAN)
+
+clean-prog-sensors:
+	$(RM) $(PROGSENSORSDIR)/*.rd $(PROGSENSORSDIR)/*.ro 
+	$(RM) $(PROGSENSORSTARGETS)
+clean :: clean-prog-sensors
Index: /lm-sensors/tags/V3-3-0/prog/sensors/sensors.1
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensors/sensors.1	(revision 4990)
+++ /lm-sensors/tags/V3-3-0/prog/sensors/sensors.1	(revision 4990)
@@ -0,0 +1,93 @@
+.\" Copyright 1999 Frodo Looijaard <frodol@dds.nl>
+.\" Copyright (C) 2007 Jean Delvare <khali@linux-fr.org>
+.\" sensors is distributed under the GPL
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one
+.\" 
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date.  The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein.  The author(s) may not
+.\" have taken the same level of care in the production of this manual,
+.\" which is licensed free of charge, as they might when working
+.\" professionally.
+.\" 
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+.\"
+.TH sensors 1  "October 2007" "lm-sensors 3" "Linux User's Manual"
+.SH NAME
+sensors \- print sensors information
+.SH SYNOPSIS
+.B sensors [
+.I options
+.B ] [
+.I chips
+.B ]
+.br
+.B sensors -s [
+.I chips
+.B ]
+.br
+.B sensors --bus-list
+
+.SH DESCRIPTION
+.B sensors
+is used to show the current readings of all sensor chips.
+.br
+.B sensors -s
+is used to set all limits as specified in the configuration file.
+.br
+.B sensors --bus-list
+is used to generate bus statements suitable for the configuration file.
+
+.SH OPTIONS
+.IP "-c config-file"
+Specify a configuration file. If no file is specified, the libsensors
+default configuration file is used. Use `-c /dev/null' to temporarily
+disable this default configuration file.
+.IP -h
+Print a help text and exit.
+.IP -s
+Evaluate all `set' statements in the configuration file and exit. You must
+be `root' to do this. If this parameter is not specified, no `set' statement
+is evaluated.
+.IP -A
+Do not show the adapter for each chip.
+.IP -u
+Raw output. This mode is only meant for debugging.
+.IP -v
+Print the program version and exit.
+.IP -f
+Print the temperatures in degrees Fahrenheit instead of Celsius.
+.IP --bus-list
+Generate bus statements suitable for using in sensors.conf. Such bus statements
+are only needed if you have several chips sharing the same address on different
+buses of the same type. As bus numbers are usually not guaranteed to be stable
+over reboots, these statements let you refer to each bus by its name rather
+than numbers.
+.SH FILES
+.I /etc/sensors3.conf
+.br
+.I /etc/sensors.conf
+.RS
+The system wide configuration file. See
+.BR sensors.conf (5)
+for further details.
+.RE
+
+.SH SEE ALSO
+sensors.conf(5)
+
+.SH AUTHOR
+Frodo Looijaard and the lm_sensors group
+http://www.lm-sensors.org/
+
+
Index: /lm-sensors/tags/V3-3-0/prog/sensors/chips.h
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensors/chips.h	(revision 5943)
+++ /lm-sensors/tags/V3-3-0/prog/sensors/chips.h	(revision 5943)
@@ -0,0 +1,53 @@
+/*
+    chips.h - Part of sensors, a user-space program for hardware monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>
+    Copyright (C) 2007        Jean Delvare <khali@linux-fr.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.
+
+    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 PROG_SENSORS_CHIPS_H
+#define PROG_SENSORS_CHIPS_H
+
+#include "lib/sensors.h"
+
+/*
+ * Retrieved subfeatures
+ */
+struct sensor_subfeature_data {
+	double value;		/* Subfeature value. Not used for alarms. */
+	const char *name;	/* Subfeature name */
+	const char *unit;	/* Unit to be displayed for this subfeature.
+				   This field is optional. */
+};
+
+/*
+ * Subfeature data structure. Used to create a table of implemented subfeatures
+ * for a given feature.
+ */
+struct sensor_subfeature_list {
+	int subfeature;
+	const struct sensor_subfeature_list *exists;
+				/* Complementary subfeatures to be displayed
+				   if subfeature exists */
+	int alarm;		/* true if this is an alarm */
+	const char *name;	/* subfeature name to be printed */
+};
+
+void print_chip_raw(const sensors_chip_name *name);
+void print_chip(const sensors_chip_name *name);
+
+#endif /* def PROG_SENSORS_CHIPS_H */
Index: /lm-sensors/tags/V3-3-0/prog/sensord/args.h
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensord/args.h	(revision 5732)
+++ /lm-sensors/tags/V3-3-0/prog/sensord/args.h	(revision 5732)
@@ -0,0 +1,30 @@
+#ifndef SENSORD_ARGS_H
+#define SENSORD_ARGS_H
+
+#include <lib/sensors.h>
+
+#define MAX_CHIP_NAMES 32
+
+struct sensord_arguments {
+	int isDaemon;
+	const char *cfgFile;
+	const char *pidFile;
+	const char *rrdFile;
+	const char *cgiDir;
+	int scanTime;
+	int logTime;
+	int rrdTime;
+	int rrdNoAverage;
+	int syslogFacility;
+	int doScan;
+	int doSet;
+	int doCGI;
+	int doLoad;
+	int debug;
+	sensors_chip_name chipNames[MAX_CHIP_NAMES];
+	int numChipNames;
+};
+
+extern struct sensord_arguments sensord_args;
+
+#endif	/* SENSORD_ARGS_H */
Index: /lm-sensors/tags/V3-3-0/prog/sensord/sensord.8
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensord/sensord.8	(revision 5737)
+++ /lm-sensors/tags/V3-3-0/prog/sensord/sensord.8	(revision 5737)
@@ -0,0 +1,389 @@
+.\" Copyright 1999-2002 Merlin Hughes <merlin@merlin.org>
+.\" sensord is distributed under the GPL
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one
+.\" 
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date.  The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein.  The author(s) may not
+.\" have taken the same level of care in the production of this manual,
+.\" which is licensed free of charge, as they might when working
+.\" professionally.
+.\" 
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+.\"
+.TH sensord 8  "October 2007" "lm-sensors 3" "Linux System Administration"
+.SH NAME
+sensord \- Sensor information logging daemon.
+.SH SYNOPSIS
+.B sensord [
+.I options
+.B ] [
+.I chips
+.B ]
+
+.SH DESCRIPTION
+.B Sensord
+is a daemon that can be used to periodically log sensor readings from
+hardware health-monitoring chips to
+.BR syslog (3)
+or a round-robin database (RRD)
+and to alert when a sensor alarm is signalled; for example, if a
+fan fails, a temperature limit is exceeded, etc.
+
+.SH OPTIONS
+.IP "-i, --interval time"
+Specify the interval between scanning for sensor alarms; the default is to
+scan every minute.
+
+The time should be specified as a raw integer (seconds) or with a suffix
+`s' for seconds, `m' for minutes or `h' for hours; for example, the
+default interval is `60' or `1m'.
+
+Specify an interval of zero to suppress scanning explicitly for alarms.
+.IP "-l, --log-interval time"
+Specify the interval between logging all sensor readings; the default is
+to log all readings every half hour.
+
+The time is specified as before; e.g., `30m'.
+
+Specify an interval of zero to suppress logging of regular sensor
+readings.
+.IP "-t, --rrd-interval time"
+Specify the interval between logging all sensor readings to a round-robin
+database; the default is to log all readings every five minutes
+.B if
+a round-robin database is configured.
+
+The time is specified as before; e.g., `5m'.
+.IP "-T, --rrd-no-average"
+Specify that the round-robin database should not be averaged.
+
+.IP "-r, --rrd-file file"
+Specify a round-robin database into which to log all sensor readings;
+e.g., `/var/log/sensord.rrd'. This database will be created if it does
+not exist. By default, no round-robin database is used.
+
+See the section
+.B ROUND ROBIN DATABASES
+below for more details.
+.IP "-c, --config-file file"
+Specify a
+.BR libsensors (3)
+configuration file. If no file is specified, the libsensors default
+configuration file is used.
+
+.IP "-p, --pid-file file"
+Specify what PID file to write; the default is to write the file
+`/var/run/sensord.pid'. You should always specify an absolute path
+here. The file is removed when the daemon exits.
+.IP "-f, --syslog-facility facility"
+Specify the
+.BR syslog (3)
+facility to use when logging sensor readings and alarms; the default is
+to use
+.IR daemon .
+
+Other possible facilities include
+.IR local0
+through
+.IR local7 ,
+and
+.IR user .
+.IP "-g, --rrd-cgi directory"
+Prints out a sample
+.BR rrdcgi (1)
+CGI script that can be used to display graphs of recent sensor information
+in a Web page, and exits. You must specify the world-writable, Web-accessible
+directory where the graphs should be stored; the CGI script assumes that
+this will be accessed under the `/sensord/' directory on the Webserver. See
+the section
+.B ROUND ROBIN DATABASES
+below for more details.
+.IP "-a, --load-average"
+Include the load average in the RRD database. You should
+also specify this flag when you create the CGI script.
+.IP "-d, --debug"
+Prints a small amount of additional debugging information.
+.IP "-h, --help"
+Prints a help message and exits.
+.IP "-v, --version"
+Displays the program version and exits.
+.SH CHIPS
+To restrict the devices that are scanned by this daemon, you may
+optionally specify a list of chip names. By default, all available
+chips are scanned.
+
+A typical chip name would be `w83782d-*' (you may want to escape the
+`*' for your shell) which would scan any W83782D chips on any bus. See
+.BR sensors.conf (5)
+for more details. Another option is to simply not load the sensor
+modules for chips in which you have no interest.
+.SH SIGNALS
+Upon receipt of a SIGTERM (see
+.BR signal (7)
+for details) this daemon should gracefully shut down.
+
+Upon receipt of a SIGHUP, this daemon will rescan the kernel interface
+for chips and features, and reload the libsensors configuration file.
+.SH LOGGING
+All messages from this daemon are logged to
+.BR syslog (3)
+under the program name `sensord' and facility
+.IR daemon ,
+or whatever is specified on the command line.
+
+Regular sensor readings are logged at the level
+.IR info .
+Alarms are logged at the level
+.IR alert .
+Inconsequential status messages are logged at
+the minimum level,
+.IR debug ,
+when debugging is enabled.
+
+You can use an appropriate `/etc/syslog.conf'
+file to direct these messages in a useful manner. See
+.BR syslog.conf (5)
+for full details. Assuming you set the logging facility to local4,
+the following is a sample configuration:
+.IP
+.nf
+# Sample syslog.conf entries
+*.info;...;local4.none;local4.warn  /var/log/messages
+local4.info                        -/var/log/sensors
+local4.alert                        /dev/console
+local4.alert                        *
+.fi
+.PP
+The first line ensures that regular sensor readings do not clutter
+`/var/log/messages'; we first say `local4.none' to eliminate
+informational messages; then `local4.warn' to enable warnings and
+above. The second line says to log all regular sensor readings to
+`/var/log/sensors'; the leading hyphen `-' means that this file
+is not flushed after every message. The final two lines ensure
+that alarms are printed to the system console as well as
+to all connected users (in addition to `/var/log/messages' and
+`/var/log/sensors').
+.SH LOG ROTATION
+On a typical system with a good sensor chip, expect about 2KB per sensor
+reading in the log file. This works out at about 3MB per month. You
+should be rotating your syslog files anyway, but just to be sure you'll
+want to use something like
+.BR logrotate (8)
+or equivalent. You might, for example, want an entry in
+`/etc/logrotate.d/syslog' containing:
+.IP
+.nf
+# Sample logrotate.d entry
+/var/log/sensors {
+    postrotate
+        /usr/sbin/killall -HUP syslogd
+    endscript
+}
+.fi
+.PP
+Note, of course, that you want to restart
+.BR syslogd (8)
+and not
+.BR sensord (8)
+.
+.SH ALARMS
+Alarms generally indicate a critical condition; for example, a fan
+failure or an unacceptable temperature or voltage. However, some
+sensor chips do not support alarms, while others are incorrectly
+configured and may signal alarms incorrectly.
+
+Note that some drivers may lack support for alarm reporting
+even though the chips they support do have alarms. As of Linux 2.6.23,
+many drivers still don't report alarms in a format suitable for
+libsensors 3.
+
+.SH BEEPS
+If you see `(beep)' beside any sensor reading, that just means that
+your system is configured to issue an audio warning from the
+motherboard if an alarm is signalled on that sensor.
+.SH ROUND ROBIN DATABASES
+.BR Sensord (8)
+provides support for storing sensor readings in a round-robin
+database. This may be a useful alternative to the use of
+.BR syslog (3).
+
+Round-robin databases are
+constant-size databases that can be used to store, for example,
+a week's worth of sensor readings. Subsequent readings stored
+in the database will overwrite readings that are over a week
+old. This capability is extremely useful because it allows
+useful information to be stored in an easily-accessible
+manner for a useful length of time, without the burden of
+ever-growing log files.
+
+The
+.BR rrdtool (1)
+utility and its associated library provide the basic framework for
+the round-robin database beneath
+.BR sensord (8).
+In addition, the
+.BR rrdcgi (1)
+and
+.BR rrdgraph (1)
+utilities provide support for generating graphs of these data for
+display in a Web page.
+
+If you wish to use the default configuration of round-robin
+database, which holds one week of sensor readings at five-minute
+intervals, then simply start
+.BR sensord (8)
+and specify where you want the database stored. It will automatically
+be created and configured using these default parameters.
+
+If you wish readings to be stored for a longer period, or want multiple
+readings to be averaged into each database entry, then you must
+manually create and configure the database before starting
+.BR sensord (8).
+Consult the
+.BR rrdcreate (1)
+manual for details. Note that the database must match exactly the
+names and order of sensors read by
+.BR sensord (8).
+It is recommended that you create the default database and then use
+.BR rrdinfo (1)
+to obtain this information, and/or
+.BR rrdtune (1)
+to change it.
+
+After creating the round-robin database, you must then configure
+your Web server to display the sensor information. This assumes that
+you have a Web server preconfigured and functioning on your machine.
+.BR Sensord (8)
+provides a command-line option
+.BR --rrd-cgi
+to generate a basic CGI script to
+display these graphs; you can then customize this script as desired.
+Consult the
+.BR rrdcgi (1)
+manual for details. This CGI script requires a world-writable, Web-accessible
+directory into which to write the graphs that it generates.
+
+An example of how to set up Web-accessible graphs of recent sensor readings
+follows:
+.IP
+.nf
+sensord --log-interval 0 \\
+  --load-average \\
+  --rrd-file /var/log/sensord.rrd
+.fi
+.PP
+Here, we start
+.BR sensord (8)
+and configure it to store readings in a round-robin database; note
+that we disable logging of sensor readings to
+.BR syslog (3),
+and enable logging of the load average.
+.IP
+.nf
+mkdir /var/www/sensord
+chown www-data:staff /var/www/sensord
+chmod a=rwxs /var/www/sensord
+.fi
+.PP
+Here, we create a world-writable, Web-accessible directory in which
+graphs will be stored; we set the ownership and permissions on this
+directory appropriately. You will have to determine the location and
+ownership that is appropriate for your machine.
+.IP
+.nf
+sensord --load-average \\
+  --rrd-file /var/log/sensord.rrd \\
+  --rrd-cgi /var/www/sensord \\
+  > /usr/lib/cgi-bin/sensord.cgi
+chmod a+rx /usr/lib/cgi-bin/sensord.cgi
+.fi
+.PP
+Here, we create
+a CGI script that will display sensor readings from the database.
+You must specify the location of the round-robin database, the
+location of the directory where the images should be stored,
+and whether you want the load average displayed. The
+.BR --rrd-cgi
+command-line parameter causes
+.BR sensord (8)
+to display a suitable CGI script on
+.BR stdout
+and then to exit. You will need to write this script to the CGI
+bin directory of your Web server,
+and edit the script if the image directory you chose is not the
+`/sensord/' directory of your Web server.
+
+Finally, you should be able to view your sensor readings from
+the URL `http://localhost/cgi-bin/sensord.cgi'.
+.SH MODULES
+It is expected that all required sensor modules are loaded prior to
+this daemon being started. This can either be achieved with a system
+specific module loading scheme (e.g., listing the required modules
+in the file `/etc/modules' under Debian) or with explicit
+.BR modprobe (1)
+commands in an init script before loading the daemon.
+
+For example, a `sensord' initialization script might
+contain (among others) the following commands:
+.IP
+.nf
+# Sample init.d scriptlet
+echo -n "Loading AMD756 module: "
+modprobe i2c-amd756 || { echo Fail. ; exit 1 ; }
+echo OK.
+echo -n "Loading W83781D module: "
+modprobe w83781d || { echo Fail. ; exit 1 ; }
+echo OK.
+echo -n "Starting sensord: "
+daemon sensord
+...
+.fi
+.PP
+Ignore the platform-specific shell functions; the general idea
+should be fairly clear.
+.SH ERRORS
+Errors encountered by this daemon are logged to
+.BR syslogd (8)
+after which the daemon will exit.
+.SH BUGS
+Round-robin database support doesn't cope with
+multiple sensor chips having duplicate sensor labels.
+.SH FILES
+.I /etc/sensors3.conf
+.br
+.I /etc/sensors.conf
+.RS
+The system-wide
+.BR libsensors (3)
+configuration file. See
+.BR sensors.conf (5)
+for further details.
+.RE
+.I /etc/syslog.conf
+.RS
+The system-wide
+.BR syslog (3)
+/
+.BR syslogd (8)
+configuration file. See
+.BR syslog.conf (5)
+for further details.
+.RE
+
+.SH SEE ALSO
+sensors.conf(5)
+.SH AUTHORS
+.B Sensord
+was written by Merlin Hughes <merlin@merlin.org>. Basics of round-robin
+databases were misappropriated from Mark D. Studebaker.
Index: /lm-sensors/tags/V3-3-0/prog/sensord/sensord.h
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensord/sensord.h	(revision 5797)
+++ /lm-sensors/tags/V3-3-0/prog/sensord/sensord.h	(revision 5797)
@@ -0,0 +1,89 @@
+/*
+ * sensord
+ *
+ * A daemon that periodically logs sensor information to syslog.
+ *
+ * Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.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.
+ *
+ * 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 "lib/sensors.h"
+
+#define ARRAY_SIZE(arr)	(int)(sizeof(arr) / sizeof((arr)[0]))
+
+extern void sensorLog(int priority, const char *fmt, ...);
+
+/* from args.c */
+
+extern int parseArgs(int argc, char **argv);
+extern int parseChips(int argc, char **argv);
+extern void freeChips(void);
+
+/* from lib.c */
+
+extern int loadLib(const char *cfgPath);
+extern int reloadLib(const char *cfgPath);
+extern int unloadLib(void);
+
+/* from sense.c */
+
+extern int readChips(void);
+extern int scanChips(void);
+extern int setChips(void);
+extern int rrdChips(void);
+
+/* from rrd.c */
+
+extern char rrdBuff[];
+extern int rrdInit(void);
+extern int rrdUpdate(void);
+extern int rrdCGI(void);
+
+/* from chips.c */
+
+#define MAX_DATA 5
+
+typedef const char *(*FormatterFN) (const double values[], int alrm,
+				     int beep);
+
+typedef const char *(*RRDFN) (const double values[]);
+
+typedef enum {
+	DataType_voltage = 0,
+	DataType_rpm,
+	DataType_temperature,
+	DataType_other = -1
+} DataType;
+
+typedef struct {
+	FormatterFN format;
+	RRDFN rrd;
+	DataType type;
+	int alarmNumber;
+	int beepNumber;
+	const sensors_feature *feature;
+	int dataNumbers[MAX_DATA + 1];
+} FeatureDescriptor;
+
+typedef struct {
+	const sensors_chip_name *name;
+	FeatureDescriptor *features;
+} ChipDescriptor;
+
+extern ChipDescriptor * knownChips;
+extern int initKnownChips(void);
+extern void freeKnownChips(void);
Index: /lm-sensors/tags/V3-3-0/prog/sensord/chips.c
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensord/chips.c	(revision 5797)
+++ /lm-sensors/tags/V3-3-0/prog/sensord/chips.c	(revision 5797)
@@ -0,0 +1,412 @@
+/*
+ * sensord
+ *
+ * A daemon that periodically logs sensor information to syslog.
+ *
+ * Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.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.
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "sensord.h"
+
+/* TODO: Temp in C/F */
+
+/** formatters **/
+
+static char buff[4096];
+
+static const char *fmtExtra(int alrm, int beep)
+{
+	if (alrm)
+		sprintf(buff + strlen(buff), " [ALARM]");
+	if (beep)
+		sprintf(buff + strlen(buff), " (beep)");
+	return buff;
+}
+
+static const char *fmtTemps_1(const double values[], int alrm, int beep)
+{
+	sprintf(buff, "%.1f C (limit = %.1f C, hysteresis = %.1f C)",
+		values[0], values[1], values[2]);
+	return fmtExtra(alrm, beep);
+}
+
+static const char *fmtTemps_minmax_1(const double values[], int alrm,
+				     int beep) {
+	sprintf(buff, "%.1f C (min = %.1f C, max = %.1f C)", values[0],
+		values[1], values[2]);
+	return fmtExtra(alrm, beep);
+}
+
+static const char *fmtTemp_only(const double values[], int alrm, int beep)
+{
+	sprintf(buff, "%.1f C", values[0]);
+	return fmtExtra(alrm, beep);
+}
+
+static const char *fmtVolt_2(const double values[], int alrm, int beep)
+{
+	sprintf(buff, "%+.2f V", values[0]);
+	return fmtExtra(alrm, beep);
+}
+
+static const char *fmtVolt_3(const double values[], int alrm, int beep)
+{
+	sprintf(buff, "%+.3f V", values[0]);
+	return fmtExtra(alrm, beep);
+}
+
+static const char *fmtVolts_2(const double values[], int alrm, int beep)
+{
+	sprintf(buff, "%+.2f V (min = %+.2f V, max = %+.2f V)", values[0],
+		values[1], values[2]);
+	return fmtExtra(alrm, beep);
+}
+
+static const char *fmtFans_0(const double values[], int alrm, int beep)
+{
+	sprintf(buff, "%.0f RPM (min = %.0f RPM, div = %.0f)", values[0],
+		values[1], values[2]);
+	return fmtExtra(alrm, beep);
+}
+
+static const char *fmtFans_nodiv_0(const double values[], int alrm, int beep)
+{
+	sprintf(buff, "%.0f RPM (min = %.0f RPM)", values[0], values[1]);
+	return fmtExtra(alrm, beep);
+}
+
+static const char *fmtFan_only(const double values[], int alrm, int beep)
+{
+	sprintf(buff, "%.0f RPM", values[0]);
+	return fmtExtra(alrm, beep);
+}
+
+static const char *fmtSoundAlarm(const double values[], int alrm, int beep)
+{
+	sprintf(buff, "Sound alarm %s",
+		(values[0] < 0.5) ? "disabled" : "enabled");
+	return fmtExtra(alrm, beep);
+}
+
+static const char *rrdF0(const double values[])
+{
+	sprintf(buff, "%.0f", values[0]);
+	return buff;
+}
+
+static const char *rrdF1(const double values[])
+{
+	sprintf(buff, "%.1f", values[0]);
+	return buff;
+}
+
+static const char *rrdF2(const double values[])
+{
+	sprintf(buff, "%.2f", values[0]);
+	return buff;
+}
+
+static const char *rrdF3(const double values[])
+{
+	sprintf(buff, "%.3f", values[0]);
+	return buff;
+}
+
+static void fillChipVoltage(FeatureDescriptor *voltage,
+			    const sensors_chip_name *name,
+			    const sensors_feature *feature)
+{
+	const sensors_subfeature *sf, *sfmin, *sfmax;
+	int pos = 0;
+
+	voltage->rrd = rrdF2;
+	voltage->type = DataType_voltage;
+
+	sf = sensors_get_subfeature(name, feature,
+				    SENSORS_SUBFEATURE_IN_INPUT);
+	if (sf)
+		voltage->dataNumbers[pos++] = sf->number;
+
+	sfmin = sensors_get_subfeature(name, feature,
+				       SENSORS_SUBFEATURE_IN_MIN);
+	sfmax = sensors_get_subfeature(name, feature,
+				       SENSORS_SUBFEATURE_IN_MAX);
+	if (sfmin && sfmax) {
+		voltage->format = fmtVolts_2;
+		voltage->dataNumbers[pos++] = sfmin->number;
+		voltage->dataNumbers[pos++] = sfmax->number;
+	} else {
+		voltage->format = fmtVolt_2;
+	}
+
+	/* terminate the list */
+	voltage->dataNumbers[pos] = -1;
+
+	/* alarm if applicable */
+	if ((sf = sensors_get_subfeature(name, feature,
+					 SENSORS_SUBFEATURE_IN_ALARM)) ||
+	    (sf = sensors_get_subfeature(name, feature,
+					 SENSORS_SUBFEATURE_IN_MIN_ALARM)) ||
+	    (sf = sensors_get_subfeature(name, feature,
+					 SENSORS_SUBFEATURE_IN_MAX_ALARM))) {
+		voltage->alarmNumber = sf->number;
+	} else {
+		voltage->alarmNumber = -1;
+	}
+	/* beep if applicable */
+	if ((sf = sensors_get_subfeature(name, feature,
+					 SENSORS_SUBFEATURE_IN_BEEP))) {
+		voltage->beepNumber = sf->number;
+	} else {
+		voltage->beepNumber = -1;
+	}
+}
+
+static void fillChipTemperature(FeatureDescriptor *temperature,
+				const sensors_chip_name *name,
+				const sensors_feature *feature)
+{
+	const sensors_subfeature *sf, *sfmin, *sfmax, *sfhyst;
+	int pos = 0;
+
+	temperature->rrd = rrdF1;
+	temperature->type = DataType_temperature;
+
+	sf = sensors_get_subfeature(name, feature,
+				    SENSORS_SUBFEATURE_TEMP_INPUT);
+	if (sf)
+		temperature->dataNumbers[pos++] = sf->number;
+
+	sfmin = sensors_get_subfeature(name, feature,
+				       SENSORS_SUBFEATURE_TEMP_MIN);
+	sfmax = sensors_get_subfeature(name, feature,
+				       SENSORS_SUBFEATURE_TEMP_MAX);
+	sfhyst = sensors_get_subfeature(name, feature,
+					SENSORS_SUBFEATURE_TEMP_MAX_HYST);
+	if (sfmin && sfmax) {
+		temperature->format = fmtTemps_minmax_1;
+		temperature->dataNumbers[pos++] = sfmin->number;
+		temperature->dataNumbers[pos++] = sfmax->number;
+	} else if (sfmax && sfhyst) {
+		temperature->format = fmtTemps_1;
+		temperature->dataNumbers[pos++] = sfmax->number;
+		temperature->dataNumbers[pos++] = sfhyst->number;
+	} else {
+		temperature->format = fmtTemp_only;
+	}
+
+	/* terminate the list */
+	temperature->dataNumbers[pos] = -1;
+
+	/* alarm if applicable */
+	if ((sf = sensors_get_subfeature(name, feature,
+					 SENSORS_SUBFEATURE_TEMP_ALARM)) ||
+	    (sf = sensors_get_subfeature(name, feature,
+					 SENSORS_SUBFEATURE_TEMP_MAX_ALARM))) {
+		temperature->alarmNumber = sf->number;
+	} else {
+		temperature->alarmNumber = -1;
+	}
+	/* beep if applicable */
+	if ((sf = sensors_get_subfeature(name, feature,
+					 SENSORS_SUBFEATURE_TEMP_BEEP))) {
+		temperature->beepNumber = sf->number;
+	} else {
+		temperature->beepNumber = -1;
+	}
+}
+
+static void fillChipFan(FeatureDescriptor *fan,
+			const sensors_chip_name *name,
+			const sensors_feature *feature)
+{
+	const sensors_subfeature *sf, *sfmin, *sfdiv;
+	int pos = 0;
+
+	fan->rrd = rrdF0;
+	fan->type = DataType_rpm;
+
+	sf = sensors_get_subfeature(name, feature,
+				    SENSORS_SUBFEATURE_FAN_INPUT);
+	if (sf)
+		fan->dataNumbers[pos++] = sf->number;
+
+	sfmin = sensors_get_subfeature(name, feature,
+				       SENSORS_SUBFEATURE_FAN_MIN);
+	if (sfmin) {
+		fan->dataNumbers[pos++] = sfmin->number;
+		sfdiv = sensors_get_subfeature(name, feature,
+					       SENSORS_SUBFEATURE_FAN_DIV);
+		if (sfdiv) {
+			fan->format = fmtFans_0;
+			fan->dataNumbers[pos++] = sfdiv->number;
+		} else {
+			fan->format = fmtFans_nodiv_0;
+		}
+	} else {
+		fan->format = fmtFan_only;
+	}
+
+	/* terminate the list */
+	fan->dataNumbers[pos] = -1;
+
+	/* alarm if applicable */
+	sf = sensors_get_subfeature(name, feature,
+				    SENSORS_SUBFEATURE_FAN_ALARM);
+	if (sf) {
+		fan->alarmNumber = sf->number;
+	} else {
+		fan->alarmNumber = -1;
+	}
+	/* beep if applicable */
+	sf = sensors_get_subfeature(name, feature,
+				    SENSORS_SUBFEATURE_FAN_BEEP);
+	if (sf) {
+		fan->beepNumber = sf->number;
+	} else {
+		fan->beepNumber = -1;
+	}
+}
+
+static void fillChipVid(FeatureDescriptor *vid,
+			const sensors_chip_name *name,
+			const sensors_feature *feature)
+{
+	const sensors_subfeature *sub;
+
+	sub = sensors_get_subfeature(name, feature, SENSORS_SUBFEATURE_VID);
+	if (!sub)
+		return;
+
+	vid->format = fmtVolt_3;
+	vid->rrd = rrdF3;
+	vid->type = DataType_voltage;
+	vid->alarmNumber = -1;
+	vid->beepNumber = -1;
+	vid->dataNumbers[0] = sub->number;
+	vid->dataNumbers[1] = -1;
+}
+
+static void fillChipBeepEnable(FeatureDescriptor *beepen,
+			       const sensors_chip_name *name,
+			       const sensors_feature *feature)
+{
+	const sensors_subfeature *sub;
+
+	sub = sensors_get_subfeature(name, feature,
+				     SENSORS_SUBFEATURE_BEEP_ENABLE);
+	if (!sub)
+		return;
+
+	beepen->format = fmtSoundAlarm;
+	beepen->rrd = rrdF0;
+	beepen->type = DataType_other;
+	beepen->alarmNumber = -1;
+	beepen->beepNumber = -1;
+	beepen->dataNumbers[0] = sub->number;
+	beepen->dataNumbers[1] = -1;
+}
+
+static FeatureDescriptor * generateChipFeatures(const sensors_chip_name *chip)
+{
+	int nr, count = 1;
+	const sensors_feature *sensor;
+	FeatureDescriptor *features;
+
+	/* How many main features do we have? */
+	nr = 0;
+	while ((sensor = sensors_get_features(chip, &nr)))
+		count++;
+
+	/* Allocate the memory we need */
+	features = calloc(count, sizeof(FeatureDescriptor));
+	if (!features)
+		return NULL;
+
+	/* Fill in the data structures */
+	count = 0;
+	nr = 0;
+	while ((sensor = sensors_get_features(chip, &nr))) {
+		switch (sensor->type) {
+		case SENSORS_FEATURE_TEMP:
+			fillChipTemperature(&features[count], chip, sensor);
+			break;
+		case SENSORS_FEATURE_IN:
+			fillChipVoltage(&features[count], chip, sensor);
+			break;
+		case SENSORS_FEATURE_FAN:
+			fillChipFan(&features[count], chip, sensor);
+			break;
+		case SENSORS_FEATURE_VID:
+			fillChipVid(&features[count], chip, sensor);
+			break;
+		case SENSORS_FEATURE_BEEP_ENABLE:
+			fillChipBeepEnable(&features[count], chip, sensor);
+			break;
+		default:
+			continue;
+		}
+
+		features[count].feature = sensor;
+		count++;
+	}
+
+	return features;
+}
+
+ChipDescriptor * knownChips;
+
+int initKnownChips(void)
+{
+	int nr, count = 1;
+	const sensors_chip_name *name;
+
+	/* How many chips do we have? */
+	nr = 0;
+	while ((name = sensors_get_detected_chips(NULL, &nr)))
+		count++;
+
+	/* Allocate the memory we need */
+	knownChips = calloc(count, sizeof(ChipDescriptor));
+	if (!knownChips)
+		return 1;
+
+	/* Fill in the data structures */
+	count = 0;
+	nr = 0;
+	while ((name = sensors_get_detected_chips(NULL, &nr))) {
+		knownChips[count].name = name;
+		if ((knownChips[count].features = generateChipFeatures(name)))
+			count++;
+	}
+
+	return 0;
+}
+
+void freeKnownChips(void)
+{
+	int index0;
+
+	for (index0 = 0; knownChips[index0].features; index0++)
+		free(knownChips[index0].features);
+	free(knownChips);
+}
Index: /lm-sensors/tags/V3-3-0/prog/sensord/lib.c
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensord/lib.c	(revision 5722)
+++ /lm-sensors/tags/V3-3-0/prog/sensord/lib.c	(revision 5722)
@@ -0,0 +1,105 @@
+/*
+ * sensord
+ *
+ * A daemon that periodically logs sensor information to syslog.
+ *
+ * Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.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.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "sensord.h"
+#include "lib/error.h"
+
+static int loadConfig(const char *cfgPath, int reload)
+{
+	int ret;
+ 	FILE *fp;
+
+ 	/* Load default configuration. */
+ 	if (!cfgPath) {
+ 		if (reload) {
+			sensorLog(LOG_INFO, "configuration reloading");
+			sensors_cleanup();
+		}
+
+ 		ret = sensors_init(NULL);
+ 		if (ret) {
+ 			sensorLog(LOG_ERR, "Error loading default"
+ 				  " configuration file: %s",
+ 				  sensors_strerror(ret));
+ 			return -1;
+  		}
+ 		return 0;
+ 	}
+
+ 	fp = fopen(cfgPath, "r");
+ 	if (!fp) {
+ 		sensorLog(LOG_ERR, "Error opening config file %s: %s",
+ 			  strerror(errno));
+ 		return -1;
+ 	}
+
+	if (reload) {
+		sensorLog(LOG_INFO, "configuration reloading");
+		sensors_cleanup();
+	}
+ 	ret = sensors_init(fp);
+ 	if (ret) {
+ 		sensorLog(LOG_ERR, "Error loading sensors configuration file"
+			  " %s: %s", cfgPath, sensors_strerror(ret));
+ 		fclose(fp);
+ 		return -1;
+ 	}
+ 	fclose(fp);
+
+ 	return 0;
+}
+
+int loadLib(const char *cfgPath)
+{
+	int ret;
+	ret = loadConfig(cfgPath, 0);
+	if (!ret)
+		ret = initKnownChips();
+	return ret;
+}
+
+int reloadLib(const char *cfgPath)
+{
+	int ret;
+	freeKnownChips();
+	ret = loadConfig(cfgPath, 1);
+	if (!ret)
+		ret = initKnownChips();
+	return ret;
+}
+
+int unloadLib(void)
+{
+	freeKnownChips();
+	sensors_cleanup();
+	return 0;
+}
Index: /lm-sensors/tags/V3-3-0/prog/sensord/rrd.c
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensord/rrd.c	(revision 5835)
+++ /lm-sensors/tags/V3-3-0/prog/sensord/rrd.c	(revision 5835)
@@ -0,0 +1,507 @@
+/*
+ * sensord
+ *
+ * A daemon that periodically logs sensor information to syslog.
+ *
+ * Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.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.
+ *
+ * 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.
+ */
+
+/*
+ * RRD is the Round Robin Database
+ *
+ * Get this package from:
+ *   http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/
+ *
+ * For compilation you need the development libraries;
+ * for execution you need the runtime libraries; for
+ * Web-based graph access you need the binary rrdtool.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <rrd.h>
+
+#include "args.h"
+#include "sensord.h"
+
+#define DO_READ 0
+#define DO_SCAN 1
+#define DO_SET 2
+#define DO_RRD 3
+
+/* one integer */
+#define STEP_BUFF 64
+/* RRA:AVERAGE:0.5:1:12345 */
+#define RRA_BUFF 256
+/* weak: max sensors for RRD .. TODO: fix */
+#define MAX_RRD_SENSORS 256
+/* weak: max raw label length .. TODO: fix */
+#define RAW_LABEL_LENGTH 32
+/* DS:label:GAUGE:900:U:U | :3000 .. TODO: fix */
+#define RRD_BUFF 64
+
+char rrdBuff[MAX_RRD_SENSORS * RRD_BUFF + 1];
+static char rrdLabels[MAX_RRD_SENSORS][RAW_LABEL_LENGTH + 1];
+
+#define LOADAVG "loadavg"
+#define LOAD_AVERAGE "Load Average"
+
+typedef void (*FeatureFN) (void *data, const char *rawLabel, const char *label,
+			   const FeatureDescriptor *feature);
+
+static char rrdNextChar(char c)
+{
+	if (c == '9') {
+		return 'A';
+	} else if (c == 'Z') {
+		return 'a';
+	} else if (c == 'z') {
+		return 0;
+	} else {
+		return c + 1;
+	}
+}
+
+static void rrdCheckLabel(const char *rawLabel, int index0)
+{
+	char *buffer = rrdLabels[index0];
+	int i, j, okay;
+
+	i = 0;
+	/* contrain raw label to [A-Za-z0-9_] */
+	while ((i < RAW_LABEL_LENGTH) && rawLabel[i]) {
+		char c = rawLabel[i];
+		if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))
+		    || ((c >= '0') && (c <= '9')) || (c == '_')) {
+			buffer[i] = c;
+		} else {
+			buffer[i] = '_';
+		}
+		++i;
+	}
+	buffer[i] = '\0';
+
+	j = 0;
+	okay = (i > 0);
+
+	/* locate duplicates */
+	while (okay && (j < index0))
+		okay = strcmp(rrdLabels[j++], buffer);
+
+	/* uniquify duplicate labels with _? or _?? */
+	while (!okay) {
+		if (!buffer[i]) {
+			if (i > RAW_LABEL_LENGTH - 3)
+				i = RAW_LABEL_LENGTH - 3;
+			buffer[i] = '_';
+			buffer[i + 1] = '0';
+			buffer[i + 2] = '\0';
+		} else if (!buffer[i + 2]) {
+			if (!(buffer[i + 1] = rrdNextChar(buffer[i + 1]))) {
+				buffer[i + 1] = '0';
+				buffer[i + 2] = '0';
+				buffer[i + 3] = '\0';
+			}
+		} else {
+			if (!(buffer[i + 2] = rrdNextChar(buffer[i + 2]))) {
+				buffer[i + 1] = rrdNextChar(buffer[i + 1]);
+				buffer[i + 2] = '0';
+			}
+		}
+		j = 0;
+		okay = 1;
+		while (okay && (j < index0))
+			okay = strcmp(rrdLabels[j ++], buffer);
+	}
+}
+
+/* Returns the number of features processed, or -1 on error */
+static int _applyToFeatures(FeatureFN fn, void *data,
+			    const sensors_chip_name *chip,
+			    const ChipDescriptor *desc,
+			    int labelOffset)
+{
+	int i;
+	const FeatureDescriptor *features = desc->features;
+	const FeatureDescriptor *feature;
+	const char *rawLabel;
+	char *label;
+
+	for (i = 0; labelOffset + i < MAX_RRD_SENSORS && features[i].format; ++i) {
+		feature = features + i;
+		rawLabel = feature->feature->name;
+
+		label = sensors_get_label(chip, feature->feature);
+		if (!label) {
+			sensorLog(LOG_ERR, "Error getting sensor label: %s/%s",
+				  chip->prefix, rawLabel);
+			return -1;
+		}
+
+		rrdCheckLabel(rawLabel, labelOffset + i);
+		fn(data, rrdLabels[labelOffset + i], label, feature);
+		free(label);
+	}
+	return i;
+}
+
+static ChipDescriptor *lookup_known_chips(const sensors_chip_name *chip)
+{
+	int i;
+
+	/* Trick: we compare addresses here. We know it works
+	 * because both pointers were returned by
+	 * sensors_get_detected_chips(), so they refer to
+	 * libsensors internal structures, which do not move.
+	 */
+	for (i = 0; knownChips[i].features; i++) {
+		if (knownChips[i].name == chip) {
+			return &knownChips[i];
+		}
+	}
+	return NULL;
+}
+
+static int applyToFeatures(FeatureFN fn, void *data)
+{
+	int i, i_detected, ret, labelOffset = 0;
+	const sensors_chip_name *chip, *chip_arg;
+	ChipDescriptor *desc;
+
+	for (i = 0; i < sensord_args.numChipNames; i++) {
+		chip_arg = &sensord_args.chipNames[i];
+		i_detected = 0;
+		while ((chip = sensors_get_detected_chips(chip_arg,
+							  &i_detected))) {
+			desc = lookup_known_chips(chip);
+			if (!desc)
+				continue;
+
+			ret = _applyToFeatures(fn, data, chip, desc, labelOffset);
+			if (ret < 0)
+				return ret;
+			labelOffset += ret;
+		}
+	}
+	return 0;
+}
+
+struct ds {
+	int num;
+	const char **argv;
+};
+
+static void rrdGetSensors_DS(void *_data, const char *rawLabel,
+			     const char *label,
+			     const FeatureDescriptor *feature)
+{
+	(void) label; /* no warning */
+	if (!feature || feature->rrd) {
+		struct ds *data = _data;
+		char *ptr = rrdBuff + data->num * RRD_BUFF;
+		const char *min, *max;
+		data->argv[data->num ++] = ptr;
+
+		/* arbitrary sanity limits */
+		switch (feature ? feature->type : DataType_other) {
+		case DataType_voltage:
+			min="-25";
+			max="25";
+			break;
+		case DataType_rpm:
+			min = "0";
+			max = "12000";
+			break;
+		case DataType_temperature:
+			min = "-100";
+			max = "250";
+			break;
+		default:
+			min = max = "U";
+			break;
+		}
+
+		/*
+		 * number of seconds downtime during which average be used
+		 * instead of unknown
+		 */
+		sprintf(ptr, "DS:%s:GAUGE:%d:%s:%s", rawLabel, 5 *
+			sensord_args.rrdTime, min, max);
+	}
+}
+
+static int rrdGetSensors(const char **argv)
+{
+	int ret = 0;
+	struct ds data = { 0, argv};
+	ret = applyToFeatures(rrdGetSensors_DS, &data);
+	if (!ret && sensord_args.doLoad)
+		rrdGetSensors_DS(&data, LOADAVG, LOAD_AVERAGE, NULL);
+	return ret ? -1 : data.num;
+}
+
+int rrdInit(void)
+{
+	int ret;
+	struct stat sb;
+	char stepBuff[STEP_BUFF], rraBuff[RRA_BUFF];
+	int argc = 4, num;
+	const char *argv[6 + MAX_RRD_SENSORS] = {
+		"sensord", sensord_args.rrdFile, "-s", stepBuff
+	};
+
+	sensorLog(LOG_DEBUG, "sensor RRD init");
+
+	/* Create RRD if it does not exist. */
+	if (stat(sensord_args.rrdFile, &sb)) {
+		if (errno != ENOENT) {
+			sensorLog(LOG_ERR, "Could not stat rrd file: %s\n",
+				  sensord_args.rrdFile);
+			return -1;
+		}
+		sensorLog(LOG_INFO, "Creating round robin database");
+
+		num = rrdGetSensors(argv + argc);
+		if (num < 1) {
+			sensorLog(LOG_ERR, "Error creating RRD: %s: %s",
+				  sensord_args.rrdFile, "No sensors detected");
+			return -1;
+		}
+
+		sprintf(stepBuff, "%d", sensord_args.rrdTime);
+		sprintf(rraBuff, "RRA:%s:%f:%d:%d",
+			sensord_args.rrdNoAverage ? "LAST" :"AVERAGE",
+			0.5, 1, 7 * 24 * 60 * 60 / sensord_args.rrdTime);
+
+		argc += num;
+		argv[argc++] = rraBuff;
+		argv[argc] = NULL;
+
+		ret = rrd_create(argc, (char**) argv);
+		if (ret == -1) {
+			sensorLog(LOG_ERR, "Error creating RRD file: %s: %s",
+				  sensord_args.rrdFile, rrd_get_error());
+			return -1;
+		}
+	}
+
+	sensorLog(LOG_DEBUG, "sensor RRD initialized");
+	return 0;
+}
+
+#define RRDCGI "/usr/bin/rrdcgi"
+#define WWWDIR "/sensord"
+
+struct gr {
+	DataType type;
+	const char *h2;
+	const char *image;
+	const char *title;
+	const char *axisTitle;
+	const char *axisDefn;
+	const char *options;
+	int loadAvg;
+};
+
+static void rrdCGI_DEF(void *_data, const char *rawLabel, const char *label,
+		       const FeatureDescriptor *feature)
+{
+	struct gr *data = _data;
+	(void) label; /* no warning */
+	if (!feature || (feature->rrd && (feature->type == data->type)))
+		printf("\n\tDEF:%s=%s:%s:AVERAGE", rawLabel,
+		       sensord_args.rrdFile, rawLabel);
+}
+
+/*
+ * Compute an arbitrary color based on the sensor label. This is preferred
+ * over a random value because this guarantees that daily and weekly charts
+ * will use the same colors.
+ */
+static int rrdCGI_color(const char *label)
+{
+	unsigned long color = 0, brightness;
+	const char *c;
+
+	for (c = label; *c; c++) {
+		color = (color << 6) + (color >> (*c & 7));
+		color ^= (*c) * 0x401;
+	}
+	color &= 0xffffff;
+	/* Adjust very light colors */
+	brightness = (color & 0xff) + ((color >> 8) & 0xff) + (color >> 16);
+	if (brightness > 672)
+		color &= 0x7f7f7f;
+	/* Adjust very dark colors */
+	else if (brightness < 96)
+		color |= 0x808080;
+	return color;
+}
+
+static void rrdCGI_LINE(void *_data, const char *rawLabel, const char *label,
+			const FeatureDescriptor *feature)
+{
+	struct gr *data = _data;
+	if (!feature || (feature->rrd && (feature->type == data->type)))
+		printf("\n\tLINE2:%s#%.6x:\"%s\"", rawLabel,
+		       rrdCGI_color(label), label);
+}
+
+static struct gr graphs[] = {
+	{
+		DataType_temperature,
+		"Daily Temperature Summary",
+		"daily-temperature",
+		"Temperature",
+		"Temperature (C)",
+		"HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
+		"-s -1d -l 0",
+		1
+	}, {
+		DataType_rpm,
+		"Daily Fan Speed Summary",
+		"daily-rpm",
+		"Fan Speed",
+		"Speed (RPM)",
+		"HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
+		"-s -1d -l 0 -X 0",
+		0
+	}, {
+		DataType_voltage,
+		"Daily Voltage Summary",
+		"daily-voltage",
+		"Power Supply",
+		"Voltage (V)",
+		"HOUR:1:HOUR:3:HOUR:3:0:%b %d %H:00",
+		"-s -1d --alt-autoscale",
+		0
+	}, {
+		DataType_temperature,
+		"Weekly Temperature Summary",
+		"weekly-temperature",
+		"Temperature",
+		"Temperature (C)",
+		"HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
+		"-s -1w -l 0",
+		1
+	}, {
+		DataType_rpm,
+		"Weekly Fan Speed Summary",
+		"weekly-rpm",
+		"Fan Speed",
+		"Speed (RPM)",
+		"HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
+		"-s -1w -l 0 -X 0",
+		0
+	}, {
+		DataType_voltage,
+		"Weekly Voltage Summary",
+		"weekly-voltage",
+		"Power Supply",
+		"Voltage (V)",
+		"HOUR:6:DAY:1:DAY:1:86400:%a %b %d",
+		"-s -1w --alt-autoscale",
+		0
+	}
+};
+
+int rrdUpdate(void)
+{
+	int ret = rrdChips ();
+
+	if (!ret && sensord_args.doLoad) {
+		FILE *loadavg;
+		if (!(loadavg = fopen("/proc/loadavg", "r"))) {
+			sensorLog(LOG_ERR,
+				  "Error opening `/proc/loadavg': %s",
+				  strerror(errno));
+			ret = 1;
+		} else {
+			float value;
+			if (fscanf(loadavg, "%f", &value) != 1) {
+				sensorLog(LOG_ERR,
+					  "Error reading load average");
+				ret = 2;
+			} else {
+				sprintf(rrdBuff + strlen(rrdBuff), ":%f",
+					value);
+			}
+			fclose(loadavg);
+		}
+	}
+	if (!ret) {
+		const char *argv[] = {
+			"sensord", sensord_args.rrdFile, rrdBuff, NULL
+		};
+		if ((ret = rrd_update(3, (char **) /* WEAK */ argv))) {
+			sensorLog(LOG_ERR, "Error updating RRD file: %s: %s",
+				  sensord_args.rrdFile, rrd_get_error());
+		}
+	}
+	sensorLog(LOG_DEBUG, "sensor rrd updated");
+
+	return ret;
+}
+
+int rrdCGI(void)
+{
+	int ret = 0, i;
+
+	printf("#!" RRDCGI "\n\n<html>\n"
+	       "<head>\n<title>sensord</title>\n</head>\n"
+	       "<body>\n<h1>sensord</h1>\n");
+
+	for (i = 0; i < ARRAY_SIZE(graphs); i++) {
+		struct gr *graph = &graphs[i];
+
+		printf("<h2>%s</h2>\n", graph->h2);
+		printf("<p>\n<RRD::GRAPH %s/%s.png\n\t--imginfo '"
+		       "<img src=" WWWDIR "/%%s width=%%lu height=%%lu>'"
+		       "\n\t-a PNG\n\t-h 200 -w 800\n",
+		       sensord_args.cgiDir, graph->image);
+
+		printf("\t--lazy\n\t-v '%s'\n\t-t '%s'\n\t-x '%s'\n\t%s",
+		       graph->axisTitle, graph->title, graph->axisDefn,
+		       graph->options);
+		if (!ret)
+			ret = applyToFeatures(rrdCGI_DEF, graph);
+		if (!ret && sensord_args.doLoad && graph->loadAvg)
+			rrdCGI_DEF(graph, LOADAVG, LOAD_AVERAGE, NULL);
+		if (!ret)
+			ret = applyToFeatures(rrdCGI_LINE, graph);
+		if (!ret && sensord_args.doLoad && graph->loadAvg)
+			rrdCGI_LINE(graph, LOADAVG, LOAD_AVERAGE, NULL);
+		printf (">\n</p>\n");
+	}
+	printf("<p>\n<small><b>sensord</b> by "
+	       "<a href=\"mailto:merlin@merlin.org\">Merlin Hughes</a>"
+	       ", all credit to the "
+	       "<a href=\"http://www.lm-sensors.org/\">lm_sensors</a> "
+	       "crew.</small>\n</p>\n");
+
+	printf("</body>\n</html>\n");
+
+	return ret;
+}
Index: /lm-sensors/tags/V3-3-0/prog/sensord/Module.mk
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensord/Module.mk	(revision 5163)
+++ /lm-sensors/tags/V3-3-0/prog/sensord/Module.mk	(revision 5163)
@@ -0,0 +1,63 @@
+#  Module.mk - Makefile for a Linux module for reading sensor data.
+#  Copyright (c) 1998, 1999  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.
+
+# The round robin database (RRD) development headers and libraries are
+# REQUIRED. Get this package from:
+#   http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/
+
+# Note that MODULE_DIR (the directory in which this file resides) is a
+# 'simply expanded variable'. That means that its value is substituted
+# verbatim in the rules, until it is redefined. 
+MODULE_DIR := prog/sensord
+PROGSENSORDDIR := $(MODULE_DIR)
+
+PROGSENSORDMAN8DIR := $(MANDIR)/man8
+PROGSENSORDMAN8FILES := $(MODULE_DIR)/sensord.8
+
+# Regrettably, even 'simply expanded variables' will not put their currently
+# defined value verbatim into the command-list of rules...
+PROGSENSORDTARGETS := $(MODULE_DIR)/sensord
+PROGSENSORDSOURCES := $(MODULE_DIR)/args.c $(MODULE_DIR)/chips.c $(MODULE_DIR)/lib.c $(MODULE_DIR)/rrd.c $(MODULE_DIR)/sense.c $(MODULE_DIR)/sensord.c
+
+# Include all dependency files. We use '.rd' to indicate this will create
+# executables.
+INCLUDEFILES += $(PROGSENSORDSOURCES:.c=.rd)
+
+REMOVESENSORDBIN := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(SBINDIR)/%,$(PROGSENSORDTARGETS))
+REMOVESENSORDMAN := $(patsubst $(MODULE_DIR)/%,$(DESTDIR)$(PROGSENSORDMAN8DIR)/%,$(PROGSENSORDMAN8FILES))
+
+$(PROGSENSORDTARGETS): $(PROGSENSORDSOURCES:.c=.ro) lib/$(LIBSHBASENAME)
+	$(CC) $(EXLDFLAGS) -o $@ $(PROGSENSORDSOURCES:.c=.ro) -Llib -lsensors -lrrd
+
+all-prog-sensord: $(PROGSENSORDTARGETS)
+user :: all-prog-sensord
+
+install-prog-sensord: all-prog-sensord
+	$(MKDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(PROGSENSORDMAN8DIR)
+	$(INSTALL) -m 755 $(PROGSENSORDTARGETS) $(DESTDIR)$(SBINDIR)
+	$(INSTALL) -m 644 $(PROGSENSORDMAN8FILES) $(DESTDIR)$(PROGSENSORDMAN8DIR)
+user_install :: install-prog-sensord
+
+user_uninstall::
+	$(RM) $(REMOVESENSORDBIN)
+	$(RM) $(REMOVESENSORDMAN)
+
+clean-prog-sensord:
+	$(RM) $(PROGSENSORDDIR)/*.rd $(PROGSENSORDDIR)/*.ro 
+	$(RM) $(PROGSENSORDTARGETS)
+clean :: clean-prog-sensord
Index: /lm-sensors/tags/V3-3-0/prog/sensord/README
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensord/README	(revision 1955)
+++ /lm-sensors/tags/V3-3-0/prog/sensord/README	(revision 1955)
@@ -0,0 +1,9 @@
+Daemon that periodically logs sensor readings to syslog or a round-robin
+database, and warns of sensor alarms.
+
+Compile this program by making lm_sensors with the following argument:
+  make PROG_EXTRA:=sensord
+
+The RRD (Round Robin Database) development headers and libraries are
+REQUIRED. Get this package from:
+  http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/
Index: /lm-sensors/tags/V3-3-0/prog/sensord/args.c
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensord/args.c	(revision 5740)
+++ /lm-sensors/tags/V3-3-0/prog/sensord/args.c	(revision 5740)
@@ -0,0 +1,274 @@
+/*
+ * sensord
+ *
+ * A daemon that periodically logs sensor information to syslog.
+ *
+ * Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.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.
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <syslog.h>
+
+#include "args.h"
+#include "sensord.h"
+#include "lib/error.h"
+#include "version.h"
+
+struct sensord_arguments sensord_args = {
+ 	.pidFile = "/var/run/sensord.pid",
+ 	.scanTime = 60,
+ 	.logTime = 30 * 60,
+ 	.rrdTime = 5 * 60,
+ 	.syslogFacility = LOG_DAEMON,
+};
+
+static int parseTime(char *arg)
+{
+	char *end;
+	int value = strtoul(arg, &end, 10);
+	if ((end > arg) && (*end == 's')) {
+		++ end;
+	} else if ((end > arg) && (*end == 'm')) {
+		value *= 60;
+		++ end;
+	} else if ((end > arg) && (*end == 'h')) {
+		value *= 60 * 60;
+		++ end;
+	}
+	if ((end == arg) || *end) {
+		fprintf(stderr, "Error parsing time value `%s'.\n", arg);
+		return -1;
+	}
+	return value;
+}
+
+static struct {
+	const char *name;
+	int id;
+} facilities[] = {
+	{ "local0", LOG_LOCAL0 },
+	{ "local1", LOG_LOCAL1 },
+	{ "local2", LOG_LOCAL2 },
+	{ "local3", LOG_LOCAL3 },
+	{ "local4", LOG_LOCAL4 },
+	{ "local5", LOG_LOCAL5 },
+	{ "local6", LOG_LOCAL6 },
+	{ "local7", LOG_LOCAL7 },
+	{ "daemon", LOG_DAEMON },
+	{ "user", LOG_USER },
+	{ NULL, 0 }
+};
+
+static int parseFacility(char *arg)
+{
+	int i = 0;
+	while (facilities[i].name && strcasecmp(arg, facilities[i].name))
+		++ i;
+	if (!facilities[i].name) {
+		fprintf(stderr, "Error parsing facility value `%s'.\n", arg);
+		return -1;
+	}
+	return facilities[i].id;
+}
+
+static const char *daemonSyntax =
+	"  -i, --interval <time>     -- interval between scanning alarms (default 60s)\n"
+	"  -l, --log-interval <time> -- interval between logging sensors (default 30m)\n"
+	"  -t, --rrd-interval <time> -- interval between updating RRD file (default 5m)\n"
+	"  -T, --rrd-no-average      -- switch RRD in non-average mode\n"
+	"  -r, --rrd-file <file>     -- RRD file (default <none>)\n"
+	"  -c, --config-file <file>  -- configuration file\n"
+	"  -p, --pid-file <file>     -- PID file (default /var/run/sensord.pid)\n"
+	"  -f, --syslog-facility <f> -- syslog facility to use (default local4)\n"
+	"  -g, --rrd-cgi <img-dir>   -- output an RRD CGI script and exit\n"
+	"  -a, --load-average        -- include load average in RRD file\n"
+	"  -d, --debug               -- display some debug information\n"
+	"  -v, --version             -- display version and exit\n"
+	"  -h, --help                -- display help and exit\n"
+	"\n"
+	"Specify a value of 0 for any interval to disable that operation;\n"
+	"for example, specify --log-interval 0 to only scan for alarms."
+	"\n"
+	"Specify the filename `-' to read the config file from stdin.\n"
+	"\n"
+	"If no chips are specified, all chip info will be printed.\n"
+	"\n"
+	"If unspecified, no RRD (round robin database) is used. If specified and the\n"
+	"file does not exist, it will be created. For RRD updates to be successful,\n"
+	"the RRD file configuration must EXACTLY match the sensors that are used. If\n"
+	"your configuration changes, delete the old RRD file and restart sensord.\n";
+
+static const char *shortOptions = "i:l:t:Tf:r:c:p:advhg:";
+
+static const struct option longOptions[] = {
+	{ "interval", required_argument, NULL, 'i' },
+	{ "log-interval", required_argument, NULL, 'l' },
+	{ "rrd-interval", required_argument, NULL, 't' },
+	{ "rrd-no-average", no_argument, NULL, 'T' },
+	{ "syslog-facility", required_argument, NULL, 'f' },
+	{ "rrd-file", required_argument, NULL, 'r' },
+	{ "config-file", required_argument, NULL, 'c' },
+	{ "pid-file", required_argument, NULL, 'p' },
+	{ "rrd-cgi", required_argument, NULL, 'g' },
+	{ "load-average", no_argument, NULL, 'a' },
+	{ "debug", no_argument, NULL, 'd' },
+	{ "version", no_argument, NULL, 'v' },
+	{ "help", no_argument, NULL, 'h' },
+	{ NULL, 0, NULL, 0 }
+};
+
+int parseArgs(int argc, char **argv)
+{
+	int c;
+
+	sensord_args.isDaemon = (argv[0][strlen (argv[0]) - 1] == 'd');
+	if (!sensord_args.isDaemon) {
+		fprintf(stderr, "Sensord no longer runs as an commandline"
+			" application.\n");
+		return -1;
+  	}
+
+	while ((c = getopt_long(argc, argv, shortOptions, longOptions, NULL))
+	       != EOF) {
+		switch(c) {
+		case 'i':
+			if ((sensord_args.scanTime = parseTime(optarg)) < 0)
+				return -1;
+			break;
+		case 'l':
+			if ((sensord_args.logTime = parseTime(optarg)) < 0)
+				return -1;
+			break;
+		case 't':
+			if ((sensord_args.rrdTime = parseTime(optarg)) < 0)
+				return -1;
+			break;
+		case 'T':
+			sensord_args.rrdNoAverage = 1;
+			break;
+		case 'f':
+			sensord_args.syslogFacility = parseFacility(optarg);
+			if (sensord_args.syslogFacility < 0)
+				return -1;
+			break;
+		case 'a':
+			sensord_args.doLoad = 1;
+			break;
+		case 'c':
+			sensord_args.cfgFile = optarg;
+			break;
+		case 'p':
+			sensord_args.pidFile = optarg;
+			break;
+		case 'r':
+			sensord_args.rrdFile = optarg;
+			break;
+		case 'd':
+			sensord_args.debug = 1;
+			break;
+		case 'g':
+			sensord_args.doCGI = 1;
+			sensord_args.cgiDir = optarg;
+			break;
+		case 'v':
+			printf("sensord version %s\n", LM_VERSION);
+			exit(EXIT_SUCCESS);
+		case 'h':
+			printf("Syntax: %s {options} {chips}\n%s", argv[0],
+			       daemonSyntax);
+			exit(EXIT_SUCCESS);
+		case ':':
+		case '?':
+			printf("Try `%s --help' for more information.\n",
+			       argv[0]);
+			return -1;
+		default:
+			fprintf(stderr,
+				"Internal error while parsing options.\n");
+			return -1;
+		}
+	}
+
+	if (sensord_args.doCGI && !sensord_args.rrdFile) {
+		fprintf(stderr,
+			"Error: Incompatible --rrd-cgi without --rrd-file.\n");
+		return -1;
+	}
+
+	if (sensord_args.rrdFile && !sensord_args.rrdTime) {
+		fprintf(stderr,
+			"Error: Incompatible --rrd-file without --rrd-interval.\n");
+		return -1;
+	}
+
+	if (!sensord_args.logTime && !sensord_args.scanTime &&
+	    !sensord_args.rrdFile) {
+		fprintf(stderr,
+			"Error: No logging, alarm or RRD scanning.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+int parseChips(int argc, char **argv)
+{
+	int i, n = argc - optind, err;
+
+	if (n == 0) {
+		sensord_args.chipNames[0].prefix =
+			SENSORS_CHIP_NAME_PREFIX_ANY;
+		sensord_args.chipNames[0].bus.type = SENSORS_BUS_TYPE_ANY;
+		sensord_args.chipNames[0].bus.nr = SENSORS_BUS_NR_ANY;
+		sensord_args.chipNames[0].addr = SENSORS_CHIP_NAME_ADDR_ANY;
+		sensord_args.numChipNames = 1;
+
+		return 0;
+	}
+
+	if (n > MAX_CHIP_NAMES) {
+		fprintf(stderr, "Too many chip names.\n");
+		return -1;
+	}
+	for (i = 0; i < n; ++i) {
+		char *arg = argv[optind + i];
+
+		err = sensors_parse_chip_name(arg, sensord_args.chipNames + i);
+		if (err) {
+			fprintf(stderr,	"Invalid chip name `%s': %s\n", arg,
+				sensors_strerror(err));
+			for (i--; i >= 0; i--)
+				sensors_free_chip_name(sensord_args.chipNames + i);
+			return -1;
+		}
+	}
+	sensord_args.numChipNames = n;
+
+	return 0;
+}
+
+void freeChips()
+{
+	int i;
+
+	for (i = 0; i < sensord_args.numChipNames; i++)
+		sensors_free_chip_name(sensord_args.chipNames + i);
+}
Index: /lm-sensors/tags/V3-3-0/prog/sensord/sense.c
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensord/sense.c	(revision 5797)
+++ /lm-sensors/tags/V3-3-0/prog/sensord/sense.c	(revision 5797)
@@ -0,0 +1,285 @@
+/*
+ * sensord
+ *
+ * A daemon that periodically logs sensor information to syslog.
+ *
+ * Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.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.
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "args.h"
+#include "sensord.h"
+#include "lib/error.h"
+
+#define DO_READ 0
+#define DO_SCAN 1
+#define DO_SET 2
+#define DO_RRD 3
+
+static const char *chipName(const sensors_chip_name *chip)
+{
+	static char buffer[256];
+	if (sensors_snprintf_chip_name(buffer, 256, chip) < 0)
+		return NULL;
+	return buffer;
+}
+
+static int idChip(const sensors_chip_name *chip)
+{
+	const char *name, *adapter;
+
+	name = chipName(chip);
+	if (!name) {
+		sensorLog(LOG_ERR, "Error getting chip name");
+		return -1;
+	}
+
+	sensorLog(LOG_INFO, "Chip: %s", name);
+
+	adapter = sensors_get_adapter_name(&chip->bus);
+	if (!adapter)
+		sensorLog(LOG_INFO, "Error getting adapter name");
+	else
+		sensorLog(LOG_INFO, "Adapter: %s", adapter);
+
+	return 0;
+}
+
+static int get_flag(const sensors_chip_name *chip, int num)
+{
+	double val;
+	int ret;
+
+	if (num == -1)
+		return 0;
+
+	ret = sensors_get_value(chip, num, &val);
+	if (ret) {
+		sensorLog(LOG_ERR, "Error getting sensor data: %s/#%d: %s",
+			  chip->prefix, num, sensors_strerror(ret));
+		return -1;
+	}
+
+	return (int) (val + 0.5);
+}
+
+static int get_features(const sensors_chip_name *chip,
+			const FeatureDescriptor *feature, int action,
+			char *label, int alrm, int beep)
+{
+	int i, ret;
+	double val[MAX_DATA];
+
+	for (i = 0; feature->dataNumbers[i] >= 0; i++) {
+		ret = sensors_get_value(chip, feature->dataNumbers[i],
+					val + i);
+		if (ret) {
+			sensorLog(LOG_ERR,
+				  "Error getting sensor data: %s/#%d: %s",
+				  chip->prefix, feature->dataNumbers[i],
+				  sensors_strerror(ret));
+			return -1;
+		}
+	}
+
+	if (action == DO_RRD) {
+		if (feature->rrd) {
+			const char *rrded = feature->rrd(val);
+
+			/* FIXME: Jean's review comment:
+			 * sprintf would me more efficient.
+			 */
+			strcat(strcat (rrdBuff, ":"), rrded ? rrded : "U");
+		}
+	} else {
+		const char *formatted = feature->format(val, alrm, beep);
+
+		if (!formatted) {
+			sensorLog(LOG_ERR, "Error formatting sensor data");
+			return -1;
+		}
+
+		if (action == DO_READ) {
+			sensorLog(LOG_INFO, "  %s: %s", label, formatted);
+		} else {
+			sensorLog(LOG_ALERT, "Sensor alarm: Chip %s: %s: %s",
+				  chipName(chip), label, formatted);
+		}
+	}
+	return 0;
+}
+
+static int do_features(const sensors_chip_name *chip,
+		       const FeatureDescriptor *feature, int action)
+{
+	char *label;
+	int alrm, beep;
+
+	label = sensors_get_label(chip, feature->feature);
+	if (!label) {
+		sensorLog(LOG_ERR, "Error getting sensor label: %s/%s",
+			  chip->prefix, feature->feature->name);
+		return -1;
+	}
+
+	alrm = get_flag(chip, feature->alarmNumber);
+	if (alrm == -1)
+		return -1;
+	else if (action == DO_SCAN && !alrm)
+		return 0;
+
+	beep = get_flag(chip, feature->beepNumber);
+	if (beep == -1)
+		return -1;
+
+	return get_features(chip, feature, action, label, alrm, beep);
+}
+
+static int doKnownChip(const sensors_chip_name *chip,
+		       const ChipDescriptor *descriptor, int action)
+{
+	const FeatureDescriptor *features = descriptor->features;
+	int i, ret = 0;
+
+	if (action == DO_READ) {
+		ret = idChip(chip);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; features[i].format; i++) {
+		ret = do_features(chip, features + i, action);
+		if (ret == -1)
+			break;
+	}
+
+	return ret;
+}
+
+static int setChip(const sensors_chip_name *chip)
+{
+	int ret = 0;
+	if ((ret = idChip(chip))) {
+		sensorLog(LOG_ERR, "Error identifying chip: %s",
+			  chip->prefix);
+	} else if ((ret = sensors_do_chip_sets(chip))) {
+		sensorLog(LOG_ERR, "Error performing chip sets: %s: %s",
+			  chip->prefix, sensors_strerror(ret));
+		ret = 50;
+	} else {
+		sensorLog(LOG_INFO, "Set.");
+	}
+	return ret;
+}
+
+static int doChip(const sensors_chip_name *chip, int action)
+{
+	int ret = 0;
+	if (action == DO_SET) {
+		ret = setChip(chip);
+	} else {
+		int index0, chipindex = -1;
+		for (index0 = 0; knownChips[index0].features; ++index0) {
+			/*
+			 * Trick: we compare addresses here. We know it works
+			 * because both pointers were returned by
+			 * sensors_get_detected_chips(), so they refer to
+			 * libsensors internal structures, which do not move.
+			 */
+			if (knownChips[index0].name == chip) {
+				chipindex = index0;
+				break;
+			}
+		}
+
+		if (chipindex >= 0) {
+			ret = doKnownChip(chip, &knownChips[chipindex],
+					  action);
+		}
+	}
+	return ret;
+}
+
+static int doChips(int action)
+{
+	const sensors_chip_name *chip, *chip_arg;
+	int i, j, ret = 0;
+
+	for (j = 0; j < sensord_args.numChipNames; j++) {
+		chip_arg = &sensord_args.chipNames[j];
+		i = 0;
+		while ((chip = sensors_get_detected_chips(chip_arg, &i))) {
+			ret = doChip(chip, action);
+			if (ret)
+				return ret;
+		}
+	}
+	return ret;
+}
+
+int readChips(void)
+{
+	int ret = 0;
+
+	sensorLog(LOG_DEBUG, "sensor read started");
+	ret = doChips(DO_READ);
+	sensorLog(LOG_DEBUG, "sensor read finished");
+
+	return ret;
+}
+
+int scanChips(void)
+{
+	int ret = 0;
+
+	sensorLog(LOG_DEBUG, "sensor sweep started");
+	ret = doChips(DO_SCAN);
+	sensorLog(LOG_DEBUG, "sensor sweep finished");
+
+	return ret;
+}
+
+int setChips(void)
+{
+	int ret = 0;
+
+	sensorLog(LOG_DEBUG, "sensor set started");
+	ret = doChips(DO_SET);
+	sensorLog(LOG_DEBUG, "sensor set finished");
+
+	return ret;
+}
+
+/* TODO: loadavg entry */
+
+int rrdChips(void)
+{
+	int ret = 0;
+
+	strcpy(rrdBuff, "N");
+
+	sensorLog(LOG_DEBUG, "sensor rrd started");
+	ret = doChips(DO_RRD);
+	sensorLog(LOG_DEBUG, "sensor rrd finished");
+
+	return ret;
+}
Index: /lm-sensors/tags/V3-3-0/prog/sensord/sensord.c
===================================================================
--- /lm-sensors/tags/V3-3-0/prog/sensord/sensord.c	(revision 5740)
+++ /lm-sensors/tags/V3-3-0/prog/sensord/sensord.c	(revision 5740)
@@ -0,0 +1,265 @@
+/*
+ * sensord
+ *
+ * A daemon that periodically logs sensor information to syslog.
+ *
+ * Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.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.
+ *
+ * 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 <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "args.h"
+#include "sensord.h"
+
+static int logOpened = 0;
+
+static volatile sig_atomic_t done = 0;
+static volatile sig_atomic_t reload = 0;
+
+#define LOG_BUFFER 4096
+
+#include <stdarg.h>
+
+void sensorLog(int priority, const char *fmt, ...)
+{
+	static char buffer[1 + LOG_BUFFER];
+	va_list ap;
+	va_start(ap, fmt);
+	vsnprintf(buffer, LOG_BUFFER, fmt, ap);
+	buffer[LOG_BUFFER] = '\0';
+	va_end(ap);
+	if (sensord_args.debug || (priority < LOG_DEBUG)) {
+		if (logOpened) {
+			syslog(priority, "%s", buffer);
+		} else {
+			fprintf(stderr, "%s\n", buffer);
+			fflush(stderr);
+		}
+	}
+}
+
+static void signalHandler(int sig)
+{
+	switch (sig) {
+	case SIGTERM:
+		done = 1;
+		break;
+	case SIGHUP:
+		reload = 1;
+		break;
+	}
+}
+
+static int sensord(void)
+{
+	int ret = 0;
+	int scanValue = 0, logValue = 0;
+	/*
+	 * First RRD update at next RRD timeslot to prevent failures due
+	 * one timeslot updated twice on restart for example.
+	 */
+	int rrdValue = sensord_args.rrdTime - time(NULL) %
+		sensord_args.rrdTime;
+
+	sensorLog(LOG_INFO, "sensord started");
+
+	while (!done) {
+		if (reload) {
+			ret = reloadLib(sensord_args.cfgFile);
+			if (ret)
+				sensorLog(LOG_NOTICE, "configuration reload"
+					  " error");
+			reload = 0;
+		}
+		if (sensord_args.scanTime && (scanValue <= 0)) {
+			if ((ret = scanChips()))
+				sensorLog(LOG_NOTICE,
+					  "sensor scan error (%d)", ret);
+			scanValue += sensord_args.scanTime;
+		}
+		if (sensord_args.logTime && (logValue <= 0)) {
+			if ((ret = readChips()))
+				sensorLog(LOG_NOTICE,
+					  "sensor read error (%d)", ret);
+			logValue += sensord_args.logTime;
+		}
+		if (sensord_args.rrdTime && sensord_args.rrdFile &&
+		    (rrdValue <= 0)) {
+			if ((ret = rrdUpdate()))
+				sensorLog(LOG_NOTICE,
+					  "rrd update error (%d)", ret);
+			/*
+			 * The amount of time to wait is computed using the
+			 * same method as in RRD instead of simply adding the
+			 * interval.
+			 */
+			rrdValue = sensord_args.rrdTime - time(NULL) %
+				sensord_args.rrdTime;
+		}
+		if (!done) {
+			int a = sensord_args.logTime ? logValue : INT_MAX;
+			int b = sensord_args.scanTime ? scanValue : INT_MAX;
+			int c = (sensord_args.rrdTime && sensord_args.rrdFile)
+				? rrdValue : INT_MAX;
+			int sleepTime = (a < b) ? ((a < c) ? a : c) :
+				((b < c) ? b : c);
+			sleep(sleepTime);
+			scanValue -= sleepTime;
+			logValue -= sleepTime;
+			rrdValue -= sleepTime;
+		}
+	}
+
+	sensorLog(LOG_INFO, "sensord stopped");
+
+	return ret;
+}
+
+static void openLog(void)
+{
+	openlog("sensord", 0, sensord_args.syslogFacility);
+	logOpened = 1;
+}
+
+static void install_sighandler(void)
+{
+	struct sigaction new;
+	int ret;
+
+	memset(&new, 0, sizeof(struct sigaction));
+	new.sa_handler = signalHandler;
+	sigemptyset(&new.sa_mask);
+	new.sa_flags = SA_RESTART;
+
+	ret = sigaction(SIGTERM, &new, NULL);
+	if (ret == -1) {
+		fprintf(stderr, "Could not set sighandler for SIGTERM: %s\n",
+			strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	ret = sigaction(SIGHUP, &new, NULL);
+	if (ret == -1) {
+		fprintf(stderr, "Could not set sighandler for SIGHUP: %s\n",
+			strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void daemonize(void)
+{
+	int pid;
+	struct stat fileStat;
+	FILE *file;
+
+	if (chdir("/") < 0) {
+		perror("chdir()");
+		exit(EXIT_FAILURE);
+	}
+
+	if (!(stat(sensord_args.pidFile, &fileStat)) &&
+	    ((!S_ISREG(fileStat.st_mode)) || (fileStat.st_size > 11))) {
+		fprintf(stderr,
+			"Error: PID file `%s' already exists and looks suspicious.\n",
+			sensord_args.pidFile);
+		exit(EXIT_FAILURE);
+	}
+
+	if (!(file = fopen(sensord_args.pidFile, "w"))) {
+		fprintf(stderr, "fopen(\"%s\"): %s\n", sensord_args.pidFile,
+			strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	install_sighandler();
+
+	if ((pid = fork()) == -1) {
+		perror("fork()");
+		exit(EXIT_FAILURE);
+	} else if (pid != 0) {
+		fprintf(file, "%d\n", pid);
+		fclose(file);
+		unloadLib();
+		exit(EXIT_SUCCESS);
+	}
+
+	if (setsid() < 0) {
+		perror("setsid()");
+		exit(EXIT_FAILURE);
+	}
+
+	fclose(file);
+	close(STDIN_FILENO);
+	close(STDOUT_FILENO);
+	close(STDERR_FILENO);
+}
+
+static void undaemonize(void)
+{
+	unlink(sensord_args.pidFile);
+	closelog();
+}
+
+int main(int argc, char **argv)
+{
+	int ret = 0;
+
+	if (parseArgs(argc, argv) ||
+	    parseChips(argc, argv))
+		exit(EXIT_FAILURE);
+
+	if (loadLib(sensord_args.cfgFile)) {
+		freeChips();
+		exit(EXIT_FAILURE);
+	}
+
+	if (!sensord_args.doCGI)
+		openLog();
+
+	if (sensord_args.rrdFile) {
+		ret = rrdInit();
+		if (ret) {
+			freeChips();
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	if (sensord_args.doCGI) {
+		ret = rrdCGI();
+	} else {
+		daemonize();
+		ret = sensord();
+		undaemonize();
+	}
+
+	freeChips();
+	if (unloadLib())
+		exit(EXIT_FAILURE);
+
+	return ret;
+}
Index: /lm-sensors/tags/V3-3-0/CHANGES
===================================================================
--- /lm-sensors/tags/V3-3-0/CHANGES	(revision 5949)
+++ /lm-sensors/tags/V3-3-0/CHANGES	(revision 5949)
@@ -0,0 +1,362 @@
+lm-sensors CHANGES file
+-----------------------
+
+3.3.0 (2011-03-28)
+  Makefile: Check for bison and flex
+  libsensors: Add support for intrusion detection (#2370)
+              Compute MAX_SUBFEATURES at run-time (#2378)
+              Compute MAX_*_SENSOR_TYPES at build time (#2378)
+	      Add support for new sysfs attributes (#2379)
+              Add support for humidity sensors
+              Limit symbol visibility
+  sensors.conf.default: Add a section for the W83627THF
+                        Add a section for the SCH5127
+                        Add a section for the W83627UHG
+                        Add a section for the W83795G/ADG
+                        Add a section for the F81865F
+  sensors: Display 3 decimal places in raw output
+           Add support for intrusion detection (#2370)
+           Fix automatic unit selection of 0 value
+           Added support for additional sensor attributes (#2379)
+  sensors-detect: Improve LM90 and W83L771 detection
+                  Fix error seen if I2C bus numbers are not sequential
+                  Add detection of SMSC EMC1423
+                  Add detection of the Analog Devices ADT7410
+                  Add detection of the National Semiconductor LM94
+                  Add detection of the ITE IT8728F
+                  Implement universal detection for coretemp (#2381)
+                  Add detection of Maxim MAX6639
+                  Add detection of SMSC EMC1023, EMC1043, EMC1053, and EMC1063
+                  Add detection of Nuvoton NCT5571D, NCT5577D and NCT6776F
+                  Add detection of SMSC EMC1002, EMC1033, EMC1046, EMC1047,
+                        EMC1072, EMC1073, EMC1074, EMC1402, and EMC1424
+                  Fixed address ranges checked for SMSC EMC1403 and EMC1404.
+                  Fix detection of SPD EEPROM on DDR3 memory modules
+                  Add detection of AMD family 12h and 14h thermal sensors
+                  Add detection of EMC6D103S
+                  Add detection of National Semiconductor LM75A
+                  Count DMI chassis type 8 as a laptop
+                  Add detection of SMSC SCH5627
+                  Add detection of many Fintek Super-I/O devices
+
+3.2.0 (2010-10-10)
+  libsensors: Increase MAX_SENSORS_PER_TYPE to 24
+              Map individual beep features
+              Change license to LGPL
+  pwmconfig: Sample all fan speeds before returning to full speed (#2380)
+             Wait for fans to return to full speed only once per PWM
+             Drop legacy invocations of cut
+  sensors.conf.default: W83667HG config is similar to W83627EHF/DHG
+  sensord: Fix support of multiple chips (#2377)
+  sensors: Properly handle sensor input read errors
+  sensors-detect: Give udev time to settle before accessing i2c nodes
+                  Add support for Intel PCH (SMBus)
+                  Add support for SMSC EMC1403 and EMC1404
+                  Fix detection of RB-C3 AMD Family 10h CPU
+                  Add support for SMSC EMC2103
+                  Add support for Fintek F71889ED
+                  Add support for ITE IT8721F
+                  Fix Maxim MAX6690 support
+                  Fix handling of duplicate detections
+                  Add support for STMicroelectronics STTS424
+                  Add support for NXP SE97 / SE97B
+                  Add reference to NCT6771F/NCT6772F/NCT6775F
+                  Add support for several JC42.4 compliant temperature sensors
+                  Add support for ITE IT8512E/F and IT8758E
+
+3.1.2 (2010-02-02)
+  libsensors: Support upcoming sysfs path to i2c adapters
+              Add support for HID devices
+  fancontrol: Check that all referenced sysfs files exist
+              Check that all devices match the configuration file
+  pwmconfig: Exit immediately if not root
+             Save device paths and names in configuration file
+  sensors.conf.default: Encourage user to not modify this file
+                        Add a section for adt7473 and adt7475
+                        Add a section for adt7476 and adt7490
+  sensord: Don't name our variables "alarm"
+  	   Major refactoring including
+	   - Cleanup of huge functions
+	   - Remove needless casts
+	   - Fix coding style
+	   - Break long lines
+	   - Make functions more robust
+	   - Remove unused return value
+	   - Make CGI script more XHTML compatible
+  sensors: Fix iconv-related build warning in set_degstr
+  sensors-detect: Refer to tmp401 driver if TMP411 is detected
+                  Clean up the discovery of i2c adapters
+                  Report unsupported Access Bus adapters
+                  Add detection for TI's TMP421, TMP422 and TMP423 chips
+                  The SMSC LPC47M233 isn't currently supported
+                  Support upcoming sysfs path to i2c adapters
+                  Rename the modprobe configuration file to lm_sensors.conf
+                  Only report AMD Family 10h CPU with working sensors
+                  Add detection for AMD Family 11h thermal sensors
+                  Add detection for Intel Atom thermal sensors
+                  Add detection for National Semiconductor LM73
+                  Add SMBus detection for AMD Hudson-2
+                  Add detection for Nuvoton W83667HG-B and W83677HG-I
+                  Add detection for Analog Devices ADT7490
+                  Refer to f71882fg driver if F71889FG is detected
+                  Add detecton for Fintek F71869FG
+                  Detect ADT7473 at 0x2c and 0x2d
+                  More known revisions for the ADT7476
+                  Add detection for Nuvoton W83L771AWG/ASG
+                  Add detection for VIA Nano
+                  Add detection for Texas Instruments AMC6821
+                  Add detection for Analog Devices ADT7411
+  sysconfig-lm_sensors-convert: Fix exit code
+
+3.1.1 (2009-06-21)
+  isadump: Use geteuid instead of getuid so that setuid bit works
+  isaset: Use geteuid instead of getuid so that setuid bit works
+  libsensors: Don't rely on dirent->dt_type being set
+              New method to free the memory allocated for chip names
+  Makefile: Include generated source files in dependency checking
+            Make it possible to skip building of the static library
+  fancontrol: Add support for absolute path to hwmon devices
+  sensord: Remove the hidden commandline interface
+           Introduce struct sensord_arguments
+           Using sigaction for signal handlers
+           Convert to linux coding style
+           Cleanup and refactoring of the source code
+           Don't use the system log when generating a CGI script
+           Disable unit scaling for fan speeds
+           Use daemon logging facility instead of local4 by default
+           Fix a memory leak when a chip name is provided
+  sensors: Fix a memory leak when a chip name is provided
+  sensors-detect: Add nNidia nForce MCP78S SMBus detection
+                  Display Super-I/O address even if LD is disabled
+                  Differentiate between PC8374L and WPCD377I
+                  Use dmidecode to find IPMI interfaces if available
+                  Add Nuvoton W83795G/ADG detection
+                  Add National Semiconductor LM95241 detection
+                  Add Winbond/Nuvoton W83627DHG-P and W83627UHG detection
+
+3.1.0 "Happy Birthday Lina" (2009-02-28)
+  libsensors: Add support for instantaneous power sensors
+              Add support for current sensors
+              Fix error propagation during expression evaluation
+              Detect excessive recursion depth during expression eval (#2365)
+              Return a string rather than NULL for unknown errors
+              Skip non-file sysfs entries for speed
+              Fix memory leak in scanner when using flex 2.5.9 or later
+              Report configuration file read errors
+              Exit the configuration file parser sooner
+              Free bus statements from the configuration file sooner
+              Read extra configuration files from /etc/sensors.d (#2174)
+              Report the configuration file name on parse errors
+              Add support for ACPI devices
+  libsensors.3: Reformat for clarity
+                Document function sensors_snprintf_chip_name
+                Document error-related functions
+  lm_sensors.init: Support new format of /etc/sysconfig/lm_sensors (#2246)
+                   Drop support for kernels 2.4 and earlier
+  lm_sensors.init.suse: Delete (actual SuSE script is much different)
+  Makefile: Install sensors.conf.default instead of sensors.conf.eg (#2333)
+  pwmconfig: Drop support for kernels 2.4 and earlier
+  fancontrol: Don't round temperature values
+              Drop support for kernels 2.4 and earlier
+              Exit if configuration file can't be read
+              Create pid file only after successful initialization
+  maxilife scripts: Delete (driver never ported to Linux 2.6)
+  sensord: Accept negative temperatures in RRD database
+           Don't bail out on transient errors (#2330)
+  sensors: Add support for instantaneous power sensors
+           Add support for current sensors
+           Fix exit code in error case
+  sensors.conf.5: Lots of additions and reworks
+  sensors.conf.default: New, minimum version of sensors.conf.eg (#2333)
+                        Add sections for the SMSC SCH311x and SCH5027
+  sensors.conf.eg: The LM99 offset is now handled in the lm90 driver
+                   Move help section to sensors.conf.5
+                   Add a section for the Asus/Fintek F8000
+  sensors-detect: Fix detection of ADT7463 and LM96000
+                  Add VIA VX800/VX820 SMBus support 
+                  Fix detection of Intel 5000 series FB-DIMM AMB
+                  Fix detection of ADT7462
+                  Fix detection of SMSC LPC47M292
+                  Add SMSC LPC47M233 support
+                  Drop support for Linux 2.4 (#2325)
+                  Handle special case chips more efficiently
+                  Fix SMBus detection of W83627EHF and W83627DHG
+                  Get I2C adapter driver names from sysfs (#2328)
+                  Drop old SiS I2C adapter entries
+                  Drop separate LM78-J entry
+                  Merge all Mozart-2 entries
+                  Merge both GL518SM entries into one
+                  Complete GL520SM detection
+                  Improve GL525SM detection a bit
+                  Let kernel 2.6.28 and later handle chips aliases
+                  Unload kernel drivers when we are done with them (#2329)
+                  Fix handling of bus driver names with an underscore
+                  Simplify loading of bus drivers
+                  Fix bus number prediction logic (#2327)
+                  Suggest the sbs driver for smart batteries
+                  Drop alias detection for Super-I/O chips
+                  Move alias detection after all chip detections
+                  Probe chip types from safest to more risky (#2322)
+                  Add an option to skip ISA probes except IPMI
+                  Skip ISA detection by default if a Super I/O was found (#2322)
+                  Do not scan I2C adapters on multimedia cards by default
+                  Skip SMBus probing by default if a Super I/O was found (#2322)
+                  Display I2C address statictics with --stat
+                  Document the new detection order and rules
+                  Gather DMI data and print it at start-up
+                  Always probe the SMBus on Asus, Tyan and Supermicro boards
+                  Move IPMI interface detection to its own section
+                  Skip IPMI probing on laptops
+                  Add Winbond/Nuvoton W83667HG support
+                  Add Intel Core I7 support
+                  Generate new format for /etc/sysconfig/lm_sensors (#2246)
+                  Print warnings about missing modules on screen
+                  Fix detection of older DS1621
+                  Add nNidia nForce MCP67, MCP73, MCP79 SMBus support
+                  Exclude auto-loaded PCI and USB drivers from list (#2368)
+                  Special case probing i2c address 0x73 (to not crash FSC ICs)
+                  Add detection of FSC Hades and Syleus IC's
+                  Add detection of Winbond WPCD377I (no sensors)
+  sensors-detect-stat.pl: Delete (functionality merged into sensors-detect)
+  sysconfig-lm_sensors-convert: Sample config file conversion script (#2246)
+
+3.0.3 (2008-09-28)
+  libsensors: Avoid namespace pollution
+  fancontrol: Don't use named pipes when we don't need them (#2319)
+  pwmconfig: Tell the user about gnuplot if it isn't installed
+             Fix MINSTOP and MINSTART test functions (#2340)
+             Test MINSTOP before MINSTART
+             Use better step values for MINSTOP test
+             Print the revision and date at start-up
+             Detect and report obviously incorrect fan speeds
+             Determine MINSTOP automatically
+             Skip MINSTOP and MINSTART tests if fan can't stop
+             Change default for MINTEMP from 0 to 20 degrees C
+             Add support for attributes in the hwmon class device (#2260)
+             Update manual page
+  sensors.conf.eg: Drop "label temp#_crit" statements
+  sensors-detect: Add Intel SCH (bus) support
+                  Add SMSC EMC6D103 support
+                  Improve MAX6657, MAX6658, MAX6659 detection
+                  Cache the byte data reads (#2326)
+                  Add Maxim MAX6654/MAX6690 support
+                  Add National Semiconductor LM95231 support
+                  Add Analog Devices ADT7481 support
+                  Refactor alias detection functions
+                  Fix Andigilog aSC7621 support
+                  Add Texas Instruments THMC51 support
+                  Fix Analog Devices ADT7461 support
+                  Add VIA C7 support
+                  Fix Winbond W83L786NR/NG/R/G support (#2336)
+                  Add Maxim MAX6646/MAX6647/MAX6649 support
+                  Add VIA VT1212 support
+                  Add SMSC EMC2700LPC support
+                  Add ITE IT8720F support
+                  Add Texas Instruments TMP411 support
+                  Prevent misdetection of W83627DHG on I2C as LM78
+                  W83627DHG has no subclients
+                  Add Maxim MAX1618 support
+
+3.0.2 (2008-05-18)
+  documentation: Delete the FAQ, now maintained on the wiki
+  libsensors: Use __func__ instead of __FUNCTION__
+              Parse the configuration file in C locale
+              Late compute statements override early ones
+              Support virtual hwmon devices (#2309)
+              Support power and energy sensors
+              Support DOS-style configuration files again (#2284)
+  pwmconfig: Don't create the configuration file before it's needed
+             Don't preserve configuration file customizations
+             Fix permissions of the configuration file
+             Don't start if fancontrol is running (#2299)
+  fancontrol: Don't start if already running (#2299)
+              Delete fancontrol.pid at exit time (#2299)
+  sensord: Fix support of multiple chip names on the command line (#2321)
+  sensors: Print energy and power sensors with automatically scaled units
+  sensors-detect: Add SMSC SCH5027D detection
+                  Do not access I/O ports on PPC
+                  Move south bridge sensor detection to the right section
+                  Run chip_special_cases() earlier
+                  New device ID for the SMSC SCH5317
+                  Add SMSC SCH5127 detection
+                  Add National Semiconductor LM64 detection
+                  Add Asus F8000 detection
+                  Add Intel ICH10 (bus) detection
+                  Don't probe I2C addresses 0x40-0x47
+                  Fix the parsing of I2C addresses not to scan
+                  Detect and skip 1-register-only I2C devices
+                  Avoid SMBus word transactions where possible
+                  Add Dallas DS1631 detection
+
+3.0.1 (2008-01-28)
+  documentation: Update the application writing guidelines
+  libsensors: No longer depend on libsysfs (#2262)
+              Don't guess the bus type from the device ID format (#2240)
+              Add support for attributes in the hwmon class device (#2260)
+  Makefile: No warnings about ld configuration for staged installations
+            Document the variable overriding mechanism (#2296)
+  pwmconfig: Really hide errors on sysfs writes
+             Deal gracefully with read-only pwm_enable files
+             Warn about outputs found in automatic mode
+             Repeat available configuration options before prompt (#2289)
+             Fix duplicate warning message when config file is broken
+  fancontrol: Detect improperly formatted FCTEMPS value (#2293)
+  sensord: Fix rrd support (#2276)
+           Use the same colors for daily and weekly charts
+           Drop workaround needed by old versions of rrdtool
+  sensors: Fix error path on library initialization error
+  sensors.conf.eg: Fix voltage value references (g520sm, lm80, pc87366)
+  sensors-detect: Drop PCA9540 detection
+                  Improve sysconfig and modprobe.d integration
+                  Add SMSC SCH5514D-NS detection (no sensors)
+                  Lower the confidence of MAX6650/MAX6651
+                  Add Fintek F71858DG detection
+                  Add Fintek F81216D detection (no sensors)
+  unhide_ICH_SMBus: Add support for the 82801AA (ICH)
+
+3.0.0 (2007-11-24)
+  Makefile: Fix MODULE_DIR usage in etc
+            Fix build on PPC
+  pwmconfig: Better diagnostics in pwmdisable
+             Give the fans some time to spin up
+  sensors-detect: Add Texas Instruments TMP401 detection
+                  Mention the f75375s driver
+                  Add SMSC LPC47B367-NC detection (no sensors)
+                  Reduce w83781d/lm78 I2C address probing range
+                  Add Intel Celeron 4xx and Penryn (CPU on 45nm) detection 
+
+3.0.0-rc3 (2007-10-28)
+  libsensors: Add a default configuration file
+              Fix memory leaks on initialization error
+              Make the configuration file optional
+              Change the default configuration file to /etc/sensors3.conf
+  sensord: Reload config on SIGHUP rather than automatically
+           No default configuration file name
+  sensors: Fix file handle leak on library initialization error
+           Handle the case where voltage measurements aren't available
+           No default configuration file name
+           Install as sensors again
+  sensors.conf.eg: Install as sensors3.conf
+  sensors-detect: Fix SMSC LPC47B357/M967 detection
+                  Select the right driver for FSC chips
+  sensors-detect-stat.pl: Handle alternative chip lists
+
+3.0.0-rc2 (2007-10-10)
+  useful_addresses.html: Moved to the wiki
+  Makefile: Add sensors-conf-convert to make install
+  libsensors: Notify the caller when writing a value fails
+              Differentiate between different read error types
+              Report I/O errors as such
+  sensord: Log the error code on failure
+           Drop the configuration file search path mechanism (#2259)
+           Manual page update
+  sensors: Fix spurious critical temperature alarm
+           Print error messages to stderr
+           Make error messages more helpful and consistent
+           Better handling of the fault flags
+  sensors-conf-convert: Add a short help text
+  sensors-detect: Fix SMSC SCH311x detection
+                  Add AMD K10 CPU sensor detection 
+
+3.0.0-rc1 (2007-09-25)
+  Initial release
Index: /lm-sensors/tags/V3-3-0/COPYING
===================================================================
--- /lm-sensors/tags/V3-3-0/COPYING	(revision 4483)
+++ /lm-sensors/tags/V3-3-0/COPYING	(revision 4483)
@@ -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.
Index: /lm-sensors/tags/V3-3-0/etc/sensors.conf.default
===================================================================
--- /lm-sensors/tags/V3-3-0/etc/sensors.conf.default	(revision 5948)
+++ /lm-sensors/tags/V3-3-0/etc/sensors.conf.default	(revision 5948)
@@ -0,0 +1,510 @@
+# libsensors configuration file
+# -----------------------------
+#
+# This default configuration file only includes statements which do not
+# differ from one mainboard to the next. Only label, compute and set
+# statements for internal voltage and temperature sensors are included.
+#
+# In general, local changes should not be added to this file, but rather
+# placed in custom configuration files located in /etc/sensors.d. This
+# approach makes further updates much easier.
+#
+# Such custom configuration files for specific mainboards can be found at
+# http://www.lm-sensors.org/wiki/Configurations
+
+chip "lm78-*" "lm79-*" "lm80-*"
+
+    label temp1 "M/B Temp"
+
+
+chip "w83792d-*"
+
+    label in0 "VcoreA"
+    label in1 "VcoreB"
+    label in6 "+5V"
+    label in7 "5VSB"
+    label in8 "Vbat"
+
+    set in6_min  5.0 * 0.90
+    set in6_max  5.0 * 1.10
+    set in7_min  5.0 * 0.90
+    set in7_max  5.0 * 1.10
+    set in8_min  3.0 * 0.90
+    set in8_max  3.0 * 1.10
+
+
+chip "w83793-*"
+
+    label in0 "VcoreA"
+    label in1 "VcoreB"
+    label in7 "+5V"
+    label in8 "5VSB"
+    label in9 "Vbat"
+
+    set in7_min  5.0 * 0.90
+    set in7_max  5.0 * 1.10
+    set in8_min  5.0 * 0.90
+    set in8_max  5.0 * 1.10
+    set in9_min  3.0 * 0.90
+    set in9_max  3.0 * 1.10
+
+
+chip "w83795g-*" "w83795adg-*"
+
+    label in12 "+3.3V"
+    label in13 "3VSB"
+    label in14 "Vbat"
+
+    set in12_min  3.3 * 0.90
+    set in12_max  3.3 * 1.10
+    set in13_min  3.3 * 0.90
+    set in13_max  3.3 * 1.10
+    set in14_min  3.0 * 0.90
+    set in14_max  3.3 * 1.10
+
+
+chip "via686a-*"
+
+    label in0 "Vcore"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+
+    set in2_min  3.3 * 0.90
+    set in2_max  3.3 * 1.10
+    set in3_min  5.0 * 0.90
+    set in3_max  5.0 * 1.10
+    set in4_min 12.0 * 0.90
+    set in4_max 12.0 * 1.10
+
+
+chip "adm1025-*" "ne1619-*"
+
+    label in1 "Vcore"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+    label in5 "VCC"
+
+    set in2_min  3.3 * 0.90
+    set in2_max  3.3 * 1.10
+    set in3_min  5.0 * 0.90
+    set in3_max  5.0 * 1.10
+    set in5_min  3.3 * 0.90
+    set in5_max  3.3 * 1.10
+# Depending on how your chip is hardwired, you may or may not have
+# +12V readings.
+#    set in4_min 12.0 * 0.90
+#    set in4_max 12.0 * 1.10
+
+    label temp1 "CPU Temp"
+    label temp2 "M/B Temp"
+
+
+chip "lm87-*" "adm1024-*"
+
+    label in1 "Vcore"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+
+    set in2_min  3.3 * 0.90
+    set in2_max  3.3 * 1.10
+    set in3_min  5.0 * 0.90
+    set in3_max  5.0 * 1.10
+    set in4_min 12.0 * 0.90
+    set in4_max 12.0 * 1.10
+
+    label temp1 "M/B Temp"
+    label temp2 "CPU Temp"
+
+
+chip "it87-*" "it8712-*" "it8716-*" "it8718-*" "it8720-*"
+
+    label in8 "Vbat"
+
+
+chip "fscpos-*" "fscher-*"
+#FSC "Hermes"
+
+    label in0 "+12V"
+    label in1 "+5V"
+    label in2 "Vbat"
+
+    label temp1 "CPU Temp"
+    label temp2 "M/B Temp"
+    label temp3 "Aux Temp"
+
+
+chip "fscscy-*"
+#FSC "Scylla"
+
+    label in0 "+12V"
+    label in1 "+5V"
+    label in2 "+3.3V"
+
+    label temp1 "CPU0 Temp"
+    label temp2 "CPU1 Temp"
+    label temp3 "M/B Temp"
+    label temp4 "Aux Temp"
+
+
+chip "fschds-*"
+# Fujitsu Technology Solutions, "Hades"-Chip
+
+# Temperatures
+    label temp1 "CPU Temp"
+    label temp2 "Super I/O Temp"
+    label temp3 "System Temp"
+
+# Fans
+    label fan1 "PSU Fan"
+    label fan2 "CPU Fan"
+    label fan3 "System FAN2"
+    label fan4 "System FAN3"
+    label fan5 "System FAN4"
+
+# Voltages
+    label in0 "+12V"
+    label in1 "+5V"
+    label in2 "Vbat"
+
+chip "fscsyl-*"
+# Fujitsu Technology Solutions, "Syleus"-Chip
+
+# Temperatures
+    label temp1 "CPU Temp"
+    label temp4 "Super I/O Temp"
+    label temp5 "Northbridge Temp"
+
+# Fans
+    label fan1 "CPU Fan"
+    label fan2 "System FAN2"
+    label fan3 "System FAN3"
+    label fan4 "System FAN4"
+    label fan7 "PSU Fan"
+
+# Voltages
+    label in0 "+12V"
+    label in1 "+5V"
+    label in2 "Vbat"
+    label in3 "+3.3V"
+    label in5 "+3.3V-Aux"
+
+chip "vt1211-*"
+
+    label in5 "+3.3V"
+
+    label temp2 "SIO Temp"
+
+
+chip "vt8231-*"
+
+    label in5 "+3.3V"
+
+
+chip "smsc47m192-*"
+
+    label in1 "Vcore"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+    label in5 "VCC"
+
+    set in2_min  3.3 * 0.90
+    set in2_max  3.3 * 1.10
+    set in3_min  5.0 * 0.90
+    set in3_max  5.0 * 1.10
+    set in4_min 12.0 * 0.90
+    set in4_max 12.0 * 1.10
+    set in5_min  3.3 * 0.90
+    set in5_max  3.3 * 1.10
+
+    label temp1 "SIO Temp"
+
+
+chip "lm85-*" "lm85b-*" "lm85c-*" "adm1027-*" "adt7463-*" "adt7468-*" "emc6d100-*" "emc6d102-*"
+
+    label in1 "Vcore"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+
+    set in2_min  3.3 * 0.90
+    set in2_max  3.3 * 1.10
+    set in3_min  5.0 * 0.90
+    set in3_max  5.0 * 1.10
+# Depending on how your chip is hardwired, you may or may not have
+# +12V readings.
+#    set in4_min 12.0 * 0.90
+#    set in4_max 12.0 * 1.10
+
+    label temp2 "M/B Temp"
+
+
+chip "pc87365-*" "pc87366-*"
+
+# Voltage inputs
+
+    label in7 "3VSB"
+    label in8 "VDD"
+    label in9 "Vbat"
+    label in10 "AVDD"
+
+    compute in7   @*2, @/2
+    compute in8   @*2, @/2
+    compute in10  @*2, @/2
+
+# These are the operating conditions as recommended by National
+# Semiconductor
+    set in7_min   3.0
+    set in7_max   3.6
+    set in8_min   3.0
+    set in8_max   3.6
+    set in10_min  3.0
+    set in10_max  3.6
+# Depending on the hardware setup, the battery voltage may or may not
+# be monitored.
+#    set in9_min   2.4
+#    set in9_max   3.6
+
+    label temp3 "SIO Temp"
+
+    set temp3_min    0
+    set temp3_max   70
+    set temp3_crit  85
+
+
+chip "adm1030-*" "adm1031-*"
+
+    label temp1 "M/B Temp"
+
+
+chip "w83627thf-*"
+
+    label in3 "+5V"
+    label in7 "5VSB"
+    label in8 "Vbat"
+
+    # Internal resistors
+    compute in3  @ * (1 + 34/51), @ / (1 + 34/51)
+    compute in7  @ * (1 + 34/51), @ / (1 + 34/51)
+
+    set in3_min  5.0 * 0.90
+    set in3_max  5.0 * 1.10
+    set in7_min  5.0 * 0.90
+    set in7_max  5.0 * 1.10
+# The battery voltage may or may not be monitored.
+#    set in8_min  3.0 * 0.90
+#    set in8_max  3.0 * 1.10
+
+
+chip "w83627ehf-*" "w83627dhg-*" "w83667hg-*" "nct6775-*" "nct6776-*"
+
+    label in0 "Vcore"
+    label in2 "AVCC"
+    label in3 "+3.3V"
+    label in7 "3VSB"
+    label in8 "Vbat"
+
+    set in2_min  3.3 * 0.90
+    set in2_max  3.3 * 1.10
+    set in3_min  3.3 * 0.90
+    set in3_max  3.3 * 1.10
+    set in7_min  3.3 * 0.90
+    set in7_max  3.3 * 1.10
+    set in8_min  3.0 * 0.90
+    set in8_max  3.0 * 1.10
+
+
+chip "w83627uhg-*"
+
+    label in2 "AVCC"
+    label in3 "+5V"
+    label in7 "5VSB"
+    label in8 "Vbat"
+
+    set in2_min  5.0 * 0.90
+    set in2_max  5.0 * 1.10
+    set in3_min  5.0 * 0.90
+    set in3_max  5.0 * 1.10
+    set in7_min  5.0 * 0.90
+    set in7_max  5.0 * 1.10
+    set in8_min  3.0 * 0.90
+    set in8_max  3.0 * 1.10
+
+
+chip "f71805f-*"
+
+    label in0 "+3.3V"
+
+    set in0_min  3.3 * 0.90
+    set in0_max  3.3 * 1.10
+
+
+chip "f71872f-*"
+
+    label in0 "+3.3V"
+    label in9 "Vbat"
+    label in10 "3VSB"
+
+    set in0_min   3.3 * 0.90
+    set in0_max   3.3 * 1.10
+    set in9_min   3.0 * 0.90
+    set in9_max   3.0 * 1.10
+    set in10_min  3.3 * 0.90
+    set in10_max  3.3 * 1.10
+
+
+chip "k8temp-*"
+
+    label temp1 "Core0 Temp"
+    label temp2 "Core0 Temp"
+    label temp3 "Core1 Temp"
+    label temp4 "Core1 Temp"
+
+
+chip "dme1737-*"
+
+    label in0 "5VSB"
+    label in1 "Vcore"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+    label in5 "3VSB"
+    label in6 "Vbat"
+
+    label temp2 "SIO Temp"
+
+    set in0_min  5.0 * 0.90
+    set in0_max  5.0 * 1.10
+    set in2_min  3.3 * 0.90
+    set in2_max  3.3 * 1.10
+    set in3_min  5.0 * 0.90
+    set in3_max  5.0 * 1.10
+    set in4_min 12.0 * 0.90
+    set in4_max 12.0 * 1.10
+    set in5_min  3.3 * 0.90
+    set in5_max  3.3 * 1.10
+    set in6_min  3.0 * 0.90
+    set in6_max  3.0 * 1.10
+
+
+chip "sch311x-*"
+
+    label in1 "Vcore"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+    label in5 "3VSB"
+    label in6 "Vbat"
+
+    label temp2 "SIO Temp"
+
+    set in2_min  3.3 * 0.90
+    set in2_max  3.3 * 1.10
+    set in3_min  5.0 * 0.90
+    set in3_max  5.0 * 1.10
+    set in4_min 12.0 * 0.90
+    set in4_max 12.0 * 1.10
+    set in5_min  3.3 * 0.90
+    set in5_max  3.3 * 1.10
+    set in6_min  3.0 * 0.90
+    set in6_max  3.0 * 1.10
+
+
+chip "sch5027-*"
+
+    label in0 "5VSB"
+    label in1 "Vcore"
+    label in2 "+3.3V"
+    label in5 "3VSB"
+    label in6 "Vbat"
+
+    label temp2 "SIO Temp"
+
+    set in0_min  5.0 * 0.90
+    set in0_max  5.0 * 1.10
+    set in2_min  3.3 * 0.90
+    set in2_max  3.3 * 1.10
+    set in5_min  3.3 * 0.90
+    set in5_max  3.3 * 1.10
+    set in6_min  3.0 * 0.90
+    set in6_max  3.0 * 1.10
+
+
+chip "sch5127-*"
+
+    label in2 "+3.3V"
+    label in5 "3VSB"
+    label in6 "Vbat"
+
+    set in2_min  3.3 * 0.90
+    set in2_max  3.3 * 1.10
+    set in5_min  3.3 * 0.90
+    set in5_max  3.3 * 1.10
+    set in6_min  3.0 * 0.90
+    set in6_max  3.0 * 1.10
+
+
+chip "f71808e-*" "f71862fg-*" "f71869-*" "f71882fg-*" \
+     "f71889fg-*" "f71889ed-*" "f71889a-*"
+
+    label in0 "+3.3V"
+    label in7 "3VSB"
+    label in8 "Vbat"
+
+    compute in0  @*2, @/2
+    compute in7  @*2, @/2
+    compute in8  @*2, @/2
+
+
+chip "f71858fg-*" "f8000-*"
+
+    label in0 "+3.3V"
+    label in1 "3VSB"
+    label in2 "Vbat"
+
+    compute in0  @*2, @/2
+    compute in1  @*2, @/2
+    compute in2  @*2, @/2
+
+
+chip "f81865f-*"
+
+    label in0 "+3.3V"
+    label in5 "3VSB"
+    label in6 "Vbat"
+
+    compute in0  @*2, @/2
+    compute in5  @*2, @/2
+    compute in6  @*2, @/2
+
+
+chip "adt7473-*" "adt7475-*"
+
+    label in2 "+3.3V"
+
+    set in2_min  3.3 * 0.90
+    set in2_max  3.3 * 1.10
+
+    label temp2 "Board Temp"
+
+
+chip "adt7476-*" "adt7490-*"
+
+    label in1 "Vcore"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+
+    set in2_min  3.3 * 0.90
+    set in2_max  3.3 * 1.10
+    set in3_min  5.0 * 0.90
+    set in3_max  5.0 * 1.10
+# Depending on how your ADT7476 is hardwired, you may or may not have
+# +12V readings.
+#    set in4_min 12.0 * 0.90
+#    set in4_max 12.0 * 1.10
+
+    label temp2 "M/B Temp"
Index: /lm-sensors/tags/V3-3-0/etc/sensors.conf.eg
===================================================================
--- /lm-sensors/tags/V3-3-0/etc/sensors.conf.eg	(revision 5815)
+++ /lm-sensors/tags/V3-3-0/etc/sensors.conf.eg	(revision 5815)
@@ -0,0 +1,2171 @@
+# libsensors configuration file
+# -----------------------------
+# NOTE:
+# All the set statements from this file are commented out by default.
+# The reason is that the proper limits are highly system-dependent,
+# and writing improper limits may have all sorts of weird effects,
+# from beeping to CPU throttling to instant reboot. If you want to
+# actually set the limits, remove the comment marks, then run "sensors -s".
+
+chip "lm78-*" "lm79-*" "w83781d-*"
+
+# These are as advised in the LM78 and LM79 data sheets, and used on almost
+# any mainboard we have seen.
+
+    label in0 "VCore 1"
+    label in1 "VCore 2"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+    label in5 "-12V"
+    label in6 "-5V"
+
+# For positive voltages (in3, in4), two resistors are used, with the following
+# formula (R1,R2: resistor values, Vs: read voltage, Vin: pin voltage)
+#   R1 = R2 * (Vs/Vin - 1)
+# For negative voltages (in5, in6) two resistors are used, with the following
+# formula (Rin,Rf: resistor values, Vs: read voltage, Vin: pin voltage)
+#   Rin = (Vs * Rf) / Vin
+#
+# Here are the official LM78 and LM79 data sheet values.
+#       Vs     R1,Rin   R2,Rf    Vin
+# in3   +5.0      6.8    10     +2.98
+# in4  +12.0     30      10     +3.00
+# in5  -12.0    240      60     +3.00
+# in6   -5.0    100      60     +3.00
+#
+# These would lead to these declarations:
+# compute in3 ((6.8/10)+1)*@ ,  @/((6.8/10)+1)
+# compute in4 ((30/10)+1)*@  ,  @/((30/10)+1)
+# compute in5 -(240/60)*@    ,  -@/(240/60)
+# compute in6 -(100/60)*@    ,  -@/(100/60)
+#
+# On almost any mainboard we have seen, the Winbond compute values lead to
+# much better results, though.
+#
+#       Vs     R1,Rin   R2,Rf    Vin
+# in4  +12.0     28      10     +3.15
+# in5  -12.0    210      60.4   +3.45
+# in6   -5.0     90.9    60.4   +3.33
+#
+# These leads to these declarations:
+
+    compute in3 ((6.8/10)+1)*@ ,  @/((6.8/10)+1)
+    compute in4 ((28/10)+1)*@  ,  @/((28/10)+1)
+    compute in5 -(210/60.4)*@  ,  -@/(210/60.4)
+    compute in6 -(90.9/60.4)*@ ,  -@/(90.9/60.4)
+
+# Here, we assume the VID readings are valid, and we use a max. 5% deviation
+
+#    set in0_min cpu0_vid*0.95
+#    set in0_max cpu0_vid*1.05
+#    set in1_min cpu0_vid*0.95
+#    set in1_max cpu0_vid*1.05
+#    set in2_min 3.3 * 0.95
+#    set in2_max 3.3 * 1.05
+#    set in3_min 5.0 * 0.95
+#    set in3_max 5.0 * 1.05
+#    set in4_min 12 * 0.95
+#    set in4_max 12 * 1.05
+#    set in5_min -12 * 0.95
+#    set in5_max -12 * 1.05
+#    set in6_min -5 * 0.95
+#    set in6_max -5 * 1.05
+
+# Examples for lm78, lm79 temperature limits
+#    set temp1_max      40
+#    set temp1_max_hyst 37
+
+# Examples for w83781d temperature limits
+#    set temp1_max      40
+#    set temp1_max_hyst 37
+#    set temp2_max      52
+#    set temp2_max_hyst 47
+#    set temp3_max      52
+#    set temp3_max_hyst 47
+
+# Examples of fan low speed limits
+#    set fan1_min 3000
+#    set fan2_min 3000
+#    set fan3_min 3000
+
+# Ignore fans you don't actually have
+#    ignore fan1
+#    ignore fan2
+#    ignore fan3
+
+# In case a lm78 is used together with a lm75, the lm78 temp sensor will
+# generally show the M/B temperature while the lm75 temp sensor will show
+# the CPU temperature.
+#    label temp1 "M/B Temp"
+
+# Uncomment the following line to enable beeps for all alarms on this chip
+#    set beep_enable 1
+
+
+
+chip "lm75-*"
+
+# Most boards don't need scaling. Following is for the Asus TX97-E.
+#   compute temp1 @*2.0, @/2.0
+
+# Examples for temperature limits
+#    set temp1_max      70
+#    set temp1_max_hyst 65
+
+# In case a lm75 is used together with a lm78, the lm78 temp sensor will
+# generally show the M/B temperature while the lm75 temp sensor will show
+# the CPU temperature.
+#    label temp1 "CPU Temp"
+
+
+chip "sis5595-*"
+
+    label in0 "VCore 1"
+    label in1 "VCore 2"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+
+    compute in3 ((6.8/10)+1)*@ ,  @/((6.8/10)+1)
+    compute in4 ((28/10)+1)*@  ,  @/((28/10)+1)
+
+#    set in0_min 2.0 * 0.95
+#    set in0_max 2.0 * 1.05
+#    set in1_min 2.0 * 0.95
+#    set in1_max 2.0 * 1.05
+#    set in2_min 3.3 * 0.95
+#    set in2_max 3.3 * 1.05
+#    set in3_min 5.0 * 0.95
+#    set in3_max 5.0 * 1.05
+#    set in4_min 12 * 0.95
+#    set in4_max 12 * 1.05
+
+#
+# SiS5595 temperature calculation
+# The driver currently includes a calculation due to the wide
+# variation in thermistor types on SiS5595 motherboards.
+# The driver currently has a calculation of t = (.83x + 52.12).
+# One user reports the correct formula of t = (.345x - 12).
+# So you want to put a compute line in sensors.conf that has
+# the inverse of the driver formula, and put your formula on top of it.
+# The inverse of the driver formula is x = (1.20t - 62.77)
+# So the final formula is newt = (.345(1.20t - 62.77)) - 12).
+# Put this in the sensors.conf file as
+# compute temp1 ((.345 * ((1.20 * @) - 62.77)) - 12), ...
+# where ... is the inverse function I leave to you.
+#
+# Look in your 'Vendor.ini' file to see which one is present
+# on your motherboard. Look for the line like:
+#	[Temp1]
+#	     ThermistorType     = NTC-10KC15-1608-1P
+# Fix up a 'compute' line to match your thermistor type.
+# Warning. You still don't have enough information to do this.
+#	     ThermistorType     = NTC-10KC15-1608-1P (10K at 25C; Beta = 3435)
+#   compute temp1 ((X * ((1.20 * @) - 62.77)) - Y), ...
+#	     ThermistorType     = NTC-103KC15-1608-1P  (??)
+#   compute temp1 ((X * ((1.20 * @) - 62.77)) - Y), ...
+#	     ThermistorType     = NTC-103AT-2 (10K at 25C; Beta = 3435)
+#   compute temp1 ((X * ((1.20 * @) - 62.77)) - Y), ...
+#	     ThermistorType     = NTC-103JT   (10K at 25C; Beta = 3435)
+#   compute temp1 ((X * ((1.20 * @) - 62.77)) - Y), ...
+
+# examples for sis5595 temperature limits;
+#    set temp1_max      40
+#    set temp1_max_hyst 37
+
+
+chip "w83782d-*" "w83627hf-*"
+
+# Same as above for w83781d except that in5 and in6 are computed differently.
+# Rather than an internal inverting op amp, the 82d/83s use standard positive
+# inputs and the negative voltages are level shifted by a 3.6V reference.
+# The math is convoluted, so we hope that your motherboard
+# uses the recommended resistor values.
+
+    label in0 "VCore 1"
+    label in1 "VCore 2"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+    label in5 "-12V"
+    label in6 "-5V"
+    label in7 "V5SB"
+    label in8 "VBat"
+
+# Abit BP6 motherboard has a few differences. VCore1 and VCore2 are the core
+# voltages of the two processors. Vtt is memory bus termination resistors
+# voltage.
+#    label in1 "Vtt"
+#    label in8 "VCore2"
+
+    compute in3 ((6.8/10)+1)*@ ,  @/((6.8/10)+1)
+    compute in4 ((28/10)+1)*@  ,  @/((28/10)+1)
+    compute in5 (5.14 * @) - 14.91  ,  (@ + 14.91) / 5.14
+    compute in6 (3.14 * @) -  7.71  ,  (@ +  7.71) / 3.14
+    compute in7 ((6.8/10)+1)*@ ,  @/((6.8/10)+1)
+
+# set limits to  5% for the critical voltages
+# set limits to 10% for the non-critical voltages
+# set limits to 20% for the battery voltage
+
+#    set in0_min cpu0_vid*0.95
+#    set in0_max cpu0_vid*1.05
+#    set in1_min cpu0_vid*0.95
+#    set in1_max cpu0_vid*1.05
+#    set in2_min 3.3 * 0.95
+#    set in2_max 3.3 * 1.05
+#    set in3_min 5.0 * 0.95
+#    set in3_max 5.0 * 1.05
+#    set in4_min 12 * 0.90
+#    set in4_max 12 * 1.10
+#    set in5_max -12 * 0.90
+#    set in5_min -12 * 1.10
+#    set in6_max -5 * 0.95
+#    set in6_min -5 * 1.05
+#    set in7_min 5 * 0.95
+#    set in7_max 5 * 1.05
+#    set in8_min 3.0 * 0.80
+#    set in8_max 3.0 * 1.20
+
+# set up sensor types (thermistor is default)
+# 1 = PII/Celeron Diode; 2 = 3904 transistor;
+# 3435 = thermistor with Beta = 3435
+# If temperature changes very little, try 1 or 2.
+#   set temp1_type 1
+#   set temp2_type 2
+#   set temp3_type 3435
+
+# examples for temperature limits
+#    set temp1_max      40
+#    set temp1_max_hyst 37
+#    set temp2_max      52
+#    set temp2_max_hyst 47
+#    set temp3_max      52
+#    set temp3_max_hyst 47
+
+
+chip "w83783s-*"
+
+# Same as above for w83781d except that in5 and in6 are computed differently.
+# Rather than an internal inverting op amp, the 82d/83s use standard positive
+# inputs and the negative voltages are level shifted by a 3.6V reference.
+# The math is convoluted, so we hope that your motherboard
+# uses the recommended resistor values.
+
+    label in0 "VCore 1"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+    label in5 "-12V"
+    label in6 "-5V"
+
+    compute in3 ((6.8/10)+1)*@ ,  @/((6.8/10)+1)
+    compute in4 ((28/10)+1)*@  ,  @/((28/10)+1)
+    compute in5 (5.14 * @) - 14.91  ,  (@ + 14.91) / 5.14
+    compute in6 (3.14 * @) -  7.71  ,  (@ +  7.71) / 3.14
+
+# set limits to  5% for the critical voltages
+# set limits to 10% for the non-critical voltages
+# set limits to 20% for the battery voltage
+
+#   set in0_min cpu0_vid*0.95
+#   set in0_max cpu0_vid*1.05
+#   set in2_min 3.3 * 0.95
+#   set in2_max 3.3 * 1.05
+#   set in3_min 5.0 * 0.95
+#   set in3_max 5.0 * 1.05
+#   set in4_min 12 * 0.90
+#   set in4_max 12 * 1.10
+#   set in5_max -12 * 0.90
+#   set in5_min -12 * 1.10
+#   set in6_max -5 * 0.95
+#   set in6_min -5 * 1.05
+
+# set up sensor types (thermistor is default)
+# 1 = PII/Celeron Diode; 2 = 3904 transistor;
+# 3435 = thermistor with Beta = 3435
+# If temperature changes very little, try 1 or 2.
+#   set temp1_type 1
+#   set temp2_type 2
+
+# examples for temperature limits
+#    set temp1_max      40
+#    set temp1_max_hyst 37
+#    set temp2_max      52
+#    set temp2_max_hyst 47
+
+
+chip "w83697hf-*"
+
+# Same as above for w83781d except that in5 and in6 are computed differently.
+# Rather than an internal inverting op amp, the 82d/83s use standard positive
+# inputs and the negative voltages are level shifted by a 3.6V reference.
+# The math is convoluted, so we hope that your motherboard
+# uses the recommended resistor values.
+
+# no in1 on this chip.
+
+    label in0 "VCore"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+    label in5 "-12V"
+    label in6 "-5V"
+    label in7 "V5SB"
+    label in8 "VBat"
+
+# Tyan Trinity S2495 KT400 has a few differences. Thanks to Eric Schumann
+# for proving this information. Same is true for Epox 8K3A and 8KHA+.
+# Thanks to Thomas Schorpp for additional feedback.
+#    label in2 "VAgp"
+#    label in5 "+3.3V" # aka. Vio
+#    label in6 "Vdimm"
+#    label in7 "VBat"
+#    label in8 "V5SB"
+#
+# You'll also want to comment out the in5 and in6 compute lines right below,
+# and rename compute in7 to compute in8.
+
+    compute in3 ((6.8/10)+1)*@ ,  @/((6.8/10)+1)
+    compute in4 ((28/10)+1)*@  ,  @/((28/10)+1)
+    compute in5 (5.14 * @) - 14.91  ,  (@ + 14.91) / 5.14
+    compute in6 (3.14 * @) -  7.71  ,  (@ +  7.71) / 3.14
+    compute in7 ((6.8/10)+1)*@ ,  @/((6.8/10)+1)
+
+# 697HF does not have VID inputs so you MUST set your core
+# voltage limits below. Currently set for 1.8V core.
+#               vvv
+
+#   set in0_min 1.8 * 0.95
+#   set in0_max 1.8 * 1.05
+
+#   set in2_min 3.3 * 0.95
+#   set in2_max 3.3 * 1.05
+#   set in3_min 5.0 * 0.95
+#   set in3_max 5.0 * 1.05
+#   set in4_min 12 * 0.90
+#   set in4_max 12 * 1.10
+#   set in5_max -12 * 0.90
+#   set in5_min -12 * 1.10
+#   set in6_max -5 * 0.95
+#   set in6_min -5 * 1.05
+#   set in7_min 5 * 0.95
+#   set in7_max 5 * 1.05
+#   set in8_min 3.0 * 0.80
+#   set in8_max 3.0 * 1.20
+
+# And for Tyan Trinity S2495 KT400 and Epox 8K3A and 8KHA+:
+#    set in2_min 1.5 * 0.95
+#    set in2_max 1.5 * 1.05
+#    set in5_min 3.3 * 0.95
+#    set in5_max 3.3 * 1.05
+#    set in6_min 2.5 * 0.95 # 2.6 on Epox
+#    set in6_max 2.5 * 1.05 # 2.6 on Epox
+#    set in7_min 3.0 * 0.90
+#    set in7_max 3.0 * 1.10
+#    set in8_min 5 * 0.90
+#    set in8_max 5 * 1.10
+
+# set up sensor types (thermistor is default)
+# 1 = PII/Celeron Diode; 2 = 3904 transistor;
+# 3435 = thermistor with Beta = 3435
+# If temperature changes very little, try 1 or 2.
+#   set temp1_type 1
+#   set temp2_type 2
+#   set temp3_type 3435
+
+# examples for temperature limits
+#    set temp1_max      40
+#    set temp1_max_hyst 37
+#    set temp2_max      52
+#    set temp2_max_hyst 47
+
+
+chip "w83627thf-*" "w83637hf-*"
+
+# Rather than an internal inverting op amp, the 627thf uses standard positive
+# inputs and the negative voltages are level shifted by a 3.6V reference
+# (same as 82d/83s).
+# The math is convoluted, so we hope that your motherboard
+# uses the recommended resistor values.
+# Note that in1 (+12V) is the usual in4, and in4 (-12V) is the usual in5.
+# Data sheet is obviously wrong for in4, the usual formula should work.
+# No in5 nor in6.
+
+    label in0 "VCore"
+    label in1 "+12V"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "-12V"
+    label in7 "V5SB"
+    label in8 "VBat"
+
+# Mori Hiroyuki reported to need this (P4P800)
+#   compute in0 @/2, @*2
+
+    compute in1 ((28/10)+1)*@, @/((28/10)+1)
+    compute in3 ((34/51)+1)*@, @/((34/51)+1)
+    compute in4 (5.14*@)-14.91, (@+14.91)/5.14
+    compute in7 ((6.8/10)+1)*@ ,  @/((6.8/10)+1)
+
+# set limits to  5% for the critical voltages
+# set limits to 10% for the non-critical voltages
+# set limits to 20% for the battery voltage
+# if your vid is wrong, you'll need to adjust in0_min and in0_max
+
+#   set in0_min cpu0_vid * 0.95
+#   set in0_max cpu0_vid * 1.05
+#   set in1_min 12 * 0.90
+#   set in1_max 12 * 1.10
+#   set in2_min 3.3 * 0.95
+#   set in2_max 3.3 * 1.05
+#   set in3_min 5.0 * 0.95
+#   set in3_max 5.0 * 1.05
+#   set in4_min -12 * 1.10
+#   set in4_max -12 * 0.90
+#   set in7_min 5 * 0.95
+#   set in7_max 5 * 1.05
+#   set in8_min 3.0 * 0.80
+#   set in8_max 3.0 * 1.20
+
+# set up sensor types (thermistor is default)
+# 1 = PII/Celeron Diode; 2 = 3904 transistor;
+# 3435 = thermistor with Beta = 3435
+# If temperature changes very little, try 1 or 2.
+#   set temp1_type 1
+#   set temp2_type 2
+#   set temp3_type 3435
+
+    label temp1 "M/B Temp"
+    label temp2 "CPU Temp"
+#   ignore temp3
+
+# examples for temperature limits
+#    set temp1_max      40
+#    set temp1_max_hyst 37
+#    set temp2_max      52
+#    set temp2_max_hyst 47
+#    set temp3_max      52
+#    set temp3_max_hyst 47
+
+#   ignore fan1
+    label fan2 "CPU Fan"
+#   ignore fan3
+
+
+# Here are configurations for Winbond W83792AD/D chip.
+chip "w83792d-*"
+
+    label in0 "VCoreA"
+    label in1 "VCoreB"
+    label in6 "5VCC"
+    label in7 "5VSB"
+    label in8 "VBAT"
+
+#    set in0_min 1.4
+#    set in0_max 1.6
+#    set in1_min 1.4
+#    set in1_max 1.6
+#    set in2_min 3.2
+#    set in2_max 3.4
+#    set in3_min 3.1
+#    set in3_max 3.3
+#    set in4_min 1.4
+#    set in4_max 1.5
+#    set in5_min 2.6
+#    set in5_max 2.65
+#    set in6_min 5 * 0.95
+#    set in6_max 5 * 1.05
+#    set in7_min 5 * 0.95
+#    set in7_max 5 * 1.05
+#    set in8_min 3 * 0.95
+#    set in8_max 3 * 1.05
+
+# fan1 adjustments examples
+
+#   set fan1_div 4
+#   set fan1_min 1500
+
+# temp2 limits examples
+
+#    set temp2_max      42
+#    set temp2_max_hyst 37
+
+# ignore examples
+
+#    ignore fan7
+#    ignore temp3
+
+
+# Here are configurations for Winbond W83793 chip.
+chip "w83793-*"
+
+    label in0 "VCoreA"
+    label in1 "VCoreB"
+    label in2 "Vtt"
+    label in5 "+3.3V"
+    label in6 "+12V"
+    label in7 "+5V"
+    label in8 "5VSB"
+    label in9 "VBAT"
+
+    compute in6 12*@ ,  @/12
+
+    label temp1 "CPU1 Temp"
+    label temp2 "CPU2 Temp"
+
+# fan1 adjustments examples
+
+#   set fan1_min 1500
+
+# temp2 limits examples
+
+#   set temp2_max       45
+#   set temp2_max_hyst  40
+
+# ignore examples
+
+#    ignore fan7
+#    ignore temp3
+
+
+chip "as99127f-*"
+
+# Asus won't release a datasheet so this is guesswork.
+# Thanks to Guntram Blohm, Jack, Ed Harrison, Artur Gawryszczak,
+# Victor G. Marimon and others for their feedback.
+
+# Dual power plane
+    label in0 "VCore 1"
+    label in1 "VCore 2"
+# Single power plane (A7V133, A7M266, CUV4X)
+#   label in0 "VCore"
+#   ignore in1
+
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+# These last two may not make sense on all motherboards.
+    label in5 "-12V"
+    label in6 "-5V"
+
+    compute in3 ((6.8/10)+1)*@ ,  @/((6.8/10)+1)
+    compute in4 ((28/10)+1)*@  ,  @/((28/10)+1)
+# AS99127F rev.1 (same as w83781d)
+    compute in5 -(240/60.4)*@ ,  -@/(240/60.4)
+    compute in6 -(90.9/60.4)*@ ,  -@/(90.9/60.4)
+# AS99127F rev.2 (same as w83782d)
+#   compute in5 (5.14 * @) - 14.91 , (@ + 14.91) / 5.14
+#   compute in6 (3.14 * @) -  7.71 , (@ +  7.71) / 3.14
+
+# Depending on your motherboard, you may have to use any of two formulae
+# for temp2. Quoting Artur Gawryszczak (edited to reflect subsequent fixes
+# to the driver):
+# "I guess, that the formula "(@*15/43)+25, (@-25)*43/15" is correct
+# for those Asus motherboards, which get CPU temperature from internal
+# thermal diode (Pentium Coppermine, and above), while no formula is needed
+# for Athlon/Duron boards, which use a thermistor in the socket."
+# An alternative formula was then found and reported by Victor G. Marimon.
+
+# Asus A7V133, Asus A7M266
+#   No compute line is needed
+# Asus CUV4X, Asus A7V8X
+#   compute temp2 (@*15/43)+25, (@-25)*43/15
+# Asus CUSL2, Asus CUV266-DLS, Asus TUSL2-C
+#   compute temp2 (@*30/43)+25, (@-25)*43/30
+
+# See comments above if temp3 looks bad. What works for temp2 is likely
+# to work for temp3 for dual-CPU boards, such as the CUV4X-D.
+
+# Most Asus boards have temperatures settled like that:
+    label temp1 "M/B Temp"
+    label temp2 "CPU Temp"
+# However, some boards have them swapped (A7N8X Deluxe rev.2,
+# A7N8X-E Deluxe rev.2, CUV4X):
+#   label temp1 "CPU Temp"
+#   label temp2 "M/B Temp"
+
+# Most boards have no temp3 by default, except for dual-CPU boards.
+#   label temp3 "CPU 2 Temp"
+#   ignore temp3
+
+# set limits to  5% for the critical voltages
+# set limits to 10% for the non-critical voltages
+# set limits to 20% for the battery voltage
+
+#    set in0_min cpu0_vid*0.95
+#    set in0_max cpu0_vid*1.05
+#    set in1_min cpu0_vid*0.95
+#    set in1_max cpu0_vid*1.05
+#    set in2_min 3.3 * 0.95
+#    set in2_max 3.3 * 1.05
+#    set in3_min 5.0 * 0.95
+#    set in3_max 5.0 * 1.05
+#    set in4_min 12 * 0.90
+#    set in4_max 12 * 1.10
+#    set in5_min -12 * 0.90
+#    set in5_max -12 * 1.10
+#    set in6_min -5 * 0.95
+#    set in6_max -5 * 1.05
+
+# examples for temperature limits
+#    set temp1_max      40
+#    set temp1_max_hyst 37
+#    set temp2_max      52
+#    set temp2_max_hyst 47
+#    set temp3_max      52
+#    set temp3_max_hyst 47
+
+
+chip "gl518sm-*"
+
+# IMPORTANT: in0, in1, and in2 values (+5, +3, and +12) CANNOT be read
+#
+# Factors and labels taken from GL518SM datasheet, they seem to give
+# reasonable values with EISCA connected Fan78
+
+  label in0 "+5V"
+  label in1 "+3.3V"
+  label in2 "+12V"
+  label in3 "Vcore"
+
+# in2 depends on external resistors (4,7k and 15k assumed here)
+# in1 and in3 require no scaling
+
+  compute in2 (197/47)*@  ,  @/(197/47)
+
+#  set in0_min 4.8
+#  set in0_max 5.2
+#  set in1_min 3.20
+#  set in1_max 3.40
+#  set in2_min 11.0
+#  set in2_max 13.0
+#  set in3_min 2.10
+#  set in3_max 2.30
+#  set fan2_min 0
+
+
+chip "gl520sm-*"
+
+# Factors and labels taken from GL520SM datasheet
+
+# The GL520SM has two application modes. In mode 1 it has two thermistor
+# inputs, in mode 2 it has only one and an extra (negative) voltage input.
+# The mode is supposed to be set properly by your BIOS so you should not
+# need to change it. Note that you have either temp2 or in4, not both.
+
+  label in0 "+5V"
+  label in1 "+3.3V"
+  label in2 "+12V"
+  label in3 "Vcore"
+  label in4 "-12V"
+
+# in1 and in3 require no scaling
+# in2 depends on external resistors (4,7k and 15k assumed)
+
+# in4 = ((R1+R2)/R2)*@ - (R1/R2)*vdd
+#
+#       -12 --| R1 |---t---| R2 |-- +5
+#                      |
+#                    vin4
+#
+
+  compute in2 (197/47)*@  ,  @/(197/47)
+  compute in4 (5*@)-(4*in0_input) , (@+4*in0_input)/5
+
+#  set in0_min 4.8
+#  set in0_max 5.2
+#  set in1_min 3.20
+#  set in1_max 3.40
+#  set in2_min 11.0
+#  set in2_max 13.0
+#  set in3_min 2.10
+#  set in3_max 2.30
+
+
+chip "lm80-*"
+
+# The values below should be correct if you own a qdi BX (brilliant1)
+# mainboard. Many thanks go to Peter T. Breuer for helping us figure
+# out how to handle the LM80.
+
+# For positive voltages (in0..in4), two resistors are used, with the following
+# formula (R1,R2: resistor values, Vs: read voltage, Vin: pin voltage)
+#   R1 = R2 * (Vs/Vin - 1)
+# For negative voltages (in5, in6) two resistors are used, with the following
+# formula (R3,R4: resistor values, Vs: read voltage, Vin: pin voltage,
+# V5: +5V)
+#   R3 = R4 * (Vs - Vin) / (Vin - V5)
+
+# Here are the official LM80 data sheet values.
+#       Vs      R1,R3   R2,R4    Vin
+#       +2.5V    23.7    75     +1.9
+#       +3.3V    22.1    30     +1.9
+#       +5.0     24      14.7   +1.9
+#      +12.0    160      30.1   +1.9
+#      -12.0    160      35.7   +1.9
+#       -5.0     36      16.2   +1.9
+
+# Now curiously enough, VCore is connected with (unknown) resistors, which
+# translate a +2.8V to +1.9V. So we use that in the computations below.
+
+    label in0 "+5V"
+    label in1 "VTT"
+    label in2 "+3.3V"
+    label in3 "+Vcore"
+    label in4 "+12V"
+    label in5 "-12V"
+    label in6 "-5V"
+
+    compute in0 (24/14.7 + 1) * @ ,       @ / (24/14.7 + 1)
+    compute in2 (22.1/30 + 1) * @ ,       @ / (22.1/30 + 1)
+    compute in3 (2.8/1.9) * @,            @ * 1.9/2.8
+    compute in4 (160/30.1 + 1) * @,       @ / (160/30.1 + 1)
+    compute in5 (160/35.7)*(@ - in0_input) + @, (@ + in0_input * 160/35.7)/ (1 + 160/35.7)
+    compute in6 (36/16.2)*(@ - in0_input) + @,  (@ + in0_input * 36/16.2) / (1 + 36/16.2)
+
+#    set in0_min 5 * 0.95
+#    set in0_max 5 * 1.05
+# What is your VTT? It is probably not this value...
+#    set in1_min 2*0.95
+#    set in1_max 2*1.05
+#    set in2_min 3.3 * 0.95
+#    set in2_max 3.3 * 1.05
+# What is your VCore? It is probably not this value...
+#    set in3_min 1.9 * 0.95
+#    set in3_max 1.9 * 1.05
+#    set in4_min 12 * 0.95
+#    set in4_max 12 * 1.05
+#    set in5_min -12 * 1.05
+#    set in5_max -12 * 0.95
+#    set in6_min -5 * 1.05
+#    set in6_max -5 * 0.95
+
+# All 4 of these limits apply to the single temperature sensor.
+# "crit" may or may not do anything on your motherboard but it should
+#  be set higher than the "max" thresholds.
+#    set temp1_max_hyst   45
+#    set temp1_max        52
+#    set temp1_crit_hyst  57
+#    set temp1_crit       62
+
+
+chip "via686a-*"
+
+# VIA is very specific about the voltage sensor inputs, and our labels
+# reflect what they say.  Unfortunately, they are not at all specific about
+# how to convert any of the register values to real units.  Fortunately,
+# Jonathan Yew and Alex van Kaam came through with some data for temp
+# conversion and formulae for voltage conversion. However, the conversions
+# should be regarded as our best guess - YMMV.
+
+# On the Tyan S1598, the 2.5V sensor reads 0 and is not displayed in the BIOS.
+# Linas Vepstas reports that this sensor shows nothing of
+# interest on the Abit KA7 (Athlon), and is also not displayed in the BIOS.
+# Likewise, Johannes Drechsel-Burkhard reports that this
+# sensor is unavailable in the BIOS of his MSI K7T Pro (Thunderbird).  So,
+# if you have one of these boards you may want to uncomment the 'ignore in1'
+# line below.
+
+    label in0 "CPU core"
+    label in1 "+2.5V"
+    #ignore in1
+    label in2 "I/O"
+    label in3 "+5V"
+    label in4 "+12V"
+
+    label fan1  "CPU Fan"
+    label fan2  "P/S Fan"
+
+# VIA suggests that temp3 is an internal temp sensor for the 686a.  However,
+# on the Tyan S1598 as well as the Abit KA7 (Athalon), the absolute values
+# of the readings from that sensor are not valid.  The readings do seem to
+# correlate with temp changes, but the conversion factor may be quite
+# different from temp1 & temp2 (as noted above, VIA has not provided
+# conversion info).  So, you may wish to 'ignore temp3'.
+
+# Johannes Drechsel-Burkhard notes that on his MSI K7T Pro,
+# temp1 is the CPU temp and temp2 is the SYS temp. Hugo van der Merwe notes
+# the same for his Gigabyte GA-7DXC, Olivier Martin for his Gigabyte
+# GA-7ZM and Patrick Thomson for his Chaintech CT-5ATA.
+# Thomas Anglmaier notes: on Epox EP-7kxa temp2 is CPU and temp1 is SYS.
+
+    label temp1 "SYS Temp"
+    label temp2 "CPU Temp"
+    label temp3 "SBr Temp"
+    #ignore temp3
+
+# Set your CPU core limits here if the BIOS did not.
+
+    #set in0_min 1.70 * 0.95
+    #set in0_max 1.70 * 1.05
+
+# Other voltage values are standard so we can enforce the limits.
+
+#    set in1_min 2.5 * 0.95
+#    set in1_max 2.5 * 1.05
+#    set in2_min 3.3 * 0.95
+#    set in2_max 3.3 * 1.05
+#    set in3_min 5 * 0.9
+#    set in3_max 5 * 1.1
+#    set in4_min 12 * 0.9
+#    set in4_max 12 * 1.1
+
+# Set your temp limits here.  Remember, 'tempX_max' is the temp at which an
+# alarm is triggered, and 'tempX_max_hyst' is the temp at which an alarm turns off.
+# Setting tempX_max_hyst to a few degrees below the corresponding tempX_max
+# prevents an oscillation between alarm on and off states.  This kind of
+# oscillation is known as hyteresis, thus the name.  (You typically get the
+# most serious and troublesome hysteresis when a sensor triggers something to
+# reduce the temp, thus creating a negative feedback loop.  Even without that,
+# we would still get some oscillation when the temp hovers around the limit
+# due to noise.)
+
+#    set temp1_max_hyst 40
+#    set temp1_max      45
+#    set temp2_max_hyst 55
+#    set temp2_max      60
+#    set temp3_max_hyst 60
+#    set temp3_max      65
+
+# You could set your fan limits too, but the defaults should be fine.
+
+    #set fan1_min 5000
+    #set fan2_min 5000
+
+
+chip "adm1025-*" "ne1619-*"
+
+# The ADM1025 has integrated scaling resistors, rather
+# than external resistors common to most sensor devices.
+# These apply to the 6 voltage inputs in0-in5 (+2.5V, VCore,
+# +3.3V, +5V, +12V, VCC). As the scaling is fixed inside
+# the chip for these inputs, it is fairly certain that the
+# motherboard connections match these labels, and that the
+# driver computations are correct. Therefore they do not need to
+# be overridden here.
+
+    label in0 "+2.5V"
+    label in1 "VCore"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+    label in5 "VCC"
+
+# Tolerate a 5% deviance for CPU power-supply
+#    set in1_min cpu0_vid * 0.95
+#    set in1_max cpu0_vid * 1.05
+# Tolerate a 10% deviance for other voltages
+#    set in0_min 2.5 * 0.90
+#    set in0_max 2.5 * 1.10
+#    set in2_min 3.3 * 0.90
+#    set in2_max 3.3 * 1.10
+#    set in3_min 5.0 * 0.90
+#    set in3_max 5.0 * 1.10
+#    set in4_min 12 * 0.90
+#    set in4_max 12 * 1.10
+#    set in5_min 3.3 * 0.90
+#    set in5_max 3.3 * 1.10
+
+# Depending on how your chipset is hardwired, you may or may not have
+# +12V readings (will show as 0.0V if you don't have it).
+#   ignore in4
+
+# VCC is the power-supply voltage of the ADM1025 chipset, generally
+# redundant with +3.3V so you may want to hide it.
+#   ignore in5
+
+# Temperatures
+    label temp1 "CPU Temp"
+    label temp2 "M/B Temp"
+#    set temp1_min 10
+#    set temp1_max 60
+#    set temp2_min 10
+#    set temp2_max 45
+
+
+chip "lm87-*" "adm1024-*"
+#
+# The LM87 has integrated scaling resistors, rather
+# than external resistors common to most sensor devices.
+# These apply to the first 6 voltage inputs in0-in5
+# (+2.5, Vccp1, +3.3, +5, 12, +Vccp2). As the scaling is fixed inside
+# the chip for these inputs, it is fairly certain that the
+# motherboard connections match these labels, and that the
+# driver computations are correct. Therefore they do not need to
+# be overridden here.
+
+   label in0  "+2.5V"
+   label in1  "VCore"
+   label in2  "+3.3V"
+   label in3  "+5V"
+   label in4  "+12V"
+#   label in5  "VCore2"
+
+   label fan1 "CPU Fan"
+#   label fan2 "Case Fan"
+   label temp1 "M/B Temp"
+   label temp2 "CPU Temp"
+#   label temp3 "AUX Temp"
+
+#   set in1_min  cpu0_vid * 0.95
+#   set in1_max  cpu0_vid * 1.05
+#   set in2_min  3.3 * 0.92
+#   set in2_max  3.3 * 1.08
+#   set in3_min    5 * 0.92
+#   set in3_max    5 * 1.08
+#   set in4_min   12 * 0.90
+#   set in4_max   12 * 1.10
+
+# These ones are mutually exclusive with temp3. If you have temp3,
+# comment out these lines as they will trigger errors on "sensors -s".
+#   set in0_min  2.5 * 0.92
+#   set in0_max  2.5 * 1.08
+#   set in5_min  cpu0_vid * 0.95
+#   set in5_max  cpu0_vid * 1.05
+
+# Increase fan clock dividers if your fans read 0 RPM while you know
+# they are connected and running.
+#   set fan1_div 4
+#   set fan2_div 4
+
+#   set fan1_min 3000
+#   set fan2_min 3000
+
+#   set temp1_min   5
+#   set temp1_max  65
+#   set temp2_min   5
+#   set temp2_max  70
+
+# Uncomment if you actually have temp3 (which means you don't have 2.5V
+# nor Vccp2, as they are mutually exclusive).
+#   set temp3_min   5
+#   set temp3_max  70
+
+# LM87 AIN1 and AIN2 Section
+# -12 and -5 may be reversed on your board, this is
+# just a guess, the datasheet gives no guidance.
+#   label in6 "-12V"
+#   label in7 "-5V"
+#   compute in6 (7.50 * @) - 21.45  ,  (@ + 21.45) / 7.50
+#   compute in7 (4.05 * @) - 10.07  ,  (@ + 10.07) / 4.05
+#   set in6_min -12 * 0.95
+#   set in7_min -5 * 0.95
+#   set in6_max -12 * 1.05
+#   set in7_max -5 * 1.05
+
+
+chip "it87-*" "it8712-*"
+
+# The values below have been tested on Asus CUSI, CUM motherboards.
+
+# Voltage monitors as advised in the It8705 data sheet
+
+    label in0 "VCore 1"
+    label in1 "VCore 2"
+    label in2 "+3.3V"
+    label in3 "+5V"
+    label in4 "+12V"
+    label in5 "-12V"
+    label in6 "-5V"
+    label in7 "Stdby"
+    label in8 "VBat"
+
+# Incubus Saturnus reports that the IT87 chip on Asus A7V8X-X seems
+# to report the VCORE voltage approximately 0.05V higher than the board's
+# BIOS does. Although it doesn't make much sense physically, uncommenting
+# the next line should bring the readings in line with the BIOS' ones in
+# this case.
+# compute in0 -0.05+@ , @+0.05
+
+# If 3.3V reads around 1.65V, uncomment the following line:
+#    compute in2   2*@ , @/2
+
+    compute in3 ((6.8/10)+1)*@ ,  @/((6.8/10)+1)
+# A number of Gigabyte boards (GA-8IPE1000Pro, GA-8KNXP, GA-7N400-L) use
+# a different resistor combination for +5V:
+#    compute in3 ((10/10)+1)*@ ,  @/((10/10)+1)
+
+    compute in4 ((30/10) +1)*@  , @/((30/10) +1)
+# For this family of chips the negative voltage equation is different from
+# the lm78.  The chip uses two external resistor for scaling but one is
+# tied to a positive reference voltage.  See ITE8705/12 datasheet (SIS950
+# data sheet is wrong)
+# Vs = (1 + Rin/Rf) * Vin - (Rin/Rf) * Vref.
+# Vref = 4.096 volts, Vin is voltage measured, Vs is actual voltage.
+
+# The next two are negative voltages (-12 and -5).
+# The following formulas must be used. Unfortunately the datasheet
+# does not give recommendations for Rin, Rf, but we can back into
+# them based on a nominal +2V input to the chip, together with a 4.096V Vref.
+# Formula:
+#    actual V = (Vmeasured * (1 + Rin/Rf)) - (Vref * (Rin/Rf))
+#    For -12V input use Rin/Rf = 6.68
+#    For -5V input use Rin/Rf = 3.33
+# Then you can convert the forumula to a standard form like:
+    compute in5 (7.67 * @) - 27.36  ,  (@ + 27.36) / 7.67
+    compute in6 (4.33 * @) - 13.64  ,  (@ + 13.64) / 4.33
+#
+# this much simpler version is reported to work for a
+# Elite Group K7S5A board
+#
+#   compute in5 -(36/10)*@, -@/(36/10)
+#   compute in6 -(56/10)*@, -@/(56/10)
+#
+    compute in7 ((6.8/10)+1)*@ ,  @/((6.8/10)+1)
+
+#    set in0_min 1.5 * 0.95
+#    set in0_max 1.5 * 1.05
+#    set in1_min 2.4
+#    set in1_max 2.6
+#    set in2_min 3.3 * 0.95
+#    set in2_max 3.3 * 1.05
+#    set in3_min 5.0 * 0.95
+#    set in3_max 5.0 * 1.05
+#    set in4_min 12 * 0.95
+#    set in4_max 12 * 1.05
+#    set in5_max -12 * 0.95
+#    set in5_min -12 * 1.05
+#    set in6_max -5 * 0.95
+#    set in6_min -5 * 1.05
+#    set in7_min 5 * 0.95
+#    set in7_max 5 * 1.05
+    #the chip does not support in8 min/max
+
+# Temperature
+#
+# Important - if your temperature readings are completely whacky
+# you probably need to change the sensor type.
+# Adujst and uncomment the appropriate lines below.
+#
+# 2 = thermistor; 3 = thermal diode; 0 = unused
+#   set temp1_type 3
+#   set temp2_type 3
+#   set temp3_type 3
+# If a given sensor isn't used, you will probably want to ignore it
+# (see ignore statement right below).
+
+    label temp1       "M/B Temp"
+#    set   temp1_max   40
+#    set   temp1_min   15
+    label temp2       "CPU Temp"
+#    set   temp2_max   45
+#    set   temp2_min   15
+#   ignore temp3
+#    set   temp3_max   45
+#    set   temp3_min   15
+
+# The A7V8X-X has temperatures inverted, and needs a conversion for
+# CPU temp. Thanks to Preben Randhol for the formula.
+#   label temp1       "CPU Temp"
+#   label temp2       "M/B Temp"
+#   compute temp1     (-15.096+1.4893*@), (@+15.096)/1.4893
+
+# The A7V600 also has temperatures inverted, and needs a different
+# conversion for CPU temp. Thanks to Dariusz Jaszkowski for the formula.
+#   label temp1       "CPU Temp"
+#   label temp2       "M/B Temp"
+#   compute temp1     (@+128)/3, (3*@-128)
+
+# Fans
+#   set fan1_min 0
+#   set fan2_min 3000
+#   ignore fan3
+#   set fan3_min 3000
+
+
+chip "it8716-*"
+
+# Voltages
+
+    label  in0  "VCore"
+    label  in1  "VDDR"
+    label  in2  "+3.3V"    # VCC3
+    label  in3  "+5V"      # VCC
+    label  in4  "+12V"
+#   label  in5  "-12V"
+#   label  in6  "-5V"
+    label  in7  "5VSB"     # VCCH
+    label  in8  "VBat"
+
+    compute in3  ((6.8/10)+1)*@ , @/((6.8/10)+1)
+    compute in4  ((30/10)+1)*@  , @/((30/10)+1)
+#   compute in5  (1+232/56)*@ - 4.096*232/56 , (@ + 4.096*232/56)/(1+232/56)
+#   compute in6  (1+120/56)*@ - 4.096*120/56 , (@ + 4.096*120/56)/(1+120/56)
+    compute in7  ((6.8/10)+1)*@ , @/((6.8/10)+1)
+
+# If vid (nominal CPU voltage) isn't correct, hardcode the correct value
+# instead.
+#    set in0_min  cpu0_vid * 0.95
+#    set in0_max  cpu0_vid * 1.05
+#    set in1_min  1.8 * 0.95
+#    set in1_max  1.8 * 1.05
+#    set in2_min  3.3 * 0.95
+#    set in2_max  3.3 * 1.05
+#    set in3_min    5 * 0.95
+#    set in3_max    5 * 1.05
+#    set in4_min   12 * 0.95
+#    set in4_max   12 * 1.05
+#    set in5_max  -12 * 0.95
+#    set in5_min  -12 * 1.05
+#    set in6_max   -5 * 0.95
+#    set in6_min   -5 * 1.05
+#    set in7_min    5 * 0.95
+#    set in7_max    5 * 1.05
+# The chip does not support in8 min/max
+
+# Temperatures
+
+# If you are lucky, the BIOS has set the proper sensor types for you.
+# If your temperature readings are completely whacky you probably
+# need to change the sensor types. Adujst and uncomment the
+# appropriate lines below.
+#
+# 2 = thermistor; 3 = thermal diode; 0 = unused
+#   set temp1_type  3
+#   set temp2_type  3
+#   set temp3_type  3
+
+# If a given sensor isn't used, you will probably want to ignore it
+# as well (see ignore statement right below).
+# The CPU sensor can be any of temp1, temp2 or temp3 - it's motherboard
+# dependent. Same for the motherboard temperature.
+
+#   label  temp1  "CPU Temp"
+#   label  temp2  "M/B Temp"
+#   ignore temp3
+
+#   set temp1_max  60
+#   set temp1_min  10
+#   set temp2_max  50
+#   set temp2_min  10
+
+# Fans
+
+# The CPU fan can be any of fan1, fan2 or fan3 - it's motherboard
+# dependent. Same for the case fan.
+
+#   label  fan1 "CPU Fan"
+#   label  fan2 "Case Fan"
+#   ignore fan3
+
+#   set fan1_min 2000
+#   set fan2_min 2000
+
+
+chip "fscpos-*"
+# Fujitsu-Siemens Poseidon chip
+
+# Temperature
+
+    label temp1       "CPU Temp"
+    label temp2       "M/B Temp"
+    label temp3       "Aux Temp"
+
+# Fans
+
+# Voltage
+
+    label in0         "+12V"
+    label in1         "+5V"
+    label in2         "Battery"
+
+
+chip "fscscy-*"
+# Fujitsu-Siemens Scylla chip
+
+# Temperature
+
+    label temp1       "CPU0 Temp"
+    label temp2       "CPU1 Temp"
+    label temp3       "M/B Temp"
+    label temp4       "Aux Temp"
+
+# Fans
+
+    label  fan1       "CPU Fan"
+    label  fan2       "CPU Fan"
+
+# Voltage
+
+    label in0         "+12V"
+    label in1         "+5V"
+    label in2         "+3.3V"
+
+
+chip "fscher-*"
+# Fujitsu-Siemens Hermes chip
+
+# Temperature
+    label temp1       "CPU Temp"
+    label temp2       "M/B Temp"
+    label temp3       "Aux Temp"
+
+# Fans
+    label fan1        "PSU Fan"
+    label fan2        "CPU Fan"
+    label fan3        "Aux Fan"
+
+# Voltage
+    label in0         "+12V"
+    label in1         "+5V"
+    label in2         "Battery"
+
+# Uncomment the computes line below when using the old fscher driver, leave
+# them commented when using the newer unified fschmd driver
+#
+#                            M    R             O               O                  M    R
+#    compute in0       (@ * (49 * 33) / 255) + (0 / 100), (@ - (0 / 100)) * 255 / (49 * 33)
+#    compute in1       (@ * (20 * 33) / 255) + (0 / 100), (@ - (0 / 100)) * 255 / (20 * 33)
+#    compute in2       (@ * (10 * 33) / 255) + (0 / 100), (@ - (0 / 100)) * 255 / (10 * 33)
+
+
+chip "pcf8591-*"
+#
+#    values for the Philips PCF8591 chip
+#
+# Analog inputs
+
+# You may discard ch2 and ch3 if you don't use them (depends on the input
+# configuration)
+#  ignore in2
+#  ignore in3
+
+   label  in0         "Chan. 0"
+   label  in1         "Chan. 1"
+   label  in2         "Chan. 2"
+   label  in3         "Chan. 3"
+
+# The driver assumes Vref = 2.56V and Agnd = 0V. If it doesn't match
+# your hardware, you have to use compute lines. The example below is
+# correct for Vref = 5.0V and Agnd = 0V.
+#  compute in0 (@ * 500 / 256), (@ * 256 / 500)
+#  compute in1 (@ * 500 / 256), (@ * 256 / 500)
+#  compute in2 (@ * 500 / 256), (@ * 256 / 500)
+#  compute in3 (@ * 500 / 256), (@ * 256 / 500)
+
+
+chip "adm1021-*" "adm1023-*" "max1617-*" "max1617a-*" "thmc10-*" "lm84-*" "gl523sm-*" "mc1066-*"
+
+   label temp1 		"Board Temp"
+   label temp2		"CPU Temp"
+#   set temp1_min	40
+#   set temp1_max	70
+#   set temp2_min	40
+#   set temp2_max	70
+
+chip "lm83-*"
+
+   label temp1 "M/B Temp"
+   label temp2 "D1 Temp"
+   label temp3 "CPU Temp"
+   label temp4 "D3 Temp"
+
+# ignore D1 and/or D3 readings if not used
+#   ignore temp2
+#   ignore temp4
+
+# change high limits to fit your needs
+#   set temp1_max 55
+#   set temp2_max 60
+#   set temp3_max 65
+#   set temp4_max 60
+
+# change critical limit to fit your needs
+# only one limit for all four temperatures
+# should be higher than each of the high limits above
+#   set temp3_crit 85
+
+
+chip "max1619-*"
+
+   label temp1 "M/B Temp"
+   label temp2 "CPU Temp"
+
+# change high and low limits to fit your needs
+#   set temp2_min   10
+#   set temp2_max   100
+
+# change critical limit and hysteresis to fit your needs
+#   set temp2_crit       50
+#   set temp2_crit_hyst  40
+
+
+chip "lm90-*" "adm1032-*" "lm86-*" "max6657-*" "adt7461-*" "max6680-*" "w83l771-*"
+
+   label temp1 "M/B Temp"
+   label temp2 "CPU Temp"
+
+# change high and low limits to fit your needs
+#   set temp1_min 10
+#   set temp1_max 55
+#   set temp2_min 10
+#   set temp2_max 66
+
+# change critical limits to fit your needs
+# should be higher than the corresponding high limit above
+#   set temp1_crit 75
+#   set temp2_crit 85
+
+# change the hysteresis values (to critical limits) to fit your needs
+# note #1: hyst2 will be automatically set with the same delta
+# note #2: the internal register, which stores a single, relative value
+# for both channels, cannot hold values greater than 31, so the delta
+# between critical temperatures and respective absolute hysteresis can
+# never exceed this value
+#   set temp1_crit_hyst 70
+
+chip "lm89-*" "lm99-*"
+
+   label temp1 "G/C Temp"
+   label temp2 "GPU Temp"
+
+# note #1: the LM99 needs the compute statement below if you're running
+# a kernel <= 2.6.27; since kernel 2.6.28 the offset is handled in the
+# lm90 driver directly instead
+# note #2: there is no way for us to differentiate between a LM89 and a
+# LM99; you have to know what you have; the driver will assume a LM99,
+# use the force_lm86 module parameter if you have a LM89
+#   compute temp2 @+16, @-16
+
+# change high and low limits to fit your needs
+#   set temp1_min  10
+#   set temp1_max  90
+#   set temp2_min  10
+#   set temp2_max 100
+
+# change critical limits to fit your needs
+# should be higher than the corresponding high limit above
+#   set temp1_crit 100
+#   set temp2_crit 110
+
+# change the hysteresis values (to critical limits) to fit your needs
+# note #1: hyst2 will be automatically set with the same delta
+# note #2: the internal register, which stores a single, relative value
+# for both channels, cannot hold values greater than 31, so the delta
+# between critical temperatures and respective absolute hysteresis can
+# never exceed this value
+#   set temp1_crit_hyst 105
+
+
+chip "lm63-*"
+
+   label temp1       "M/B Temp"
+   label temp2       "CPU Temp"
+   label fan1        "CPU Fan"
+
+# Change limits to fit your needs. Note that temp2_crit is read-only.
+#   set temp1_max        50
+#   set temp2_min        10
+#   set temp2_max        70
+#   set temp2_crit_hyst  75
+#   set fan1_min         2000
+
+
+chip "vt1211-*"
+
+    label in0 "+3.3V"
+    label in1 "+2.5V"
+    label in2 "VCore"
+    label in3 "+5V"
+    label in4 "+12V"
+    label in5 "+3.3V"
+
+    label temp1 "CPU Temp"
+    label temp2 "Int Temp"
+
+    label fan1 "Case Fan"
+    label fan2 "CPU Fan"
+
+# All voltage calculations have the form
+#    compute inX  @ * (1 + R1 / R2), @ / (1 + R1 / R2)
+#
+# The following are the resistor values as recommended by VIA
+#	Voltage		R1	R2
+#	-------		----	----
+#	VCore	 	-	-	(no scaling)
+#	 3.3		6.8k	10k
+#	 2.5		2k	10k
+#	 5.0		14k	10k
+#	12.0		47k	10k
+#
+# The VT1211 internal 3.3V (in5) is scaled by the driver and doesn't
+# need to be adjusted here. VCore doesn't need scaling at all.
+
+    compute in0  @ * (1 + 6.8 / 10),  @ / (1 + 6.8 / 10)
+    compute in1  @ * (1 +   2 / 10),  @ / (1 +   2 / 10)
+    compute in3  @ * (1 +  14 / 10),  @ / (1 +  14 / 10)
+    compute in4  @ * (1 +  47 / 10),  @ / (1 +  47 / 10)
+
+#   set in0_min 3.3 * 0.95
+#   set in0_max 3.3 * 1.05
+#   set in1_min 2.5 * 0.95
+#   set in1_max 2.5 * 1.05
+# If your vid is wrong, hardcode the CPU voltage (e.g. 1.4)
+#   set in2_min cpu0_vid * 0.97
+#   set in2_max cpu0_vid * 1.03
+#   set in3_min 5.0 * 0.95
+#   set in3_max 5.0 * 1.05
+#   set in4_min 12.0 * 0.90
+#   set in4_max 12.0 * 1.10
+    set in5_min 3.3 * 0.95
+    set in5_max 3.3 * 1.05
+
+# The temperature calculations are of the form
+#    compute tempX  (@ - Offset) / Gain, (@ * Gain) + Offset
+#
+# The following are the gain and offset values as recommended by VIA
+#	Diode Type	Gain	Offset
+#	----------	----	------
+# 	Intel CPU	0.9528	88.638
+#			0.9686	65.000	*)
+#	VIA C3 Ezra	0.9528	83.869
+#	VIA C3 Ezra-T	0.9528	73.869
+#
+# *) These are the values from the previous sensors.conf. I don't know
+# where they came from or how they got derived.
+#
+# The VT1211 internal temperature (temp2) is scaled by the driver
+# and doesn't need to be adjusted here.
+
+    compute temp1  (@ - 73.869) / 0.9528,  (@ * 0.9528) + 73.869
+
+# The thermistor calculations are of the form
+#    compute tempX  1 / (1 / 298.15 - (` Vmax / @ - 1)) / B) - 273.15, \
+#                   Vmax / (1 + (^ (B / 298.15 - B / (273.15 + @))))
+#
+# B is the thermistor beta value, Vmax is the reference voltage, '^' is the
+# exp() operator and '`' is the ln() operator.
+# Given B = 3435 and Vmax = 2.2V and assuming that the thermistor forms a
+# resistor divider with a resistor equal to the thermistor's nominal value at
+# 25 degrees C, the following compute lines can be used:
+
+    compute temp3  1 / (1 / 298.15 - (` (2.2 / @ - 1)) / 3435) - 273.15, \
+                   2.2 / (1 + (^ (3435 / 298.15 - 3435 / (273.15 + @))))
+    compute temp4  1 / (1 / 298.15 - (` (2.2 / @ - 1)) / 3435) - 273.15, \
+                   2.2 / (1 + (^ (3435 / 298.15 - 3435 / (273.15 + @))))
+    compute temp5  1 / (1 / 298.15 - (` (2.2 / @ - 1)) / 3435) - 273.15, \
+                   2.2 / (1 + (^ (3435 / 298.15 - 3435 / (273.15 + @))))
+    compute temp6  1 / (1 / 298.15 - (` (2.2 / @ - 1)) / 3435) - 273.15, \
+                   2.2 / (1 + (^ (3435 / 298.15 - 3435 / (273.15 + @))))
+    compute temp7  1 / (1 / 298.15 - (` (2.2 / @ - 1)) / 3435) - 273.15, \
+                   2.2 / (1 + (^ (3435 / 298.15 - 3435 / (273.15 + @))))
+
+#    set temp1_max_hyst 80
+#    set temp1_max      85
+#    set temp2_max_hyst 60
+#    set temp2_max      65
+#    set temp3_max_hyst 60
+#    set temp3_max      65
+#    set temp4_max_hyst 40
+#    set temp4_max      45
+
+#    set fan1_min 3000
+#    set fan2_min 3000
+
+chip "vt8231-*"
+
+    label in1 "+2.5V"
+    label in2 "VCore"
+    label in3 "+5V"
+    label in4 "+12V"
+    label in5 "+3.3V"
+
+    label temp1 "CPU Temp"
+    label temp2 "M/B Temp"
+
+# Here are the resistor values as recommended by VIA:
+#   Voltage			R1	R2
+#   VCore			no scaling
+#    2.5			2k	10k
+#    3.5 (3.3V ext.)		6.8k	10k
+#    5.0			14k	10k
+#   12.0			47k	10k
+
+#   compute in0  @ * (1 + 6.8 / 10),  @ / (1 + 6.8 / 10)
+    compute in1  @ * (1 +   2 / 10),  @ / (1 +   2 / 10)
+    compute in3  @ * (1 +  14 / 10),  @ / (1 +  14 / 10)
+    compute in4  @ * (1 +  47 / 10),  @ / (1 +  47 / 10)
+# in5 is scaled internally so scaling is done by the driver.
+
+#   set in0_min 2.5 * 0.95
+#   set in0_max 2.5 * 1.05
+#   set in1_min 2.5 * 0.95
+#   set in1_max 2.5 * 1.05
+# Replace "2.0" with your nominal CPU voltage for in2.
+#   set in2_min 2.0 * 0.95
+#   set in2_max 2.0 * 1.05
+#   set in3_min 5.0 * 0.95
+#   set in3_max 5.0 * 1.05
+#   set in4_min 12.0 * 0.95
+#   set in4_max 12.0 * 1.05
+#   set in5_min 3.3 * 0.95
+#   set in5_max 3.3 * 1.05
+
+# For Intel CPU:
+    compute temp1  (@ - 65) / 0.9686,  (@ * 0.9686) + 65
+
+# For VIA EPIA CPU (provided by Roger Lucas):
+#   compute temp1  (@ - 45) / 0.7809,  (@ * 0.7809) + 45
+
+# Thermistor calculations
+# 3435 is the thermistor beta, 2.2 is the reference voltage.
+# '^' is the e**x operator; '`' is the ln(x) operator
+# This assumes that the thermistor forms a resistor divider with a resistor
+# equal to its nominal value at 25 degrees C.
+
+    compute temp2  1 / (1 / 298.15 - (` (2.2 / @ - 1)) / 3435) - 273.15, \
+                   2.2 / (1 + (^ (3435 / 298.15 - 3435 / (273.15 + @))))
+    compute temp3  1 / (1 / 298.15 - (` (2.2 / @ - 1)) / 3435) - 273.15, \
+                   2.2 / (1 + (^ (3435 / 298.15 - 3435 / (273.15 + @))))
+
+#    set temp1_max_hyst 60
+#    set temp1_max      65
+#    set temp2_max_hyst 40
+#    set temp2_max      45
+
+#    set fan1_min 3000
+#    set fan2_min 3000
+
+
+chip "smsc47m1-*"
+
+# SMSC LPC47M10x, LPC47M13x, LPC47M14x and LPC47B27x chips
+
+#    set fan1_min 3000
+#    set fan2_min 3000
+
+chip "smsc47m192-*"
+
+# Temperature and voltage input from SMSC LPC47M192 and LPC47M997 chips
+# This example works on a Gigabyte K8U motherboard
+# Voltages are scaled internally, no computations needed
+
+    label in0 "+2.5V"
+#    set in0_min  2.5 * 0.95
+#    set in0_max  2.5 * 1.05
+
+    label in1 "VCore"
+#    set in1_min  cpu0_vid * 0.95
+#    set in1_max  cpu0_vid * 1.05
+
+    label in2 "+3.3V"
+#    set in2_min  3.3 * 0.95
+#    set in2_max  3.3 * 1.05
+
+    label in3 "+5V"
+#    set in3_min  5.0 * 0.95
+#    set in3_max  5.0 * 1.05
+
+    label in4 "+12V"
+#    set in4_min  12.0 * 0.95
+#    set in4_max  12.0 * 1.05
+
+    label in5 "VCC"
+#    set in5_min  3.3 * 0.95
+#    set in5_max  3.3 * 1.05
+
+    label in6 "+1.5V"
+#    set in6_min  1.5 * 0.95
+#    set in6_max  1.5 * 1.05
+#   ignore in6
+
+    label in7 "+1.8V"
+#    set in7_min  1.8 * 0.95
+#    set in7_max  1.8 * 1.05
+#   Haven't yet heard from any board that has 1.8V connected, so
+#   this might be more appropriate:
+#   ignore in7
+
+    label temp1 "Chip Temp"
+#    set temp1_min   0
+#    set temp1_max   60
+
+    label temp2 "CPU Temp"
+#    set temp2_min   0
+#    set temp2_max   60
+
+    label temp3 "Sys Temp"
+#    set temp3_min   0
+#    set temp3_max   60
+
+#
+# This example was tested vs. Asus P4S333
+#
+chip "asb100-*"
+
+    label in0 "VCore 1"
+    #set in0_min cpu0_vid * 0.95
+    #set in0_max cpu0_vid * 1.05
+
+    label in1 "VCore 2"
+    ignore in1
+    #set in1_min cpu0_vid * 0.95
+    #set in1_max cpu0_vid * 1.05
+
+    label in2 "+3.3V"
+    #set in2_min 3.3 * 0.95
+    #set in2_max 3.3 * 1.05
+
+    label in3 "+5V"
+    compute in3 1.68 * @ ,  @ / 1.68
+    #set in3_min 5.0 * 0.95
+    #set in3_max 5.0 * 1.05
+
+    label in4 "+12V"
+    compute in4 3.8 * @ , @ / 3.8
+    #set in4_min 12  * 0.90
+    #set in4_max 12  * 1.10
+
+    label in5 "-12V (reserved)"
+    #ignore in5
+    compute in5 -@ * 3.97 ,  -@ / 3.97
+    #set in5_max -12 * 0.90
+    #set in5_min -12 * 1.10
+
+    label in6 "-5V (reserved)"
+    #ignore in6
+    compute in6 -@ * 1.666 , -@ / 1.666
+    #set in6_max -5  * 0.95
+    #set in6_min -5  * 1.05
+
+    label temp1 "M/B Temp"
+    #set temp1_max      45
+    #set temp1_max_hyst 40
+
+    label temp2 "CPU Temp (Intel)"
+    #ignore temp2
+    #set temp2_max      60
+    #set temp2_max_hyst 50
+
+    # PWRTMP connector on P4S333, for external sensor
+    label temp3 "Power Temp"
+    #ignore temp3
+    #set temp3_max      45
+    #set temp3_max_hyst 40
+
+
+    # Used for Athlon diode, ignore for P4S333
+    label temp4 "CPU Temp (AMD)"
+    #set temp4_max      60
+    #set temp4_max_hyst 50
+    #ignore temp4
+
+    label fan1 "CPU Fan"
+    #set fan1_div 4
+    #set fan1_min 2000
+
+    label fan2 "Chassis Fan"
+    #set fan2_div 2
+    #set fan2_min 4000
+
+    label fan3 "Power Fan"
+    #set fan3_div 2
+    #set fan3_min 4000
+
+#
+# Sample configuration for the Intel S845WD1-E
+# courtesy of Marcus Schopen
+#
+chip "lm85-*" "lm85b-*" "lm85c-*" "adm1027-*" "adt7463-*" "adt7468-*" "emc6d100-*" "emc6d102-*"
+
+# Voltage inputs
+# Depending on the hardware setup, the ADT7463 and ADT7468 may not have in4.
+   label in0   "V1.5"      # AGP on Intel S845WD1-E
+   label in1   "VCore"
+   label in2   "V3.3"
+   label in3   "V5"
+   label in4   "V12"
+
+# Temperature inputs
+   label temp1  "CPU Temp"
+   label temp2  "Board Temp"
+   label temp3  "Remote Temp"
+
+# Fan inputs
+   label fan1   "CPU Fan"
+
+# Voltage scaling is done on-chip.  No 'compute' directive
+# should be necessary.  If in0 has external scaling set
+# it here.
+
+#   compute in0  @ * 2.5,   @ / 2.5
+
+# Adjust fans speeds for actual pulses per rev
+#   compute fan1  @ * 2,  @ / 2    # 1 pulse per rev
+#   compute fan2  @ / 2,  @ * 2    # 4 pulse per rev
+
+# Ignore fans you (or your motherboard) don't have
+#   ignore fan2
+#   ignore fan3
+#   ignore fan4
+
+# Set voltage limits
+#   set in0_min  1.5 * 0.95
+#   set in0_max  1.5 * 1.05
+#   set in1_min  cpu0_vid * 0.95
+#   set in1_max  cpu0_vid * 1.05
+#   set in2_min  3.3 * 0.95
+#   set in2_max  3.3 * 1.05
+#   set in3_min  5.0 * 0.95
+#   set in3_max  5.0 * 1.05
+#   set in4_min   12 * 0.95
+#   set in4_max   12 * 1.05
+
+# Set Fan limits
+#   set fan1_min 4000
+
+# Set Temp Limits
+#   set temp1_min 10
+#   set temp1_max 50
+#   set temp2_min 10
+#   set temp2_max 35
+#   set temp3_min 10
+#   set temp3_max 35
+
+chip "pc87365-*" "pc87366-*"
+
+# Voltage inputs
+
+   label in7   "Vsb"
+   label in8   "Vdd"
+   label in9   "Vbat"
+   label in10  "AVdd"
+
+   compute in7   @*2, @/2
+   compute in8   @*2, @/2
+   compute in10  @*2, @/2
+
+# These are the operating conditions as recommended by National
+# Semiconductor
+   set in7_min   3.0
+   set in7_max   3.6
+   set in8_min   3.0
+   set in8_max   3.6
+#   set in9_min   2.4
+#   set in9_max   3.6
+   set in10_min  3.0
+   set in10_max  3.6
+
+# Temperature inputs
+
+   label temp1       "CPU0 Temp"
+   label temp2       "CPU1 Temp"
+   label temp3       "S-IO Temp"
+
+#   set temp1_min    0
+#   set temp1_max   70
+#   set temp1_crit  85
+#   set temp2_min    0
+#   set temp2_max   70
+#   set temp2_crit  85
+#   set temp3_min    0
+#   set temp3_max   70
+#   set temp3_crit  85
+
+# Thermistors
+# On some systems, thermistors are used instead of thermal diodes.
+# Note that these are the same pins used differently, so you really
+# can't have them all on a given system.
+#   ignore temp1
+#   ignore temp2
+   ignore temp4
+   ignore temp5
+   ignore temp6
+
+# 3435 is the thermistor beta.
+# This assumes that the thermistor forms a resistor divider with a resistor
+# equal to its nominal value at 25 degres Celsius. If not, change the values.
+# We also assume that you have a working in10 (which is forcibly enabled by
+# default). If not, use 3.3 instead, but you lose accuracy.
+
+   compute temp4  3435 / (` (1 / (1 - @ / in10_input) - 1) + 3435 / 298.15) - 273.15, \
+                  in10_input * (1 - 1 / (1 + (^ (3435 / (273.15 + @) - 3435 / 298.15))))
+   compute temp5  3435 / (` (1 / (1 - @ / in10_input) - 1) + 3435 / 298.15) - 273.15, \
+                  in10_input * (1 - 1 / (1 + (^ (3435 / (273.15 + @) - 3435 / 298.15))))
+   compute temp6  3435 / (` (1 / (1 - @ / in10_input) - 1) + 3435 / 298.15) - 273.15, \
+                  in10_input * (1 - 1 / (1 + (^ (3435 / (273.15 + @) - 3435 / 298.15))))
+
+#   set temp4_min    0
+#   set temp4_max   70
+#   set temp4_crit  85
+#   set temp5_min    0
+#   set temp5_max   70
+#   set temp5_crit  85
+#   set temp6_min    0
+#   set temp6_max   70
+#   set temp6_crit  85
+
+# Fan inputs
+
+# Ignore fans you don't have
+#   ignore fan2
+#   ignore fan3
+
+#   set fan1_min 2000
+#   set fan2_min 2000
+#   set fan3_min 2000
+
+
+chip "adm1030-*"
+
+   label temp1 "SYS Temp"
+   label temp2 "CPU Temp"
+
+#   set temp1_max  60
+#   set temp2_max  70
+#   set temp1_crit 85
+#   set temp2_crit 85
+
+   label fan1 "CPU Fan"
+
+#   set fan1_min 2000
+
+
+chip "adm1031-*"
+
+   label temp1 "SYS Temp"
+   label temp2 "CPU Temp"
+   label temp3 "AUX Temp"
+
+#   set temp1_max  60
+#   set temp2_max  70
+#   set temp3_max  70
+#   set temp1_crit 85
+#   set temp2_crit 85
+#   set temp3_crit 85
+
+   label fan1 "CPU Fan"
+   label fan2 "Case Fan"
+
+#   set fan1_min 2000
+#   set fan2_min 2000
+
+
+chip "w83l785ts-*"
+
+   label temp1 "CPU Diode"
+
+chip "lm92-*"
+
+   label temp1 "CPU Temp"
+
+# Change limits as you see fit
+#   set temp1_min 14
+#   set temp1_max 60
+#   set temp1_crit 72
+# Hysteresis is computed from critical limit
+# The same relative hysteresis is used for all of low, high
+# and critical limits.
+#   set temp1_crit_hyst 70
+
+
+# Winbond W83627EHF configuration originally contributed by Leon Moonen
+# This is for an Asus P5P800, voltages for A8V-E SE.
+chip "w83627ehf-*" "w83627dhg-*"
+
+    label in0 "VCore"
+    label in1 "+12V"
+    label in2 "AVCC"
+    label in3 "3VCC"
+    label in6 "+5V"
+    label in7 "VSB"
+    label in8 "VBAT"
+
+# +12V is in1 and +5V is in6 as recommended by datasheet
+    compute in1 @*(1+(56/10)),  @/(1+(56/10))
+    compute in6 @*(1+(22/10)),  @/(1+(22/10))
+#    set in1_min   12.0*0.9
+#    set in1_max   12.0*1.1
+#    set in6_min   5.0*0.95
+#    set in6_max   5.0*1.05
+
+# Set the 3.3V
+#    set in2_min   3.3*0.95
+#    set in2_max   3.3*1.05
+#    set in3_min   3.3*0.95
+#    set in3_max   3.3*1.05
+#    set in7_min   3.3*0.95
+#    set in7_max   3.3*1.05
+#    set in8_min   3.3*0.95
+#    set in8_max   3.3*1.05
+
+# Fans
+   label fan1      "Case Fan"
+   label fan2      "CPU Fan"
+   label fan3      "Aux Fan"
+#  ignore fan3
+#  ignore fan4
+#  set fan1_min    1200
+#  set fan2_min    1700
+
+# Temperatures
+   label temp1     "Sys Temp"
+   label temp2     "CPU Temp"
+   label temp3     "AUX Temp"
+
+#  ignore temp3
+#  set temp1_max       45
+#  set temp1_max_hyst  40
+#  set temp2_max       45
+#  set temp2_max_hyst  40
+
+
+# Fintek F71805F/FG configuration
+# This is the recommended wiring and resistor values from the F71805F/FG
+# datasheet. Your motherboard manufacturer may or may not have followed
+# these.
+chip "f71805f-*" "f71872f-*"
+# Voltages
+   label in0 "+3.3V"
+   label in1 "Vtt1.2V"
+   label in2 "Vram"
+   label in3 "Vchip"
+   label in4 "+5V"
+   label in5 "+12V"
+   label in6 "Vcc1.5V"
+   label in7 "Vcore"
+   label in8 "5VSB"
+
+   # in0 is scaled internally
+   compute in2  @*(1+100/100), @/(1+100/100)
+   compute in3  @*(1+100/47),  @/(1+100/47)
+   compute in4  @*(1+200/47),  @/(1+200/47)
+   compute in5  @*(1+200/20),  @/(1+200/20)
+   compute in8  @*(1+200/47),  @/(1+200/47)
+
+   # in0 is the chip's own VCC.
+   set in0_min  3.0
+   set in0_max  3.6
+
+   #set in1_min  1.2 * 0.95
+   #set in1_max  1.2 * 1.05
+   #set in2_min  2.5 * 0.95
+   #set in2_max  2.6 * 1.05
+   #set in3_min  3.3 * 0.95
+   #set in3_max  3.3 * 1.05
+   #set in4_min  5.0 * 0.95
+   #set in4_max  5.0 * 1.05
+   #set in5_min 12.0 * 0.95
+   #set in5_max 12.0 * 1.05
+   #set in6_min  1.5 * 0.95
+   #set in6_max  1.5 * 1.05
+   # in7 nominal value depends on the CPU model
+   #set in7_min  1.4 * 0.95
+   #set in7_max  1.4 * 1.05
+   #set in8_min  5.0 * 0.95
+   #set in8_max  5.0 * 1.05
+
+# Fans
+   label fan1 "CPU Fan"
+   label fan2 "Sys Fan"
+   label fan3 "Aux Fan"
+
+   #set fan1_min 2100
+   #set fan2_min 1400
+   #set fan3_min 1400
+
+# Temperatures
+   label temp1 "CPU Temp"
+   label temp2 "Sys Temp"
+   label temp3 "Aux Temp"
+
+   #set temp1_max       60
+   #set temp1_max_hyst  58
+   #set temp2_max       50
+   #set temp2_max_hyst  48
+   #set temp3_max       50
+   #set temp3_max_hyst  48
+
+
+# Abit Uguru sensor part configuration.
+# The Abit Uguru is relativly straight forward to configure.
+# label statements:
+# The voltage (in) temp and fan sensors are usualy in the same order as listed
+# in the BIOS, but not always!
+# compute statements:
+# The temp and fan sensors don't need any compute statements. Most voltage
+# inputs are directly connected to the IC and thus don't need an compute line
+# because the 0-3494 mV voltage given by the kernel driver is correct. The sen-
+# sors for higher voltages however are connect through a divider and measure
+# ranges of: 0-4361mV, 0-6248mV or 0-14510mV. Thus the measured voltages must
+# be multiplied by resp. 1.248, 1.788 or 4.153. 3.3 volt sources use the 1.248
+# mutiplier, 5 volt the 1.788 and 12 volt the 4.153.
+# set statements:
+# The Abit BIOS sets reasonable treshholds and allows changing them, thus
+# set statements may be ommited. The abituguru kernel driver does support
+# them if you want to add them.
+#
+# The configuration below is for the Kv8Pro and AV8 this is the default as this
+# driver is developed and tested on a Kv8Pro.
+# Configurations for many other Abit boards can be found at:
+# http://www.lm-sensors.org/trac/wiki/Configurations/Abit
+# If your motherboard isn't listed there and you create a configuration for it
+# please add it there.
+
+chip "abituguru-*"
+
+   label in0 "CPU Core Voltage"
+   label in1 "DDR Voltage"
+   label in2 "DDR VTT Voltage"
+   label in3 "NB Voltage"
+   label in4 "SB Voltage"
+   label in5 "HyperTransport Voltage"
+   label in6 "AGP VDDQ Voltage"
+   label in7 "ATX +5V"
+   compute in7 @*1.788 , @/1.788
+   label in8 "ATX +3.3V"
+   compute in8 @*1.248 , @/1.248
+   label in9 "Standby Voltage (+5V)"
+   compute in9 @*1.788 , @/1.788
+   label in10 "3VDual Voltage"
+   compute in10 @*1.248 , @/1.248
+
+   label temp1 "CPU Temperature"
+   label temp2 "SYS Temperature"
+   label temp3 "PWM Temperature"
+   ignore temp4
+   ignore temp5
+   ignore temp6
+   ignore temp7
+
+   label fan1 "CPU FAN Speed"
+   label fan2 "NB FAN Speed"
+   label fan3 "SYS FAN Speed"
+   label fan4 "AUX1 FAN Speed"
+   label fan5 "AUX2 FAN Speed"
+   ignore fan6
+
+chip "k8temp-*"
+
+   label temp1 "Core0 Temp"
+   label temp2 "Core0 Temp"
+   label temp3 "Core1 Temp"
+   label temp4 "Core1 Temp"
+
+
+#
+# Sample configuration for the SMSC DME1737 and ASUS A8000
+#
+chip "dme1737-*"
+
+# Voltage inputs
+   label in0   "V5stby"
+   label in1   "Vccp"
+   label in2   "V3.3"
+   label in3   "V5"
+   label in4   "V12"
+   label in5   "V3.3stby"
+   label in6   "Vbat"
+
+# Temperature inputs
+   label temp1   "RD1 Temp"
+   label temp2   "Int Temp"
+   label temp3   "CPU Temp"
+
+# Fan inputs
+   label fan1   "CPU Fan"
+
+# Set voltage limits
+#   set in0_min   5.0 * 0.95
+#   set in0_max   5.0 * 1.05
+#   set in1_min   1.4 * 0.95
+#   set in1_max   1.4 * 1.05
+#   set in2_min   3.3 * 0.95
+#   set in2_max   3.3 * 1.05
+#   set in3_min   5.0 * 0.95
+#   set in3_max   5.0 * 1.05
+#   set in4_min  12.0 * 0.95
+#   set in4_max  12.0 * 1.05
+#   set in5_min   3.3 * 0.95
+#   set in5_max   3.3 * 1.05
+#   set in6_min   3.0 * 0.95
+#   set in6_max   3.0 * 1.05
+
+# Set Temp Limits
+#   set temp1_min 10
+#   set temp1_max 75
+#   set temp2_min 10
+#   set temp2_max 75
+#   set temp3_min 10
+#   set temp3_max 75
+
+# Set Fan limits
+#   set fan1_min 1000
+#   set fan2_min 1000
+#   set fan3_min 1000
+#   set fan4_min 1000
+#   set fan5_min 1000
+#   set fan6_min 1000
+
+
+#
+# sample configuration for the Fintek f71882fg and f71883fg
+#
+# The configuration below is for the Epox EP-9U1697 GLI board, which has a
+# Fintek f71883fg relabeled as Epox ep1308, this is the default config as this
+# driver is developed and tested on an Epox EP-9U1697 GLI board.
+#
+# Premade configurations for other boards can be found at:
+# http://www.lm-sensors.org/trac/wiki/Configurations/
+# If your motherboard isn't listed there and you create a configuration for it
+# please add it there.
+#
+chip "f71882fg-*"
+
+# Temperature
+    label temp1       "CPU"
+    label temp2       "System"
+    ignore temp3
+
+# Fans
+    label fan1        "CPU"
+    label fan2        "System"
+    label fan3        "Power"
+    label fan4        "Aux"
+
+# Voltage
+    label in0         "3.3V"
+    label in1         "Vcore"
+    label in2         "Vdimm"
+    label in3         "Vchip"
+    label in4         "+5V"
+    label in5         "12V"
+    label in6         "5VSB"
+    label in7         "3VSB"
+    label in8         "Battery"
+
+# never change the in0, in7 and in8 compute, these are hardwired in the chip!
+    compute in0       (@ * 2), (@ / 2)
+    compute in2       (@ * 2), (@ / 2)
+    compute in3       (@ * 2), (@ / 2)
+    compute in4       (@ * 5.25), (@ / 5.25)
+    compute in5       (@ * 12.83), (@ / 12.83)
+    compute in6       (@ * 5.25), (@ / 5.25)
+    compute in7       (@ * 2), (@ / 2)
+    compute in8       (@ * 2), (@ / 2)
+
+
+chip "adm1022-*" "thmc50-*"
+
+   label temp1 "M/B Temp"
+   # Single CPU setup
+   label temp2 "CPU Temp"
+
+   # Dual CPU setup (ADM1022 only)
+   #label temp2 "CPU0 Temp"
+   #label temp3 "CPU1 Temp"
+
+# Asus/Fintek F8000
+chip "f8000-*"
+
+   # All 3 voltages are internal, but scaling is not done by the driver
+   # so we do it here
+   compute in0  @*2, @/2
+   compute in1  @*2, @/2
+   compute in2  @*2, @/2
+
+   label  in0  "+3.3V"
+   label  in1  "3VSB"
+   label  in2  "Vbat"
Index: /lm-sensors/tags/V3-3-0/etc/Module.mk
===================================================================
--- /lm-sensors/tags/V3-3-0/etc/Module.mk	(revision 5760)
+++ /lm-sensors/tags/V3-3-0/etc/Module.mk	(revision 5760)
@@ -0,0 +1,41 @@
+#  Module.mk - Makefile for a Linux module for reading sensor data.
+#  Copyright (c) 1998, 1999  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.
+
+# Note: Don't confuse ETC_DIR (the directory in which this file resides)
+# with ETCDIR (the system directory in which configuration files will be
+# installed; typically /etc).
+ETC_DIR := etc
+
+install-etc:
+	$(MKDIR) $(DESTDIR)$(ETCDIR) $(DESTDIR)$(ETCDIR)/sensors.d
+	if [ ! -e $(DESTDIR)$(ETCDIR)/sensors3.conf ] ; then \
+	  $(INSTALL) -m 644 $(ETC_DIR)/sensors.conf.default $(DESTDIR)$(ETCDIR)/sensors3.conf ; \
+	fi
+	$(MKDIR) $(DESTDIR)$(BINDIR)
+	$(INSTALL) -m 755 $(ETC_DIR)/sensors-conf-convert $(DESTDIR)$(BINDIR)
+	if [ -e $(DESTDIR)$(ETCDIR)/modprobe.d/lm_sensors \
+	     -a ! -e $(DESTDIR)$(ETCDIR)/modprobe.d/lm_sensors.conf ] ; then \
+	  $(MV) $(DESTDIR)$(ETCDIR)/modprobe.d/lm_sensors $(DESTDIR)$(ETCDIR)/modprobe.d/lm_sensors.conf ; \
+	fi
+
+user_install :: install-etc
+
+uninstall-etc:
+	$(RM) $(DESTDIR)$(BINDIR)/sensors-conf-convert
+
+user_uninstall :: uninstall-etc
Index: /lm-sensors/tags/V3-3-0/etc/sensors-conf-convert
===================================================================
--- /lm-sensors/tags/V3-3-0/etc/sensors-conf-convert	(revision 4990)
+++ /lm-sensors/tags/V3-3-0/etc/sensors-conf-convert	(revision 4990)
@@ -0,0 +1,482 @@
+#!/usr/bin/perl -w -pi.old
+
+# Convert a sensors.conf file from the old (Linux 2.4, lm-sensors 2)
+# symbol names to the new (Linux 2.6, lm-sensors 3) symbol names.
+#
+# Copyright (C) 2007  Jean Delvare <khali@linux-fr.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.
+#
+# 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.
+
+# Implemented conversions:
+# * The "algorithm name" part of bus statements is removed.
+# * Bus statements for the ISA bus are removed.
+# * Chip names containing dashes are removed.
+# * Label, ignore, set and compute statements referencing features
+#   which are not part of the new interface are removed.
+# * Label, ignore, set and compute statements referencing features
+#   which have been renamed are updated. This includes both general
+#   feature name changes and chip-specific feature name changes.
+#
+# Note that we have to look at the commented out lines too, so that
+# the user doesn't have a bad surprise when uncommenting them.
+
+use strict;
+use vars qw($debug $chip %trans @delete %chip_trans %chip_delete);
+
+BEGIN
+{
+	$debug = 0;
+
+	%trans = (
+		qr/\bvid\b/			=> sub { "cpu0_vid" },
+		qr/\bremote_temp\b/		=> sub { "temp2" },
+		qr/\bremote_temp_hyst\b/	=> sub { "temp2_max_hyst" },
+		qr/\bremote_temp_low\b/		=> sub { "temp2_min" },
+		qr/\bremote_temp_over\b/	=> sub { "temp2_max" },
+		qr/\btemp\b/			=> sub { "temp1" },
+		qr/\btemp_hyst\b/		=> sub { "temp1_max_hyst" },
+		qr/\btemp_low\b/		=> sub { "temp1_min" },
+		qr/\btemp_over\b/		=> sub { "temp1_max" },
+		qr/\btemp_high\b/		=> sub { "temp1_max" },
+		qr/\btemp_crit\b/		=> sub { "temp1_crit" },
+
+		qr/\bvin(\d+)_max\b/		=> sub { "in$1_max" },
+		qr/\bvin(\d+)_min\b/		=> sub { "in$1_min" },
+		qr/\bvin(\d+)\b/		=> sub { "in$1" },
+		qr/\btemp(\d+)_over\b/		=> sub { "temp$1_max" },
+		qr/\btemp(\d+)_hyst\b/		=> sub { "temp$1_max_hyst" },
+		qr/\btemp(\d+)_high\b/		=> sub { "temp$1_max" },
+		qr/\btemp(\d+)_low\b/		=> sub { "temp$1_min" },
+		qr/\bsensor(\d+)\b/		=> sub { "temp$1_type" },
+	);
+
+	@delete = (
+		qr/\balarms\b/,
+		qr/\bbeeps\b/,
+		qr/\bpwm\d*\b/,
+		qr/\bpwm\d+_enable\b/,
+		qr/\b(in|temp|fan)\d+_(state|status)\b/,
+		qr/\banalog_out\b/,
+		qr/\balarms_(in|temp|fan)\b/,
+	);
+
+	%chip_trans = (
+		"gl518sm" => {
+			qr/\bvdd\b/			=> sub { "in0" },
+			qr/\bvdd_min\b/			=> sub { "in0_min" },
+			qr/\bvdd_max\b/			=> sub { "in0_max" },
+		},
+		"gl520sm" => {
+			qr/\bvdd\b/			=> sub { "in0" },
+			qr/\bvdd_min\b/			=> sub { "in0_min" },
+			qr/\bvdd_max\b/			=> sub { "in0_max" },
+		},
+		"lm80" => {
+			qr/\btemp_hot_hyst\b/		=> sub { "temp1_max_hyst" },
+			qr/\btemp_hot_max\b/		=> sub { "temp1_max" },
+			qr/\btemp_os_hyst\b/		=> sub { "temp1_crit_hyst" },
+			qr/\btemp_os_max\b/		=> sub { "temp1_crit" },
+		},
+		"lm83" => {
+			qr/\btcrit\b/			=> sub { "temp3_crit" },
+		},
+		"lm90" => {
+			qr/\btcrit(\d)\b/		=> sub { "temp$1_crit" },
+			qr/\bhyst(\d)\b/		=> sub { "temp$1_crit_hyst" },
+		},
+		"adm1032" => {
+			qr/\btcrit(\d)\b/		=> sub { "temp$1_crit" },
+			qr/\bhyst(\d)\b/		=> sub { "temp$1_crit_hyst" },
+		},
+		"lm86" => {
+			qr/\btcrit(\d)\b/		=> sub { "temp$1_crit" },
+			qr/\bhyst(\d)\b/		=> sub { "temp$1_crit_hyst" },
+		},
+		"lm99" => {
+			qr/\btcrit(\d)\b/		=> sub { "temp$1_crit" },
+			qr/\bhyst(\d)\b/		=> sub { "temp$1_crit_hyst" },
+		},
+		"adt7461" => {
+			qr/\btcrit(\d)\b/		=> sub { "temp$1_crit" },
+			qr/\bhyst(\d)\b/		=> sub { "temp$1_crit_hyst" },
+		},
+		"max6657" => {
+			qr/\btcrit(\d)\b/		=> sub { "temp$1_crit" },
+			qr/\bhyst(\d)\b/		=> sub { "temp$1_crit_hyst" },
+		},
+		"max6680" => {
+			qr/\btcrit(\d)\b/		=> sub { "temp$1_crit" },
+			qr/\bhyst(\d)\b/		=> sub { "temp$1_crit_hyst" },
+		},
+		"lm93" => {
+			qr/\bvid([12])\b/			=> sub { "cpu".($1-1)."_vid" },
+		},
+		"adm9240" => {
+			qr/"2\.5V(|_min|_max)"/		=> sub { "in0$1" },
+			qr/\bVccp1(|_min|_max)\b/	=> sub { "in1$1" },
+			qr/"3\.3V(|_min|_max)"/		=> sub { "in2$1" },
+			qr/"5V(|_min|_max)"/		=> sub { "in3$1" },
+			qr/"12V(|_min|_max)"/		=> sub { "in4$1" },
+			qr/\bVccp2(|_min|_max)\b/	=> sub { "in5$1" },
+		},
+		"lm81" => {
+			qr/"2\.5V(|_min|_max)"/		=> sub { "in0$1" },
+			qr/\bVccp1(|_min|_max)\b/	=> sub { "in1$1" },
+			qr/"3\.3V(|_min|_max)"/		=> sub { "in2$1" },
+			qr/"5V(|_min|_max)"/		=> sub { "in3$1" },
+			qr/"12V(|_min|_max)"/		=> sub { "in4$1" },
+			qr/\bVccp2(|_min|_max)\b/	=> sub { "in5$1" },
+		},
+		"ds1780" => {
+			qr/"2\.5V(|_min|_max)"/		=> sub { "in0$1" },
+			qr/\bVccp1(|_min|_max)\b/	=> sub { "in1$1" },
+			qr/"3\.3V(|_min|_max)"/		=> sub { "in2$1" },
+			qr/"5V(|_min|_max)"/		=> sub { "in3$1" },
+			qr/"12V(|_min|_max)"/		=> sub { "in4$1" },
+			qr/\bVccp2(|_min|_max)\b/	=> sub { "in5$1" },
+		},
+		"adm1024" => {
+			qr/"2\.5V(|_min|_max)"/		=> sub { "in0$1" },
+			qr/\bVccp1(|_min|_max)\b/	=> sub { "in1$1" },
+			qr/"3\.3V(|_min|_max)"/		=> sub { "in2$1" },
+			qr/"5V(|_min|_max)"/		=> sub { "in3$1" },
+			qr/"12V(|_min|_max)"/		=> sub { "in4$1" },
+			qr/\bVccp2(|_min|_max)\b/	=> sub { "in5$1" },
+			qr/\btemp_hyst\b/		=> sub { "temp1_min" },
+			qr/\btemp(\d)\b/		=> sub { "temp".($1+1) },
+			qr/\btemp(\d)_hyst\b/		=> sub { "temp".($1+1)."_min" },
+			qr/\btemp(\d)_over\b/		=> sub { "temp".($1+1)."_max" },
+		},
+		"maxilife" => {
+			qr/\bvid(\d)(|_min|_max)\b/	=> sub { "in".($1-1).$2 },
+		},
+		"thmc50" => {
+			qr/\btemp_hyst\b/		=> sub { "temp1_min" },
+			qr/\bremote_temp_hyst\b/	=> sub { "temp2_min" },
+			qr/\bremote_temp2\b/		=> sub { "temp3" },
+			qr/\bremote_temp2_hyst\b/	=> sub { "temp3_min" },
+			qr/\bremote_temp2_over\b/	=> sub { "temp3_max" },
+		},
+		"adm1022" => {
+			qr/\btemp_hyst\b/		=> sub { "temp1_min" },
+			qr/\bremote_temp_hyst\b/	=> sub { "temp2_min" },
+			qr/\bremote_temp2\b/		=> sub { "temp3" },
+			qr/\bremote_temp2_hyst\b/	=> sub { "temp3_min" },
+			qr/\bremote_temp2_over\b/	=> sub { "temp3_max" },
+		},
+		"adm1026" => {
+			qr/\bfan(\d)(|_div|_min)\b/	=> sub { "fan".($1+1).$2 },
+			qr/\btemp(\d)_therm\b/		=> sub { "temp$1_crit" },
+		},
+		"via686a" => {
+			qr/"2\.0V"/			=> sub { "in0" },
+			qr/"2\.5V"/			=> sub { "in1" },
+			qr/"3\.3V"/			=> sub { "in2" },
+			qr/"5\.0V"/			=> sub { "in3" },
+			qr/"12V"/			=> sub { "in4" },
+		},
+		"lm87" => {
+			qr/"2\.5V(|_min|_max)"/		=> sub { "in0$1" },
+			qr/\bVccp1(|_min|_max)\b/	=> sub { "in1$1" },
+			qr/"3\.3V(|_min|_max)"/		=> sub { "in2$1" },
+			qr/"5V(|_min|_max)"/		=> sub { "in3$1" },
+			qr/"12V(|_min|_max)"/		=> sub { "in4$1" },
+			qr/\bVccp2(|_min|_max)\b/	=> sub { "in5$1" },
+			qr/\bAIN1(|_min|_max)\b/	=> sub { "in6$1" },
+			qr/\bAIN2(|_min|_max)\b/	=> sub { "in7$1" },
+			qr/\bCPU_Temp\b/		=> sub { "temp2" },
+		},
+		"fscpos" => {
+			qr/\bvolt12\b/			=> sub { "in0" },
+			qr/\bvolt5\b/			=> sub { "in1" },
+			qr/\bvoltbatt\b/		=> sub { "in2" },
+		},
+		"fscscy" => {
+			qr/\bvolt12\b/			=> sub { "in0" },
+			qr/\bvolt5\b/			=> sub { "in1" },
+			qr/\bvoltbatt\b/		=> sub { "in2" },
+		},
+		"pcf8591" => {
+			qr/\bch(\d)\b/			=> sub { "in$1" },
+		},
+		"smsc47m192" => {
+			qr/\btemp(\d)_input_fault\b/	=> sub { "temp$1_fault" },
+		},
+		"lm92" => {
+			qr/\btemp_hyst\b/		=> sub { "temp1_crit_hyst" },
+		},
+		"max1619" => {
+			qr/\btemp2_hyst\b/		=> sub { "temp2_crit_hyst" },
+		},
+		"lm78" => {
+			qr/\bin([56])_(min|max)\b/	=> sub { "in$1_".($2 eq "max" ? "min" : "max") },
+		},
+		"lm79" => {
+			qr/\bin([56])_(min|max)\b/	=> sub { "in$1_".($2 eq "max" ? "min" : "max") },
+		},
+		"w83781d" => {
+			qr/\bin([56])_(min|max)\b/	=> sub { "in$1_".($2 eq "max" ? "min" : "max") },
+		},
+		"as99127f" => {
+			qr/\bin([56])_(min|max)\b/	=> sub { "in$1_".($2 eq "max" ? "min" : "max") },
+		},
+	);
+
+	%chip_delete = (
+		"adm1021" => [
+			qr/\bdie_code\b/,
+		],
+		"lm84" => [
+			qr/\bdie_code\b/,
+		],
+		"gl523" => [
+			qr/\bdie_code\b/,
+		],
+		"thmc10" => [
+			qr/\bdie_code\b/,
+		],
+		"gl518sm" => [
+			qr/\bfan1_off\b/,
+			qr/\bfan1_off_pin\b/,
+			qr/\biterate\b/,
+		],
+		"gl520sm" => [
+			qr/\bfan1_off\b/,
+			qr/\btwo_temps\b/,
+		],
+		"w83792d" => [
+			qr/\bchassis\b/,
+		],
+		"w83793" => [
+			qr/\bchassis\b/,
+		],
+		"maxilife" => [
+			qr/\bpll(|_min|_max)\b/,
+		],
+		"thmc50" => [
+			qr/\banalog output\b/,
+			qr/\binterrupts\b/,
+			qr/\binterrupt mask\b/,
+			qr/\bdie_code\b/,
+		],
+		"adm1022" => [
+			qr/\banalog output\b/,
+			qr/\binterrupts\b/,
+			qr/\binterrupt mask\b/,
+			qr/\bdie_code\b/,
+		],
+		"adm1026" => [
+			qr/\balarm_mask\b/,
+			qr/\bgpio\b/,
+			qr/\bgpio_mask\b/,
+			qr/\bafc_pwm\b/,
+			qr/\bafc_analog_out\b/,
+			qr/\btemp\d_tmin\b/,
+		],
+		"lm85" => [
+			qr/\bpwm\d_(spinup|min|freq|min_ctl|zone|spinup_ctl)\b/,
+			qr/\bzone\d_(limit|hyst|range|critical|smooth)\b/,
+			qr/\bfan\d_(tach_mode|ppr)\b/,
+		],
+		"lm85b" => [
+			qr/\bpwm\d_(spinup|min|freq|min_ctl|zone|spinup_ctl)\b/,
+			qr/\bzone\d_(limit|hyst|range|critical|smooth)\b/,
+			qr/\bfan\d_(tach_mode|ppr)\b/,
+		],
+		"lm85c" => [
+			qr/\bpwm\d_(spinup|min|freq|min_ctl|zone|spinup_ctl)\b/,
+			qr/\bzone\d_(limit|hyst|range|critical|smooth)\b/,
+			qr/\bfan\d_(tach_mode|ppr)\b/,
+		],
+		"emc6d100" => [
+			qr/\bpwm\d_(spinup|min|freq|min_ctl|zone|spinup_ctl)\b/,
+			qr/\bzone\d_(limit|hyst|range|critical|smooth)\b/,
+			qr/\bfan\d_(tach_mode|ppr)\b/,
+		],
+		"emc6d102" => [
+			qr/\bpwm\d_(spinup|min|freq|min_ctl|zone|spinup_ctl)\b/,
+			qr/\bzone\d_(limit|hyst|range|critical|smooth)\b/,
+			qr/\bfan\d_(tach_mode|ppr)\b/,
+		],
+		"adm1027" => [
+			qr/\bpwm\d_(spinup|min|freq|min_ctl|zone|spinup_ctl)\b/,
+			qr/\bzone\d_(limit|hyst|range|critical|smooth)\b/,
+			qr/\bfan\d_(tach_mode|ppr)\b/,
+		],
+		"adt7473" => [
+			qr/\bpwm\d_(spinup|min|freq|min_ctl|zone|spinup_ctl)\b/,
+			qr/\bzone\d_(limit|hyst|range|critical|smooth)\b/,
+			qr/\bfan\d_(tach_mode|ppr)\b/,
+		],
+		"fscpos" => [
+			qr/\brev\b/,
+			qr/\bevent\b/,
+			qr/\bcontrol\b/,
+			qr/\bfan\d_ripple\b/,
+			qr/\bwdog_(preset|state|control)\b/,
+		],
+		"fscscy" => [
+			qr/\brev\b/,
+			qr/\bevent\b/,
+			qr/\bcontrol\b/,
+			qr/\btemp\d_lim\b/,
+			qr/\bfan\d_ripple\b/,
+			qr/\bwdog_(preset|state|control)\b/,
+		],
+		"fscher" => [
+			qr/\brev\b/,
+			qr/\bevent\b/,
+			qr/\bcontrol\b/,
+			qr/\bfan\d_ripple\b/,
+			qr/\bwdog_(preset|state|control)\b/,
+		],
+		"pcf8591" => [
+			qr/\bain_conf\b/,
+			qr/\baout_enable\b/,
+			qr/\baout\b/,
+		],
+		"vt1211" => [
+			qr/\bconfig\b/,
+		],
+		"vt8231" => [
+			qr/\bconfig\b/,
+		],
+		"max6650" => [
+			qr/\bspeed\b/,
+		],
+		"max6651" => [
+			qr/\bspeed\b/,
+		],
+		"applesmc" => [
+			qr/\bfan\d_(max|safe)\b/,
+		],
+	);
+
+	if (defined $ARGV[0] && ($ARGV[0] eq '-h' || $ARGV[0] eq '--help')) {
+		print "Convert sensors.conf from lm-sensors 2 format to lm-sensors 3 format\n",
+		      "Typical usage: sensors-conf-convert < /etc/sensors.conf > /etc/sensors3.conf\n";
+		exit 0;
+	}
+}
+
+if ($. == 1) {
+	print "# Converted by sensors-conf-convert on ".localtime()."\n";
+}
+
+sub substitute_line($$)
+{
+	my ($chip, $line) = @_;
+
+	# First the chip-specific ones
+	if ($chip && exists $chip_trans{$chip}) {
+		foreach my $t (keys %{$chip_trans{$chip}}) {
+			$line =~ s/$t/$chip_trans{$chip}->{$t}->()/ge;
+		}
+	}
+
+	# Then the general substitutions
+	foreach my $t (keys %trans) {
+		# Kudos to the nice folks in #perl on freenode :)
+		$line =~ s/$t/$trans{$t}->()/ge;
+	}
+
+	return $line;
+}
+
+sub delete_line($)
+{
+	my $feature = shift;
+
+	# First the general deletions
+	foreach my $t (@delete) {
+		return 1 if $feature =~ m/$t/;
+	}
+
+	# Then the chip-specific ones
+	if ($chip && exists $chip_delete{$chip}) {
+		foreach my $t (@{$chip_delete{$chip}}) {
+			return 1 if $feature =~ m/$t/;
+		}
+	}
+
+	return 0;
+}
+
+if (m/^# Converted by sensors-conf-convert/) {
+	print STDERR "WARNING: Converting an already converted file!\n";
+	print STDERR "         Result will be incorrect for some chip types.\n";
+}
+
+# Bus statements
+if (m/^([\s#]*bus\s+"?i2c-\d+"?\s+"([^"]+)")\s+"[^"]*"/) {
+	print STDERR "Processing bus statement: $2\n" if $debug;
+
+	if ($2 eq "ISA main adapter") {
+		$_ = '';	# Drop entirely 
+	} else {
+		$_ = "$1\n";	# Drop algorithm name
+	}
+}
+
+# Chip statements
+elsif (m/^[\s#]*chip\s+"([\w\d*-]+)"/) {
+	# We only remember the first chip name, assuming that all chips
+	# in a given "chip" statement need the same specific processing
+	$chip = $1;
+	$chip =~ s/-.*//;
+	undef $chip if $chip eq "*";
+
+	# Remove dashes from chip names, as this is no longer allowed
+	s/(\s+"lm78-)j-/$1/g;
+	s/(\s+"maxilife-)(cg|co|as|nba)-/$1/g;
+	# Simplify possible duplicates
+	s/("lm78-\*")(\s+"lm78-\*")+/$1/g;
+	s/("maxilife-\*")(\s+"maxilife-\*")+/$1/g;
+
+	# "*" is no longer a valid chip name
+	s/"\*"/"\*-\*"/g;
+
+	print STDERR "Processing chip section: $chip\n" if $chip and $debug;
+}
+
+# Drop references to lm78-j
+elsif (m/^\s*#+.*lm78-?j/i) {
+	s/(lm78), lm78-?j/$1/gi;
+	s/(lm78)-?j/$1/gi;
+}
+
+# Drop references to vrm
+elsif (m/^[\s#]*set\s+vrm\s/i
+    || m/^\s*#+\s*adjust this if your vid is wrong/i
+    || m/^\s*#+\s*Also, one should set vrm prior to using vid in any formula/) {
+	$_ = '';
+}
+
+# Drop references to pwm
+elsif (m/^\s*#+\s*PWM Output/i) {
+	$_ = '';
+}
+
+# Feature name substitution
+elsif (m/^[\s#]*(?:label|ignore|set|compute)\s+(\S+)\s/) {
+	if (delete_line($1)) {
+		# Delete references to features that are now gone
+		$_ = '';
+	} else {
+		# Substitute feature names
+		$_ = substitute_line($chip, $_);
+	}
+}
Index: /lm-sensors/tags/V3-3-0/version.h
===================================================================
--- /lm-sensors/tags/V3-3-0/version.h	(revision 5949)
+++ /lm-sensors/tags/V3-3-0/version.h	(revision 5949)
@@ -0,0 +1,1 @@
+#define LM_VERSION "3.3.0"
Index: /lm-sensors/tags/V3-3-0/CONTRIBUTORS
===================================================================
--- /lm-sensors/tags/V3-3-0/CONTRIBUTORS	(revision 5662)
+++ /lm-sensors/tags/V3-3-0/CONTRIBUTORS	(revision 5662)
@@ -0,0 +1,17 @@
+Here is a list of the main contributors to lm-sensors version 3.
+
+* Frodo Looijaard
+  Original author of libsensors, sensors-detect, sensors and isadump.
+* Merlin Hughes
+  Original author of sensord.
+* Bob Schlaermann
+  Dynamic chip feature detection (a.k.a. generic chip support) in
+  libsensors and sensors.
+* Mark M. Hoffman
+  Many improvements to the libsensors configuration file scanner.
+* Jean Delvare
+  New libsensors API, and migration of sensors and sensord thereto.
+  Many optimizations in libsensors and sensors.
+  Configuration file converter.
+  Rewrite of sensors-detect.
+  Support for multiple configuration files in libsensors.
Index: /lm-sensors/tags/V3-3-0/Makefile
===================================================================
--- /lm-sensors/tags/V3-3-0/Makefile	(revision 5871)
+++ /lm-sensors/tags/V3-3-0/Makefile	(revision 5871)
@@ -0,0 +1,287 @@
+#  Makefile - Makefile for a Linux module for reading sensor data.
+#  Copyright (c) 1998, 1999  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.
+
+# Everything you may want to change is in the top of this file. Usually, you
+# can just use the defaults, fortunately.
+
+# You need a full complement of GNU utilities to run this Makefile
+# successfully; most notably, you need GNU make, flex (>= 2.5.1)
+# and bison.
+
+# Uncomment the second line if you are a developer. This will enable many
+# additional warnings at compile-time
+#WARN := 0
+WARN := 1
+
+# Uncomment the second line if you want to get (loads of) debug information
+# at run-time.
+# Not recommended, unless you are actually debugging the code
+DEBUG := 0
+#DEBUG := 1
+
+# Note that all the installation paths below can also be set on the make
+# command line (e.g. "make PREFIX=/usr").
+
+# If you want to install at some other place then at from which you will run
+# everything, set DESTDIR to the extra prefix.
+DESTDIR :=
+
+# This is the prefix that will be used for almost all directories below.
+PREFIX := /usr/local
+
+# Your C compiler
+CC := gcc
+
+# This is the directory where sensors3.conf will be installed, if no other
+# configuration file is found
+ETCDIR := /etc
+
+# You should not need to change this. It is the directory into which the
+# library files (both static and shared) will be installed.
+LIBDIR := $(PREFIX)/lib
+
+EXLDFLAGS := -Wl,-rpath,$(LIBDIR)
+
+# You should not need to change this. It is the directory into which the
+# executable program files will be installed. BINDIR for programs that are
+# also useful for normal users, SBINDIR for programs that can only be run
+# by the superuser.
+# Note that not all programs in this package are really installed;
+# some are just examples. You can always install them by hand, of
+# course.
+BINDIR := $(PREFIX)/bin
+SBINDIR := $(PREFIX)/sbin
+
+# You should not need to change this. It is the basic directory into which
+# include files will be installed. The actual directory will be 
+# $(INCLUDEDIR)/sensors for library include files.
+INCLUDEDIR := $(PREFIX)/include
+LIBINCLUDEDIR := $(INCLUDEDIR)/sensors
+
+# You should not need to change this. It is the base directory under which the
+# manual pages will be installed.
+MANDIR := $(PREFIX)/man
+
+MACHINE := $(shell uname -m)
+
+# Extra non-default programs to build; e.g., sensord
+#PROG_EXTRA := sensord
+
+# Build and install static library
+BUILD_STATIC_LIB := 1
+
+# Set these to add preprocessor or compiler flags, or use
+# environment variables
+# CFLAGS :=
+# CPPFLAGS :=
+
+##################################################
+# Below this, nothing should need to be changed. #
+##################################################
+
+# Note that this is a monolithic Makefile; it calls no sub-Makefiles,
+# but instead, it compiles everything right from here. Yes, there are
+# some distinct advantages to this; see the following paper for more info:
+#   http://www.tip.net.au/~millerp/rmch/recu-make-cons-harm.html
+# Note that is still uses Makefile fragments in sub-directories; these
+# are called 'Module.mk'.
+
+# Within each Module.mk, rules and dependencies can be added to targets
+# all, install and clean. Use double colons instead of single ones
+# to do this. 
+
+# The subdirectories we need to build things in 
+SRCDIRS := lib prog/detect prog/pwm \
+           prog/sensors ${PROG_EXTRA:%=prog/%} etc
+# Only build isadump and isaset on x86 machines.
+ifneq (,$(findstring $(MACHINE), i386 i486 i586 i686 x86_64))
+SRCDIRS += prog/dump
+endif
+SRCDIRS += lib/test
+
+# Some often-used commands with default options
+MKDIR := mkdir -p
+RMDIR := rmdir
+RM := rm -f
+MV := mv -f
+BISON := bison
+FLEX := flex
+AR := ar
+INSTALL := install
+LN := ln -sf
+GREP := grep
+AWK := awk
+SED := sed
+
+# Determine the default compiler flags
+# Set CFLAGS or CPPFLAGS above to add your own flags to all.
+# ALLCPPFLAGS/ALLCFLAGS are common flags, plus any user-specified overrides from the environment or make command line.
+# PROGCPPFLAGS/PROGCFLAGS is to create regular object files (which are linked into executables).
+# ARCPPFLAGS/ARCFLAGS are used to create archive object files (static libraries).
+# LIBCPPFLAGS/LIBCFLAGS are for shared library objects.
+ALL_CPPFLAGS := -I.
+ALL_CFLAGS := -Wall
+
+ifeq ($(DEBUG),1)
+ALL_CPPFLAGS += -DDEBUG
+ALL_CFLAGS += -O -g
+else
+ALL_CFLAGS += -O2
+endif
+
+ifeq ($(WARN),1)
+ALL_CFLAGS += -Wstrict-prototypes -Wshadow -Wpointer-arith -Wcast-qual \
+            -Wcast-align -Wwrite-strings -Wnested-externs -Winline -W \
+            -Wmissing-prototypes -Wundef
+endif
+
+ALL_CPPFLAGS += $(CPPFLAGS)
+ALL_CFLAGS += $(CFLAGS)
+
+PROGCPPFLAGS := -DETCDIR="\"$(ETCDIR)\"" $(ALL_CPPFLAGS)
+PROGCFLAGS := $(ALL_CFLAGS)
+ARCPPFLAGS := -DETCDIR="\"$(ETCDIR)\"" $(ALL_CPPFLAGS)
+ARCFLAGS := $(ALL_CFLAGS)
+LIBCPPFLAGS := -DETCDIR="\"$(ETCDIR)\"" $(ALL_CPPFLAGS)
+LIBCFLAGS := -fpic -D_REENTRANT $(ALL_CFLAGS)
+
+.PHONY: all user clean install user_install uninstall user_uninstall
+
+# Make all the default rule
+all::
+
+# Include all makefiles for sub-modules
+INCLUDEFILES := 
+include $(patsubst %,%/Module.mk,$(SRCDIRS))
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),uninstall)
+ifneq ($(MAKECMDGOALS),user_uninstall)
+ifneq ($(MAKECMDGOALS),help)
+include $(INCLUDEFILES)
+endif
+endif
+endif
+endif
+
+# Man pages
+MANPAGES := $(LIBMAN3FILES) $(LIBMAN5FILES) $(PROGDETECTMAN8FILES) $(PROGDUMPMAN8FILES) \
+            $(PROGSENSORSMAN1FILES) $(PROGPWMMAN8FILES) prog/sensord/sensord.8
+
+user ::
+user_install::
+	@echo "*** Important notes:"
+	@echo "***  * The libsensors configuration file ($(ETCDIR)/sensors3.conf) is never"
+	@echo "***    overwritten by our installation process, so that you won't lose"
+	@echo "***    your personal settings in that file. You still can get our latest"
+	@echo "***    default config file in etc/sensors.conf.default and manually copy"
+	@echo "***    it to $(ETCDIR)/sensors3.conf if you want. You will then want to"
+	@echo "***    edit it to fit your needs again."
+	@echo "***  * The format of $(ETCDIR)/sensors3.conf changed with lm-sensors 3.0.0."
+	@echo "***    If you have a custom configuration file using the old format, you"
+	@echo "***    can convert it using the sensors-conf-convert script. Otherwise just"
+	@echo "***    overwrite your old configuration file with the new default one."
+	@echo "***  * As of lm-sensors 3.1.0, the default configuration file only"
+	@echo "***    contains statements which do not depend on how chips are wired."
+	@echo "***    If you miss parts of the bigger configuration file that used to be"
+	@echo "***    the default, copy the relevant parts from etc/sensors.conf.eg to"
+	@echo "***    $(ETCDIR)/sensors3.conf."
+all :: user
+install :: all user_install
+
+clean::
+	$(RM) lm_sensors-* lex.backup
+
+user_uninstall::
+
+uninstall :: user_uninstall
+
+help:
+	@echo 'Make targets are:'
+	@echo '  all (default): build library and userspace programs'
+	@echo '  install: install library and userspace programs'
+	@echo '  uninstall: uninstall library and userspace programs'
+	@echo '  clean: cleanup'
+
+# Generate html man pages to be copied to the lm_sensors website.
+# This uses the man2html from here
+# http://ftp.math.utah.edu/pub/sgml/
+# which works directly from the nroff source
+manhtml:
+	$(MKDIR) html
+	cp $(MANPAGES) html
+	cd html ; \
+	export LOGNAME=sensors ; \
+	export HOSTNAME=www.lm-sensors.org ; \
+	man2html *.[1-8] ; \
+	$(RM) *.[1-8]
+
+# Here, we define all implicit rules we want to use.
+
+.SUFFIXES:
+
+# We need to create dependency files. Tricky. The sed rule puts dir/file.d and
+# dir/file.c in front of the dependency file rule.
+
+
+# .ro files are used for programs (as opposed to modules)
+%.ro: %.c
+	$(CC) $(PROGCPPFLAGS) $(PROGCFLAGS) -c $< -o $@
+
+%.rd: %.c
+	$(CC) -M -MG $(PROGCPPFLAGS) $(PROGCFLAGS) $< | \
+	$(SED) -e 's@^\(.*\)\.o:@$*.rd $*.ro: Makefile '`dirname $*.rd`/Module.mk' @' > $@
+
+
+# .ao files are used for static archives
+%.ao: %.c
+	$(CC) $(ARCPPFLAGS) $(ARCFLAGS) -c $< -o $@
+
+%.ad: %.c
+	$(CC) -M -MG $(ARCPPFLAGS) $(ARCFLAGS) $< | \
+	$(SED) -e 's@^\(.*\)\.o:@$*.ad $*.ao: Makefile '`dirname $*.ad`/Module.mk' @' > $@
+
+
+# .lo files are used for shared libraries
+%.lo: %.c
+	$(CC) $(LIBCPPFLAGS) $(LIBCFLAGS) -c $< -o $@
+
+%.ld: %.c
+	$(CC) -M -MG $(LIBCPPFLAGS) $(LIBCFLAGS) $< | \
+	$(SED) -e 's@^\(.*\)\.o:@$*.ld $*.lo: Makefile '`dirname $*.ld`/Module.mk' @' > $@
+
+
+# Flex and Bison
+%.c: %.y
+	@if ! which $(BISON) 2> /dev/null ; then \
+		echo "Please install $(BISON), then run \"make clean\" and try again" ; \
+		false ; \
+	fi
+	$(BISON) -p sensors_yy -d $< -o $@
+
+ifeq ($(DEBUG),1)
+FLEX_FLAGS := -Psensors_yy -t -b -Cfe -8
+else
+FLEX_FLAGS := -Psensors_yy -t -Cfe -8
+endif
+
+%.c: %.l
+	@if ! which $(FLEX) 2> /dev/null ; then \
+		echo "Please install $(FLEX), then run \"make clean\" and try again" ; \
+		false ; \
+	fi
+	$(FLEX) $(FLEX_FLAGS) $< > $@
Index: /lm-sensors/tags/V3-3-0/README
===================================================================
--- /lm-sensors/tags/V3-3-0/README	(revision 5844)
+++ /lm-sensors/tags/V3-3-0/README	(revision 5844)
@@ -0,0 +1,109 @@
+OVERVIEW OF THE LM-SENSORS PACKAGE
+==================================
+
+The lm-sensors package, version 3, provides user-space support for the
+hardware monitoring drivers in Linux 2.6.5 and later. For older kernel
+versions, you have to use lm-sensors version 2.
+
+The directories within this package:
+
+* doc
+  Documentation.
+* etc
+  A sample configuration file for libsensors, and a script to convert
+  lm-sensors version 2 configuration files to work with version 3.
+* lib
+  The user-space sensors support library code (libsensors).
+* prog
+  Several supporting programs. The most important ones are:
+  - sensors-detect: A stand-alone program for detecting installed
+    hardware and recommending specific modules to load.
+  - sensors: A console tool to report sensor readings and set new
+    sensor limits.
+  - sensord: A daemon to watch sensor values and log problems. It
+    includes RRD support.
+
+
+INSTALLING LM-SENSORS
+---------------------
+
+See the INSTALL file.
+
+
+HARDWARE SUPPORT
+----------------
+
+To find out what hardware you have, just run 'sensors-detect' as root.
+
+Most modern mainboards incorporate some form of hardware monitoring chips.
+These chips read things like chip temperatures, fan rotation speeds and
+voltage levels. There are quite a few different chips which can be used by
+mainboard builders for approximately the same results.
+
+Laptops, on the other hand, rarely expose any hardware monitoring
+chip. They often have some BIOS and/or ACPI magic to get the CPU
+temperature value, but that's about it. For such laptops, the lm-sensors
+package is of no use (sensors-detect will not find anything), and you have
+to use acpi instead.
+
+This package doesn't contain chip-specific knowledge. It will support all
+the hardware monitoring chips your kernel has drivers for. In other words,
+if you find out that you have unsupported hardware (e.g. sensors-detect
+told you so) then it means that you need a more recent kernel, or you
+even need to wait for a new kernel driver to be written. Updating the
+lm-sensors package itself will not help.
+
+
+LIBSENSORS
+----------
+
+The kernel drivers communicate their information through the /sys
+interface. Because every motherboard is different, the drivers always
+advert the measurements at their pins. This means that the values they
+report are not always immediately relevant to you. They have to be
+labelled properly, and sometimes they must be scaled to correspond to
+real-world values.
+
+libsensors is a (shared or static) library of access functions. It
+offers a simple-to-use interface for applications to access the sensor
+chip readings and configure them as you like. It has a configuration
+file where you can put all the motherboard-specific labels and
+conversion rules. That way, all applications do not need to duplicate
+the effort and can simply link with libsensors and work out of the box.
+
+
+APPLICATIONS
+------------
+
+This package contains an example console program that reports all current
+sensors values. This program is called 'sensors'. You can use it as a
+reference implementation for more intricate programs. It also contains a
+daemon watching for sensor values, logging alarms and feeding an RRD
+database with the sensor measurements.
+
+This package does not contain a nice graphical monitor. See
+http://www.lm-sensors.org/wiki/UsefulLinks for pointers to such programs.
+
+
+OTHER INFORMATION
+-----------------
+
+The developers of this package can be reached through a mailing-list
+(see http://www.lm-sensors.org/wiki/AuthorsAndContributors). Do not hesitate
+to mail us if you have questions, suggestions, problems, want to
+contribute, or just want to report it works for you. But please try to
+read the documentation before you ask any questions! It's all under doc/.
+Also make sure you read the FAQ at http://www.lm-sensors.org/wiki/FAQ.
+
+The latest version of this package can always be found at:
+http://www.lm-sensors.org/wiki/Download. Pre-release versions can be
+retrieved through anonymous SVN; see doc/svn for details.
+
+
+LICENSE
+-------
+
+The library (libsensors) is released under the GNU Lesser General Public
+License (LGPL), as included in the file COPYING.LGPL. The rest of this
+package may be distributed according to the GNU General Public License
+(GPL), as included in the file COPYING.
